“AI 技术+人才”如何成为企业增长新引擎?戳此了解>>> 了解详情
写点什么

Git、Gerrit 与 Jenkins/Hudson CI 服务器

  • 2011-09-06
  • 本文字数:8696 字

    阅读完需:约 29 分钟

本文讲述了如何为基于团队的代码审查系统配置 Git、Gerrit 与 Jenkins/Hudson,正如我在《Git, Gerrit and Jenkins for iOS development》《Gerrit Git Review with Jenkins CI Server》演讲(以及第一次提出这种做法的《Someday…》)中所倡导的那样。 文中的范例假定你所使用的操作系统是OS X 或Linux,但是如果你愿意,也可以在Windows 上运行它们。

配置Git

很多系统(例如Linux)已经默认提供了Git,在 Git 主页也可以找到安装程序。对于 Windows 用户,最好的选择是 MsysGit 。请注意,如果你安装了 Apple Developer Tools (for Xcode 4),那么其中已经自带 Git 二进制包了。如果遇到了问题, help.github.com 中可以找到很多非常出色的指南。

因为所有的 Git 提交都带有作者和电子邮件地址,如果你还没有设置过这些内容,请执行以下命令进行配置:

复制代码
$ git config --global user.name "Alex Blewitt"
$ git config --global user.email <a href="mailto:Alex.Blewitt@example.com">Alex.Blewitt@example.com</a>

最好有一个 Git 代码库。Gerrit 在初始化阶段会自动扫描 Git 代码库,这样做比在后期配置代码库容易一些,因此最好在初始化 Gerrit 前准备好 Git 代码库。

如果还没有 Git 代码库,可以创建一个:

git init --bare /path/to/gits/example.git

Gerrit

可以从 http://code.google.com/p/gerrit/ 下载到 Gerrit,那是一个 WAR 文件。Gerrit 有很好的文档(目前的最新文档是 2.2.1 ,但也可用于 2.1.7)和安装指南

Gerrit 在运行时需要用到数据库(用于存储代码审查的信息)。目前支持的数据库包括 H2、PostgreSQL 和 MySQL。在没有进行额外配置的情况下,它默认使用 H2。

请注意,Gerrit 2.2.x 正把项目配置、权限和其他元数据移到 Git 存储中,这样就可以通过 Git 进行访问和版本控制。在 2.2.x 中,这个转变会慢慢扩展到其他类型的元数据上,包含代码评审内容。详见 2.2.0 版本的发布说明

要初始化 Gerrit,运行 java -jar gerrit.war init -d /path/to/location 会在指定路径上安装 Gerrit 运行时。

如果是在交互终端中运行的,安装程序会提几个问题,例如:

  • Git 代码库的位置 [git]
  • 导入现有代码库 [Y/n]
  • 数据库服务器类型 [H2/?]
  • 身份验证方法 [OPENID/?]
  • SMTP 服务器主机名 [localhost]
  • SMTP 服务器端口 [(default)]
  • SMTP 加密 [NONE/?]
  • SMTP 用户名
  • 以何种身份运行 [you]
  • Java 运行时 [/path/to/jvm]
  • 将 gerrit.war 复制到 /path/to/location/bin/gerrit.war [Y/n]
  • 监听地址 [*]
  • 监听端口 [29418]
  • 下载并安装 Bouncy Castle [Y/n]
  • 位于 HTTP 反向代理之后 [y/N]
  • 使用 SSL [y/N]
  • 监听地址 [*]
  • 监听端口 [8080]

其中大部分可以保留默认值,但是有几个需要重点关注。

  • Git 代码库的位置要指向正确的位置。默认值是位于安装目录中的’git’目录,也可以是其他不同位置(例如 /var/gits 等等)。应该先配置好这个路径,因为在 Gerrit 启动时它会先扫描该目录来添加新项目。
  • 监听地址对有多个地址(例如 IPv4 和 IPv6)的主机很有用,它可以限定使用哪个地址。* 表示本地主机上的任意地址。
  • 监听端口是一个端口号。29418 是默认的 Gerrit SSH 端口,而 8080 是默认的 Gerrit Web 端口。但是如果 8080 已经被别的应用程序占用了,那么你可能会想修改第二个端口。
  • 身份验证方法确定了如何登录 Gerrit。如果你想挂入某个现有的身份验证提供方(例如 Google Accounts),那么可以使用 OpenID。但对测试而言(还有上面提到的范例),可以使用 development_become_any_account。键入? 会显示一个可用方法的列表。

Gerrit 启动后,会打开一个浏览器,显示 Gerrit 主页。登录的第一个用户将自动成为管理员;所有后续登录的用户都是无权限用户。如果你选择了 development_become_any_account,在页面顶端会有一个 Become 链接,通过它可以进入注册 / 登录页面。

注册用户

为了使用 Gerrit,你需要一个账号,生成一个 SSH 密钥对。在命令行中运行 ssh-keygen -t rsa -b 2048 可以生成密钥对,将其放到你的.ssh 目录中。如果你需要更多信息,可以访问这篇博文,这是我六年前写的SSH 密钥相关文章。此外, GitHub 帮助页面中也有更多相关信息。

默认文件名为 id_rsa(这是私钥)和 id_rsa.pub(这是公钥)。你只能给别人公钥,千万别给别人私钥。

有了密钥,你就可以在 Gerrit 中注册新账户了。点击顶部右端的“Become”链接,然后再点击“New account”按钮,输入 Git 知道的名称和电子邮件(即上面用 git config 配置的),这些内容要完全匹配(包括大小写)。保存变更,然后选择一个唯一的用户名(填入了名称后点击’select username’,例如 demo)。

头痛的电子邮件 就算是 development_become_any_account 模式,Gerrit 也会给你发送电子邮件以验证邮件地址。不通过验证的话就无法使用该电子邮件地址,你也无法提交代码。

我们可以很快处理掉这个问题,因此如果 Gerrit 目前不让你注册你的邮箱地址,请不用担心。

在’add SSH public key’文本框中,添加.pub 文件中的公钥。如果你使用的是 OS X,pbcopy < ~/.ssh/id_rsa.pub 就可轻松搞定。记住要点击“Add”保存公钥。

点击 Continue 后应该就能登录到 Gerrit 的主窗口了。到目前为止一切还不错,现在可以测试 SSH 的连通性了。

键入 ssh -p 29418 demo@localhost 会尝试连接 Gerrit 服务器,会出现以下三者之一:

  1. 复制代码

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!

复制代码

这并没有看上去那么糟。它只是说你的~/.ssh/known_hosts 文件中有旧的密钥。最懒的一种修复方法是删除这个文件(这是 GitHub 推荐的做法)。更好的方法是找到提示出错的那一行,把它删了:

复制代码
Offending key in /Users/demo/.ssh/known_hosts:123

你可以用任何文本编辑器来删除 known_hosts 文件中的第 123 行。在标准 UNIX 配置下,可以用以下命令自动删除:

sed -i '' '123d' ~/.ssh/known_hosts

  1. 复制代码

The authenticity of host ‘[localhost]:29418 ([::1]:29418)’ can’t be established.
RSA key fingerprint is e8:e2:fe:19:6f:e2:db:c1:05:b5:bf:a6:ad:4b:04:33.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added ‘[localhost]:29418’ (RSA) to the list of known hosts.
Permission denied (publickey).

复制代码

如果看到这个消息,说明 Gerrit 没有识别出你提交的任何密钥。ssh 默认会发送 id_rsa,要确认它的确发送了该值,可以查看.ssh/config,文件头上有一行 IdentityFile ~/.ssh/id_rsa。可以运行 ssh -v 查看发送的内容:

复制代码
debug1: Next authentication method: publickey
debug1: Trying private key: /Users/demo/.ssh/id_dsa
debug1: Offering public key: /Users/demo/.ssh/id_rsa

假设已经发送了正确的密钥,那么在 settings - ssh public keys 菜单项中检查该密钥是否关联了 Gerrit 中的用户。如果没有的话点击’Add Key’并和之前一样粘贴公钥。

如果 Gerrit 还是说你未经身份验证,检查用户名和配置页面里的用户名是否一致。如果用户名不同,试试 ssh -p 29418 username@localhost。

最后,要验证具体的密钥,可以运行 ssh -i ~/.ssh/id_rsa 显式地选择要使用的密钥,而不是让它自动选择密钥。如果这样可以工作,但不带 -i 参数却不行的话,那么问题出在你的~/.ssh/config 文件里――你需要保证选择了合适的 IdentityFile。
3. > ```

** Welcome to Gerrit Code Review **

Hi demo, you have successfully connected over SSH.

Unfortunately, interactive shells are disabled.
To clone a hosted Git repository, use:

git clone ssh://demo@localhost:29418/REPOSITORY_NAME.git

Connection to localhost closed.

复制代码

如果看到这个提示,说明 Gerrit 已经正常工作了。

修正电子邮件地址

如果之前你无法在 Gerrit 中注册电子邮件地址,那么可以手工进行注册。我们可以停止 Gerrit,运行 GSQL 工具更新特定数据字段。

复制代码
$ bin/gerrit.sh stop
$ java -jar bin/gerrit.war gsql
Welcome to Gerrit Code Review 2.1.6.1
(H2 1.2.134 (2010-04-23))
Type '\h' for help. Type '\r' to clear the buffer.
gerrit> select * from ACCOUNT_EXTERNAL_IDS;
ACCOUNT_ID | EMAIL_ADDRESS | PASSWORD | EXTERNAL_ID
-----------+------------------------+----------+------------------------------------------
1000000 | NULL | NULL | uuid:ac1b8a08-2dd1-4aa1-8449-8b2994dffaed
1000000 | NULL | NULL | username:demo
(2 rows; 23 ms)
gerrit> update ACCOUNT_EXTERNAL_IDS set EMAIL_ADDRESS='alex.blewitt@example.com' where ACCOUNT_ID=1000000;
UPDATE 2; 5 ms
gerrit> select * from ACCOUNT_EXTERNAL_IDS;
ACCOUNT_ID | EMAIL_ADDRESS | PASSWORD | EXTERNAL_ID
-----------+------------------------+----------+------------------------------------------
1000000 | alex.blewitt@example.com | NULL | uuid:ac1b8a08-2dd1-4aa1-8449-8b2994dffaed
1000000 | alex.blewitt@example.com | NULL | username:demo
(2 rows; 23 ms)
gerrit> \q
Bye
$ bin/gerrit.sh start

创建项目,克隆并推送代码

开始前,我们需要先在 Gerrit 中创建一个项目。如果 Gerrit 没有在你的范例目录中检测到项目,在它运行时,我们可以创建一个项目。

复制代码
$ ssh -p 29418 demo@localhost gerrit create-project --name example.git

上述命令会创建一个名为 example 的项目,在之前指定的 Git 目录里初始化一个空的代码库。如果已经有一个代码库了,Gerrit 不允许创建同名代码库——但你可以先对它进行临时重命名,随后再把名字改回来。

随后,可以创建一个克隆:

复制代码
$ git clone ssh://demo@localhost:29418/example.git
Cloning into example...
warning: You appear to have cloned an empty repository.

我们可以向代码库进行提交和推送,就和其他 Git 系统一样:

复制代码
$ cd example
$ echo hello > world
$ git add world
$ git commit -m "The World"
[master (root-commit) 06bf85e] The World
1 files changed, 1 insertions(+), 0 deletions(-)
create mode 100644 world
$ git push
No refs in common and none specified; doing nothing.
Perhaps you should specify a branch such as 'master'.
$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 217 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To ssh://me@localhost:29418/example.git
! [remote rejected] master -> master (prohibited by Gerrit)
error: failed to push some refs to 'ssh://demo@localhost:29418/example.git'

这是什么情况?Gerrit 不希望我们直接覆写 Git 代码库中的任何分支,而是将变更推送到另一个 _refspec_ 中,这让 Gerrit 有机会在代码审查中修改代码。最简单的方法是为推送配置一个默认的 refspec:

复制代码
$ git config remote.origin.push refs/heads/*:refs/for/*
$ git push origin
Counting objects: 3, done.
Writing objects: 100% (3/3), 217 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To ssh://demo@localhost:29418/example.git
* [new branch] master -> refs/for/master

我们推送了一个分支,在 Gerrit 的 change,1 里可以发现,尽管实际的分支名称是 refs/changes/01/1/1,但看到的却是 refs/for/master。你会发现当前分支的哈希值和此处显示的一样(在我的例子里是 06bf85e)。如果我们要再次推送,会创建一个新的分支 refs/changes/02/2/1,而不是覆盖 refs/for/master。第一个数字是变更号的最后两位,第二个数字是变更号,第三个数字是补丁集号。所以变更 123 的补丁集 17 就是 refs/changes/23/123/17

如果想要修正(amend)提交,Gerrit 会创建一个新的代码审查引用,这么做不太好。如果两次变更之间有审查评语,你将丢失这些内容。

我们可以修改 commit-msg,添加一个(Gerrit 专门的)Change-Id。同一变更的所有后续提交都会关联到一个补丁集上。Gerrit 提供了一个实现,只要复制出来就行了。

复制代码
$ cd .git/hooks
$ scp -P 29418 demo@localhost:hooks/commit-msg .
$ cd ../..

现在,当我们要提交修正时便会自动生成一个 Change-Id。可以用这个值为变更集生成多个补丁。通过修正提交,并提供 Gerrit 变更的 Change-Id,我们可以修改当前的提交。

复制代码
$ git commit --amend -m "Hello World
>
> Change-Id: I06bf85ed12f370212ec22dbd76c115861b653cf2
> "
[master 86a7a39] Hello World
1 files changed, 1 insertions(+), 0 deletions(-)
$ git push
Counting objects: 3, done.
Writing objects: 100% (3/3), 260 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
remote: (W) 86a7a39: no files changed, message updated
To ssh://me@localhost:29418/example.git
* [new branch] master -> refs/for/master

现在再去看 Gerrit 中的 change 1 ,你会发现出现了第二个与该变更相关联的补丁集。远端分支 refs/changes/01/1/2 包含了新的提交消息(尽管所有文件还是一样的)。

通常情况下,你并不需要添加 Change-Id,因为提交消息 Hook 会自动帮你添加的。

代码审查与结果提交

我们要创建一个 build.sh 脚本来执行构建过程。Jenkins/Hudson 会签出并运行该脚本。一般而言,这是一个 Maven 的构建,也可能是 xcodebuild 或 make——为了避免针对某种语言,我们就使用一个普通的脚本。

复制代码
$ cat > build.sh
#!/bin/sh
echo Pretending to build ...
echo done
^D
$ chmod a+x build.sh
$ git add build.sh
$ git commit -m "Adding (dummy) build script"
$ git push

问题是这些变更还在 Gerrit 的评审队列里,我们要先批准这些变更。进入变更页面,会看到这些文件,还有一个 Review 按钮。在变更页面有一个让你做代码审查的Review 按钮,但却没有提交按钮……

这是因为变更在提交前默认要评审+2,每个人只能+1,也就是说只有一个人做代码审查是无法提交代码的。

我们可以修改Gerrit 的规则,允许一个人投票+2,也可以准许+1 提交。

最简单的方法是进入项目管理页面 All Projects Access 标签页(在 Gerrit 2.2.1 和 2.1.7.2 里这个标签从–All Projects–改成了 All-Projects)。点击“Code review”规则旁边的复选框,修改“Permitted Range”为 +2: Looks good to me, approved。Gerrit 还需要一个 +1 校验,随后也会为 Jenkins/Hudson 配置的。按照下面内容添加一条新规则:

复制代码

Category Verified Group Name Non-Interactive Users Reference Name refs/* Permitted Range -1: Fails to +1: Verified 点击“Add Access Right”来配置该权限。

我们需要为 Jenkins/Hudson 创建一个新用户并添加到这个分组里,因此进到登录页面注册一个新账户buildbot。还需要一个新的SSH 密钥(名字是id_rsa.buildbot),和之前一样粘贴在公钥一栏里。

重新用管理员ID 登录(你早些时候创建的那个账户),进入 Admin - Groups 标签。在 Non-Interactive Users 里加入 buildbot 和 demo 用户(后者是临时的)。

配置好了校验规则,我们应该回到变更页面,将这个变更标记为通过。

但我们还是缺少一个Submit 按钮,需要用它来合并分支。提交权限独立于校验和代码审查权限之外,因此需要回到 All Projects Access 标签页,添加一个权限:

复制代码

Category Submit Group Name Registered Users Reference Name refs/* Permitted Range +1: Submit 点击“Add Access Right”来配置该权限。

现在,回到变更页面,我们就能看到Submit 按钮(如果变更已经通过了代码审查)了,在评论/ 代码审查页面也有Publish and Submit 按钮。(请注意,如果代码审查还没到达提交所需的级别,在点击Publish and Submit 时会出现一个错误提示。)

最后,发布并提交所有未完成的代码审查(保证构建脚本已经就绪),执行git pull 来确保你的代码库是最新的。它们应该出现在已合并标签页中。

管理员注意事项:一般来说,不会普遍赋予’All Projects Access’权限,而是根据项目进行分配。这里使用’All Projects’是为了方便运行,也可以根据项目进行配置。

配置Jenkins/Hudson

现在要做的就是配置 Jenkins (如果你喜欢也可以用 Hudson )。它们和 Gerrit 一样,默认运行在 8080 端口上,你需要为它们换个端口。修改 Gerrit 的端口要重新执行之前的配置过程,修改 Jenkins/Hudson 的端口只需命令行即可。

复制代码
$ #java -jar hudson-2.0.1.war --httpPort=1234
$ java -jar jenkins.war -httpPort=1234
...

Jenkins/Hudson 可以直接签出 Git 项目,也可以通过 Gerrit 签出。但是签出过程未必都能自定义 SSH 身份(不用.ssh/config 中的默认身份集)。有时使用匿名 Git 协议来托管 Git 代码库会容易些,即无需身份验证。可以运行如下命令:

复制代码
$ git daemon --export-all --base-path=/path/to/gits

我们还需要安装 Git 插件和 Git/Gerrit 触发器。打开位于 http://localhost:1234 的 Jenkins/Hudson,点击左上方的 Manage Jenkins/Hudson 链接,再点击 Manage Plugins 链接。切换到 Available 标签页,安装:

  • Gerrit Trigger

Install 按钮在右下方,Jenkins/Hudson 稍后会重启。Gerrit Trigger 会安装 Git 插件。请注意,有一个 Gerrit Plugin,那个不是我们要装的。(如果你在使用 Hudson,并且更新页面里没有任何内容的话,进入’Advanced’点击下方的’Check now’。)

现在,我们可以开始配置 Jenkins/Hudson 了。先按以下步骤新建一个 CI 任务检查 Git 代码库的变更(合并):

  1. 点击左上方的’New Job’。这会要求你输入一个名称(例如 Example)和类型;本例中选择 free-style 项目。
  2. SCM 类型选择 Git(如果没有这个选项,说明需要安装上面提到的 Git 插件)。URL 是 git://localhost/example.git
  3. 至于构建触发器,选中 Poll SCM 并输入 * * * * *。这里的格式与 crontab 类似,这里相当于每分钟检查一次代码库。
  4. 滚动到 add a build step,选择 execute shell。输入 $WORKSPACE/build.sh(请注意:如果项目名中有空格,需要用引号把它括起来,例如"$WORKSPACE/build.sh")
  5. 最后,点击 Save,项目创建好了。

上面创建了一个项目,签出 master,如果有变化则执行 build.sh。点击 build now 链接会签出代码,运行脚本并报告执行成功与否。(如果失败了,可以检查构建日志了解详细信息,在继续后续任务前把问题解决掉。)

集成 Gerrit

我们现在已经可以在 master 发生变更时通过 Jenkins/Hudson 自动构建了,但是我们还应该在提交代码审查时进行构建。

下面要创建另一个任务:

复制代码

Name Example-Gerrit Type Free-style SCM Git URL git://localhost/example.git Advanced (below URL of repository) Refspec $GERRIT_REFSPEC Advanced (above repository browser) Choosing strategy Gerrit-plugin Build Triggers Gerrit Event Gerrit Project Path - - Path - 有新事件发生时,Gerrit 会触发项目。但是我们还差一步,要告诉 Jenkins/Hudson 监听哪个 Gerrit 服务器。

在 Manage Jenkins/Hudson 标签页中,有一项 Gerrit Trigger,添加如下信息:

复制代码

Hostname localhost URL http://localhost:8080 Port 29418 Username buildbot Keyfile /path/to/.ssh/id_rsa.buildbot SSH Keyfile password … 首先,点击下方的“Save”按钮。然后,点击 Test Connection 按钮检查配置是否成功。如果成功,点击“Restart”,或者重启 Jenkins/Hudson。

如果一切顺利,应该可以回到主页,左边有一个 Query and Trigger Gerrit Patches 链接。点击该链接,输入 is:open 查看打开的变更,is:merged 查看合并的变更。完成后,选中复选框,点击 Trigger Selected 触发该变更的构建。

如果你使用的是 Hudson 2.0.0 和 Gerrit 2.2.0,在 Hudson 控制台里会看到 java.lang.reflect.InvocationTargetException 错误。这是新 Gerrit Trigger 的一个问题;升级到 Hudson 2.0.1 就好了。

整合到一起

你现在可以在代码库的本地克隆中创建一个变更,推送到 Gerrit 中,让 Jenkins/Hudson 自动为你执行构建。

要测试失败的情况,编辑 build.sh 脚本,在文件末尾输入 exit 1。提交文件并将其推送到 Gerrit,你会看到 Jenkins/Hudson 的状态会变为失败,因为现在的构建脚本返回了一个非零码。修复提交,将刚才那行改为 exit 0,推送之后你会看到构建结果变回成功。

最后,如果要使用 xcodebuild 构建 iOS 应用程序,通过 ocunit2junit.rb 脚本来串联整个构建,它会把 SenTest 的断言转为 Jenkins/Hudson 可以理解的形式,这样失败的断言就能打印在输出的构建日志里了。

关于作者

Alex Blewitt 博士是 Bandlem Limited 的创始人,虽然就职于伦敦的一家投资银行,但仍然花时间了解最新的 OSGi 和 Eclipse 动态。尽管他曾是 EclipseZone 的编辑,在 2007 年被提名为 Eclipse Ambassador,可是他的日常工作与 Eclipse 和 Java 都没有任何关系。在他那所剩无几的空余时间里,他会和家人在一起,如果天气好他们会出去玩。你可以在 Twitter 上关注 @alblue ,或者去他的博客 alblue.bandlem.com

查看英文原文: Git, Gerrit Review and Jenkins or Hudson CI Servers


给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2011-09-06 00:0038606
用户头像

发布了 135 篇内容, 共 58.4 次阅读, 收获喜欢 43 次。

关注

评论

发布
暂无评论
发现更多内容

【docker 总结】第四篇 - 镜像和容器操作

Brave

Docker 12月日更

实用机器学习笔记十六:循环神经网络

打工人!

深度学习 学习笔记 循环神经网络 机器学习算法 12月日更

当我们在聊高可用时,我们其实在聊什么?

xcbeyond

28天写作 12月日更

如何基于 Docker 快速搭建 Springboot + Mysql + Redis 项目

秦怀杂货店

Java redis Docker springboot redis sentinel

山的那一边,是什么?(16/28)

赵新龙

28天写作

Dubbo框架学习笔记四

风翱

dubbo 12月日更

小程序的发布【小程序专题3】

坚果

小程序 28天写作 12月日更

Python爬虫120例之案例58,手机APP爬虫,“武器库”的准备and皮皮虾APP的测试

梦想橡皮擦

12月日更

如何在 AWS 云中从 Amazon EC2 启动 RHEL 8?

Ethereal

云计算 AWS RHEL 8 Amazon EC2

DotNetCore 开发工具箱之调度小能手——Coravel

为自己带盐

dotnet 28天写作 12月日更 Coravel

管理中的推理事件随笔

搬砖的周狮傅

随笔杂谈 推理

React进阶(七):props 属性介绍

No Silver Bullet

React 12月日更 props

明道云荣获2021亚洲最佳职场“最佳创新奖”荣誉!

明道云

TCP/IP的底层队列

程序员历小冰

网络 TCP/IP 28天写作 12月日更

Go+ XML 编码和解码处理教程(5.5)

liuzhen007

28天写作 12月日更

数据库牛人是如何进行SQL优化的?

Ethereal

数据库 sql SQL优化

Enum Parse 中的坑

喵叔

28天写作 12月日更

前端开发:Vue中获取input输入框值的方法

三掌柜

28天写作 28 12月日更 12月

明道云对接小鹅通,沉淀内容付费平台数据

明道云

JavaScript操作 DOM 的这些事件基础

你好bk

JavaScript 前端 DOM BOM WebApi

数据分析流程浅析

圣迪

数据分析 数据 CRISP-DM

All in one:如何搭建端到端可观测体系

阿里巴巴云原生

阿里云 云原生 可观测

华为硬件配置命令,建议收藏

Ethereal

网络工程师 网络技术 华为设备 厂商设备 运维技术

[Pulsar] Pulsar Resources介绍

Zike Yang

Apache Pulsar 12月日更

Apache Log4j2,RASP 防御优势及原理

阿里巴巴云原生

阿里云 云原生 漏洞 可观测 Apache Log4j2

Android aapt 在 Mac 和 Windows 上使用方法小结

阿策小和尚

28天写作 Android 小菜鸟 12月日更

什么是Kubernetes?Kubernetes是怎样工作的?

Ethereal

云计算 Kubernetes

100行代码让您学会JavaScript原生的Proxy设计模式

Jerry Wang

JavaScript 设计模式 代理模式 28天写作 12月日更

联邦学习在光大科技的落地应用

博文视点Broadview

一文讲述数仓组件SysCache

华为云开发者联盟

事务 存储 GaussDB(DWS) SysCache 缓存信息

Spring Boot Serverless 实战系列“架构篇” | 光速入门函数计算

阿里巴巴云原生

阿里云 Serverless 架构 云原生 函数计算

Git、Gerrit与Jenkins/Hudson CI服务器_Java_Alex Blewitt_InfoQ精选文章