-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearchindex.json
1 lines (1 loc) · 243 KB
/
searchindex.json
1
{"categories":[{"title":"docker","uri":"https://fhxisdog.github.io/categories/docker/"},{"title":"flutter","uri":"https://fhxisdog.github.io/categories/flutter/"},{"title":"Git","uri":"https://fhxisdog.github.io/categories/git/"},{"title":"jvm","uri":"https://fhxisdog.github.io/categories/jvm/"},{"title":"linux","uri":"https://fhxisdog.github.io/categories/linux/"},{"title":"maven","uri":"https://fhxisdog.github.io/categories/maven/"},{"title":"nacos","uri":"https://fhxisdog.github.io/categories/nacos/"},{"title":"orther","uri":"https://fhxisdog.github.io/categories/orther/"},{"title":"postman","uri":"https://fhxisdog.github.io/categories/postman/"},{"title":"skywalking","uri":"https://fhxisdog.github.io/categories/skywalking/"},{"title":"smicroservices","uri":"https://fhxisdog.github.io/categories/smicroservices/"},{"title":"spring boot","uri":"https://fhxisdog.github.io/categories/spring-boot/"},{"title":"tips","uri":"https://fhxisdog.github.io/categories/tips/"}],"posts":[{"content":"平时使用postman时经常会有需要在一下请求前先执行一些脚本,或者先统一发一次请求的需求,例如:先登陆再请求其他接口。经过摸索,发现postman可以在项目前统一执行脚本。步骤如下(以执行登陆为例):\n1.新建一个Collections,点这个Collections,在右侧可以看到Pre-request Script选项。在里面填写自己要执行的脚本即可。\n Example:\npm.sendRequest( { method: \u0026#34;POST\u0026#34;, url: `${pm.environment.get(\u0026#34;protocal\u0026#34;)}://${pm.environment.get(\u0026#34;host\u0026#34;)}:${pm.environment.get(\u0026#34;port\u0026#34;)}${pm.environment.get(\u0026#34;base-path\u0026#34;)}/authentication/login-name`, header: { \u0026#39;Content-Type\u0026#39;: \u0026#39;application/json\u0026#39; }, body: { mode: \u0026#34;raw\u0026#34;, raw: JSON.stringify({ loginName: pm.environment.get(\u0026#39;loginName\u0026#39;), //从环境变量中获取登录名和密码 password: pm.environment.get(\u0026#39;password\u0026#39;) }) }, }, function (err, response) { pm.environment.set(\u0026#34;Authorization\u0026#34;, response.json().data); } ); //添加请求头 pm.request.headers.add({ // These keys appears when you set a header by hand. Just for fun they are here disabled: false, description:{ content: \u0026#34;DescriptionTest\u0026#34;, type: \u0026#34;text/plain\u0026#34; }, // Your header, effectively key: \u0026#39;KeyTest\u0026#39;, name: \u0026#39;NameTest\u0026#39;, // If you set a variable you can access it // HeaderTest here has value=\u0026#34;ValueHeaderTest\u0026#34; value: pm.collectionVariables.get(\u0026#34;HeaderTest\u0026#34;) }); ","id":0,"section":"posts","summary":"平时使用postman时经常会有需要在一下请求前先执行一些脚本,或者先统一发一次请求的需求,例如:先登陆再请求其他接口。经过摸索,发现pos","tags":["postman","skill"],"title":"Postman 在请求前添加自动执行脚本","uri":"https://fhxisdog.github.io/2021/05/postman_pre_request/","year":"2021"},{"content":"问题描述: 最近在使用dockers部署应用时发现,每次执行更新时时间会往后推8个小时, 查看数据库间连接:db.url=jdbc:mysql://xxxxx/xx?useUnicode=true\u0026amp;characterEncoding=utf-8\u0026amp;serverTimezone=GMT%2b8\u0026amp;autoReconnect=true\u0026amp;failOverReadOnly=false\n使用java的LocalDateTime.now()打印出的时间也是快了8个小时\n解决方案: docker创建应用的容器时,不仅要将/etc/localtime挂载进去,也要将时区挂载进去.\n例如:\ndocker run --name app-name -d \\ -v /etc/localtime:/etc/localtime \\ -v /app:/app --network xx-bridge \\ -p 8080:8080 java:8 \\ sh -c \u0026#39;echo \u0026#34;Asia/Shanghai\u0026#34; \u0026gt; /etc/timezone \u0026amp;\u0026amp; java -jar /app/app.jar \u0026#39; ","id":1,"section":"posts","summary":"问题描述: 最近在使用dockers部署应用时发现,每次执行更新时时间会往后推8个小时, 查看数据库间连接:db.url=jdbc:mysql:","tags":null,"title":"docker时区问题","uri":"https://fhxisdog.github.io/2020/06/docker_timezone/","year":"2020"},{"content":"官网下载MySQL压缩包 解压缩至目录D:/ 将D:\\mysql-8.0.11-winx64\\bin配置到环境变量path中 在D:\\mysql-8.0.11-winx64中新建my.ini文件,添加如下配置: [mysqld] # mysql位置 basedir=D:\\mysql-8.0.11-winx64 # 数据保存位置 datadir=F:\\MySql_Data # 端口 port = 3306 初始化数据库 mysqld --initialize --user=mysql --console 在控制台消息尾部会出现随机生成的初始密码,记下来(因为有特殊字符,很容易记错,最好把整个消息保存在记事本里)\n以管理员身份打开命令行,将mysql添加到服务 mysqld --install MySQL net start MySQL 启动Mysql并修改密码 mysql -u root -p #密码为刚刚初始化时生成的密码 修改密码 ALTER USER 'root'@'localhost' IDENTIFIED BY 'MyNewPass'; 参考链接: mysql官方链接\n ","id":2,"section":"posts","summary":"官网下载MySQL压缩包 解压缩至目录D:/ 将D:\\mysql-8.0.11-winx64\\bin配置到环境变量path中 在D:\\mysql-","tags":null,"title":"windows以zip方式安装mysql","uri":"https://fhxisdog.github.io/2020/06/windowsinstallmysqlwithzip/","year":"2020"},{"content":"问题现象 使用Maven过程中,曾经出现过本地仓库中已经存在某jar包,但是Maven仍然从远程仓库下载jar包的现象,并且可能会报出类似以下的错误:\n[exec] [ERROR] The project com.acme:test:0.0.1-SNAPSHOT (/home/acme/pom.xml) has 1 error [exec] [ERROR] Non-resolvable parent POM: Failure to find com.acme.maven:parent-pom:pom:2 in http://repo.maven.apache.org/maven2 was cached in the local repository, resolution will not be reattempted until the update interval of central has elapsed or updates are forced and \u0026#39;parent.relativePath\u0026#39; points at no local POM @ line 5, column 13 -\u0026gt; [Help 2] 解决方案 在stackoverflow看到了类似的问题,可以通过删除_remote.repositories文件解决问题。\nMaven使用_remote.repositories文件存储本地jar对应的远程仓库源头。如果源头已经不存在该jar包(比如更改配置切换镜像、切换仓库导致的),Maven的resolve会失败,从而导致项目构建失败。删除了该文件以后,Maven不再从远程仓库执行此操作,因此可以解决问题。\n参考资料 Maven偶现本地仓库jar存在仍然从远程仓库拉取且失败的现象 | Blog.new \u0026lsquo;书豪\u0026rsquo;\nmaven-cant-find-my-local-artifacts\nremote-repositories-prevents-maven-from-resolving-remote-parent\n ","id":3,"section":"posts","summary":"问题现象 使用Maven过程中,曾经出现过本地仓库中已经存在某jar包,但是Maven仍然从远程仓库下载jar包的现象,并且可能会报出类似以下","tags":["maven","java"],"title":"Maven 本地有jar包仍拉取远程仓库的jar包","uri":"https://fhxisdog.github.io/2020/04/maven-cant-find-my-local-artifacts/","year":"2020"},{"content":"今天发现 Win10 自带的微软输入法还可以添加 小鹤双拼,终于可以抛弃其他的广告输入法了。\n步骤:\n win + R,输入 regedit,打开注册表\n 找到 计算机\\HKEY_CURRENT_USER\\Software\\Microsoft\\InputMethod\\Settings\\CHS 项\n 新建一个名为 UserDefinedDoublePinyinScheme0 的字符串值,值为 小鹤双拼2^iuvdjhcwfg^xmlnpbksqszxkrltvyovt*\n 打开控制面板\u0026ndash;微软拼音输入法设置,把 小鹤双拼 设置为双拼的默认选择即可。\n 其他高级配置:\n控制面板\u0026ndash;时间和语言\u0026ndash;语言\u0026ndash;拼写,输入和键盘设置\u0026ndash;高级键盘设置\u0026ndash;允许我为每个应用窗口使用不同的输入法 \u0026ndash; 打勾\n 参考连接:Win10 微软拼音添加小鹤双拼以及其他配置\n ","id":4,"section":"posts","summary":"今天发现 Win10 自带的微软输入法还可以添加 小鹤双拼,终于可以抛弃其他的广告输入法了。 步骤: win + R,输入 regedit,打开注册表 找到 计算机\\HKE","tags":["tips"],"title":"win10自带输入法添加小鹤双拼","uri":"https://fhxisdog.github.io/2020/04/win10_xiaohe/","year":"2020"},{"content":"使用场景 基于公司的项目会越来越多,常常需要提取一个公共的类库提供给多个项目使用,但是这个library怎么和git在一起方便管理呢? 我们需要解决下面几个问题:\n如何在git项目中导入library库?\nlibrary库在其他的项目中被修改了可以更新到远程的代码库中?\n其他项目如何获取到library库最新的提交?\n如何在clone的时候能够自动导入library库?\n解决以上问题,可以考虑使用git的 Submodule来解决。\n什么是Submodule? git Submodule 是一个很好的多项目使用共同类库的工具,他允许类库项目做为repository,子项目做为一个单独的git项目存在父项目中,子项目可以有自己的独立的commit,push,pull。而父项目以Submodule的形式包含子项目,父项目可以指定子项目header,父项目中会的提交信息包含Submodule的信息,再clone父项目的时候可以把Submodule初始化。\n使用 添加子模块 git submodule add \u0026lt;url\u0026gt; #在当前项目添加一个同名的子模块 git submodule add \u0026lt;url\u0026gt; \u0026lt;path\u0026gt; #在\u0026lt;path\u0026gt; 目录添加子模块 clone子模块 git clone \u0026lt;url\u0026gt; --recuresive 如果clone时忘记添加参数--recursive可以使用以下命令进行拉取子模块\ngit submodule update --init --recursive #如果子模块也包含子模块,递归拉取,如果无需递归,去掉 --recursive即可 在包含子模块的项目上工作 从子模块的远端拉取上游修改 在项目中使用子模块的最简模型,就是只使用子项目并不时地获取更新,而并不在你的检出中进行任何更改。我们来看一个简单的例子。\n如果想要在子模块中查看新工作,可以进入到目录中运行 git fetch 与 git merge,合并上游分支来更新本地代码。\n$ git fetch From https://github.com/chaconinc/DbConnector c3f01dc..d0354fc master -\u0026gt; origin/master $ git merge origin/master Updating c3f01dc..d0354fc Fast-forward scripts/connect.sh | 1 + src/db.c | 1 + 2 files changed, 2 insertions(+) 如果你现在返回到主项目并运行 git diff \u0026ndash;submodule,就会看到子模块被更新的同时获得了一个包含新添加提交的列表。 如果你不想每次运行 git diff 时都输入 \u0026ndash;submodle,那么可以将 diff.submodule 设置为 “log” 来将其作为默认行为。\n$ git config --global diff.submodule log $ git diff Submodule DbConnector c3f01dc..d0354fc: \u0026gt; more efficient db routine \u0026gt; better connection routine 如果你现在返回到主项目并运行 git diff \u0026ndash;submodule,就会看到子模块被更新的同时获得了一个包含新添加提交的列表。 如果你不想每次运行 git diff 时都输入 \u0026ndash;submodle,那么可以将 diff.submodule 设置为 “log” 来将其作为默认行为。\n$ git config --global diff.submodule log $ git diff Submodule DbConnector c3f01dc..d0354fc: \u0026gt; more efficient db routine \u0026gt; better connection routine 如果在此时提交,那么你会将子模块锁定为其他人更新时的新代码。\n如果你不想在子目录中手动抓取与合并,那么还有种更容易的方式。 运行 git submodule update --remote,Git将会进入子模块然后抓取并更新。\n$ git submodule update --remote DbConnector remote: Counting objects: 4, done. remote: Compressing objects: 100% (2/2), done. remote: Total 4 (delta 2), reused 4 (delta 2) Unpacking objects: 100% (4/4), done. From https://github.com/chaconinc/DbConnector 3f19983..d0354fc master -\u0026gt; origin/master Submodule path \u0026#39;DbConnector\u0026#39;: checked out \u0026#39;d0354fc054692d3906c85c3af05ddce39a1c0644\u0026#39; 此命令默认会假定你想要更新并检出子模块仓库的 master 分支。 不过你也可以设置为想要的其他分支。 例如,你想要 DbConnector 子模块跟踪仓库的 “stable” 分支,那么既可以在 .gitmodules 文件中设置 (这样其他人也可以跟踪它),也可以只在本地的 .git/config 文件中设置。 让我们在 .gitmodules 文件中设置它:\n$ git config -f .gitmodules submodule.DbConnector.branch stable $ git submodule update --remote remote: Counting objects: 4, done. remote: Compressing objects: 100% (2/2), done. remote: Total 4 (delta 2), reused 4 (delta 2) Unpacking objects: 100% (4/4), done. From https://github.com/chaconinc/DbConnector 27cf5d3..c87d55d stable -\u0026gt; origin/stable Submodule path \u0026#39;DbConnector\u0026#39;: checked out \u0026#39;c87d55d4c6d4b05ee34fbc8cb6f7bf4585ae6687\u0026#39; 如果不用 -f .gitmodules 选项,那么它只会为你做修改。但是在仓库中保留跟踪信息更有意义一些,因为其他人也可以得到同样的效果。\n这时我们运行 git status,Git 会显示子模块中有“新提交”。\n$ git status On branch master Your branch is up-to-date with \u0026#39;origin/master\u0026#39;. Changes not staged for commit: (use \u0026#34;git add \u0026lt;file\u0026gt;...\u0026#34; to update what will be committed) (use \u0026#34;git checkout -- \u0026lt;file\u0026gt;...\u0026#34; to discard changes in working directory) modified: .gitmodules modified: DbConnector (new commits) no changes added to commit (use \u0026#34;git add\u0026#34; and/or \u0026#34;git commit -a\u0026#34;) 如果你设置了配置选项 status.submodulesummary,Git 也会显示你的子模块的更改摘要:\n$ git config status.submodulesummary 1 $ git status On branch master Your branch is up-to-date with \u0026#39;origin/master\u0026#39;. Changes not staged for commit: (use \u0026#34;git add \u0026lt;file\u0026gt;...\u0026#34; to update what will be committed) (use \u0026#34;git checkout -- \u0026lt;file\u0026gt;...\u0026#34; to discard changes in working directory) modified: .gitmodules modified: DbConnector (new commits) Submodules changed but not updated: * DbConnector c3f01dc...c87d55d (4): \u0026gt; catch non-null terminated lines 这时如果运行 git diff,可以看到我们修改了 .gitmodules 文件,同时还有几个已拉取的提交需要提交到我们自己的子模块项目中。\n$ git diff diff --git a/.gitmodules b/.gitmodules index 6fc0b3d..fd1cc29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule \u0026#34;DbConnector\u0026#34;] path = DbConnector url = https://github.com/chaconinc/DbConnector + branch = stable Submodule DbConnector c3f01dc..c87d55d: \u0026gt; catch non-null terminated lines \u0026gt; more robust error handling \u0026gt; more efficient db routine \u0026gt; better connection routine 这非常有趣,因为我们可以直接看到将要提交到子模块中的提交日志。 提交之后,你也可以运行 git log -p 查看这个信息。\n$ git log -p --submodule commit 0a24cfc121a8a3c118e0105ae4ae4c00281cf7ae Author: Scott Chacon \u0026lt;schacon@gmail.com\u0026gt; Date: Wed Sep 17 16:37:02 2014 +0200 updating DbConnector for bug fixes diff --git a/.gitmodules b/.gitmodules index 6fc0b3d..fd1cc29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule \u0026#34;DbConnector\u0026#34;] path = DbConnector url = https://github.com/chaconinc/DbConnector + branch = stable Submodule DbConnector c3f01dc..c87d55d: \u0026gt; catch non-null terminated lines \u0026gt; more robust error handling \u0026gt; more efficient db routine \u0026gt; better connection routine 当运行 git submodule update \u0026ndash;remote 时,Git 默认会尝试更新 所有 子模块, 所以如果有很多子模块的话,你可以传递想要更新的子模块的名字。\n从项目远端拉取上游更改 现在,让我们站在协作者的视角,他有自己的 MainProject 仓库的本地克隆, 只是执行 git pull 获取你新提交的更改还不够:\n$ git pull From https://github.com/chaconinc/MainProject fb9093c..0a24cfc master -\u0026gt; origin/master Fetching submodule DbConnector From https://github.com/chaconinc/DbConnector c3f01dc..c87d55d stable -\u0026gt; origin/stable Updating fb9093c..0a24cfc Fast-forward .gitmodules | 2 +- DbConnector | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) $ git status On branch master Your branch is up-to-date with \u0026#39;origin/master\u0026#39;. Changes not staged for commit: (use \u0026#34;git add \u0026lt;file\u0026gt;...\u0026#34; to update what will be committed) (use \u0026#34;git checkout -- \u0026lt;file\u0026gt;...\u0026#34; to discard changes in working directory) modified: DbConnector (new commits) Submodules changed but not updated: * DbConnector c87d55d...c3f01dc (4): \u0026lt; catch non-null terminated lines \u0026lt; more robust error handling \u0026lt; more efficient db routine \u0026lt; better connection routine no changes added to commit (use \u0026#34;git add\u0026#34; and/or \u0026#34;git commit -a\u0026#34;) 默认情况下,git pull 命令会递归地抓取子模块的更改,如上面第一个命令的输出所示。 然而,它不会 更新 子模块。这点可通过git status命令看到,它会显示子模块“已修改”,且“有新的提交”。 此外,左边的尖括号(\u0026lt;)指出了新的提交,表示这些提交已在MainProject中记录,但尚未在本地的 DbConnector 中检出。 为了完成更新,你需要运行 git submodule update:\n$ git submodule update --init --recursive Submodule path \u0026#39;vendor/plugins/demo\u0026#39;: checked out \u0026#39;48679c6302815f6c76f1fe30625d795d9e55fc56\u0026#39; $ git status On branch master Your branch is up-to-date with \u0026#39;origin/master\u0026#39;. nothing to commit, working tree clean 请注意,为安全起见,如果 MainProject 提交了你刚拉取的新子模块,那么应该在 git submodule update 后面添加 --init 选项,如果子模块有嵌套的子模块,则应使用 --recursive 选项。\n如果你想自动化此过程,那么可以为 git pull 命令添加 --recurse-submodules 选项(从 Git 2.14 开始)。 这会让 Git 在拉取后运行 git submodule update,将子模块置为正确的状态。 此外,如果你想让 Git 总是以 --recurse-submodules 拉取,可以将配置选项 submodule.recurse 设置为 true (从 Git 2.15 开始可用于 git pull)。此选项会让 Git 为所有支持 --recurse-submodules 的命令使用该选项(除 clone 以外)。\n在为父级项目拉取更新时,还会出现一种特殊的情况:在你拉取的提交中, 可能 .gitmodules 文件中记录的子模块的 URL 发生了改变。 比如,若子模块项目改变了它的托管平台,就会发生这种情况。 此时,若父级项目引用的子模块提交不在仓库中本地配置的子模块远端上,那么执行 git pull --recurse-submodules 或 git submodule update 就会失败。 为了补救,git submodule sync 命令需要:\n# 将新的 URL 复制到本地配置中 $ git submodule sync --recursive # 从新 URL 更新子模块 $ git submodule update --init --recursive 在子模块上工作 你很有可能正在使用子模块,因为你确实想在子模块中编写代码的同时,还想在主项目上编写代码(或者跨子模块工作)。 否则你大概只能用简单的依赖管理系统(如 Maven 或 Rubygems)来替代了。\n现在我们将通过一个例子来演示如何在子模块与主项目中同时做修改,以及如何同时提交与发布那些修改。\n到目前为止,当我们运行 git submodule update 从子模块仓库中抓取修改时, Git 将会获得这些改动并更新子目录中的文件,但是会将子仓库留在一个称作“游离的 HEAD”的状态。 这意味着没有本地工作分支(例如 “master” )跟踪改动。 如果没有工作分支跟踪更改,也就意味着即便你将更改提交到了子模块,这些更改也很可能会在下次运行 git submodule update 时丢失。如果你想要在子模块中跟踪这些修改,还需要一些额外的步骤。\n为了将子模块设置得更容易进入并修改,你需要做两件事。 首先,进入每个子模块并检出其相应的工作分支。 接着,若你做了更改就需要告诉 Git 它该做什么,然后运行 git submodule update --remote 来从上游拉取新工作。 你可以选择将它们合并到你的本地工作中,也可以尝试将你的工作变基到新的更改上。\n首先,让我们进入子模块目录然后检出一个分支。\n$ cd DbConnector/ $ git checkout stable Switched to branch \u0026#39;stable\u0026#39; 然后尝试用 “merge” 选项来更新子模块。 为了手动指定它,我们只需给 update 添加 \u0026ndash;merge 选项即可。 这时我们将会看到服务器上的这个子模块有一个改动并且它被合并了进来。\n$ cd .. $ git submodule update --remote --merge remote: Counting objects: 4, done. remote: Compressing objects: 100% (2/2), done. remote: Total 4 (delta 2), reused 4 (delta 2) Unpacking objects: 100% (4/4), done. From https://github.com/chaconinc/DbConnector c87d55d..92c7337 stable -\u0026gt; origin/stable Updating c87d55d..92c7337 Fast-forward src/main.c | 1 + 1 file changed, 1 insertion(+) Submodule path \u0026#39;DbConnector\u0026#39;: merged in \u0026#39;92c7337b30ef9e0893e758dac2459d07362ab5ea\u0026#39; 如果我们进入 DbConnector 目录,可以发现新的改动已经合并入本地 stable 分支。 现在让我们看看当我们对库做一些本地的改动而同时其他人推送另外一个修改到上游时会发生什么。\n$ cd DbConnector/ $ vim src/db.c $ git commit -am \u0026#39;unicode support\u0026#39; [stable f906e16] unicode support 1 file changed, 1 insertion(+) 如果我们现在更新子模块,就会看到当我们在本地做了更改时上游也有一个改动,我们需要将它并入本地。\n$ cd .. $ git submodule update --remote --rebase First, rewinding head to replay your work on top of it... Applying: unicode support Submodule path \u0026#39;DbConnector\u0026#39;: rebased into \u0026#39;5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94\u0026#39; 如果你忘记 \u0026ndash;rebase 或 \u0026ndash;merge,Git 会将子模块更新为服务器上的状态。并且会将项目重置为一个游离的 HEAD 状态。\n$ git submodule update --remote Submodule path \u0026#39;DbConnector\u0026#39;: checked out \u0026#39;5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94\u0026#39; 即便这真的发生了也不要紧,你只需回到目录中再次检出你的分支(即还包含着你的工作的分支)然后手动地合并或变基 origin/stable(或任何一个你想要的远程分支)就行了。\n如果你没有提交子模块的改动,那么运行一个子模块更新也不会出现问题,此时 Git 会只抓取更改而并不会覆盖子模块目录中未保存的工作。\n$ git submodule update --remote remote: Counting objects: 4, done. remote: Compressing objects: 100% (3/3), done. remote: Total 4 (delta 0), reused 4 (delta 0) Unpacking objects: 100% (4/4), done. From https://github.com/chaconinc/DbConnector 5d60ef9..c75e92a stable -\u0026gt; origin/stable error: Your local changes to the following files would be overwritten by checkout: scripts/setup.sh Please, commit your changes or stash them before you can switch branches. Aborting Unable to checkout \u0026#39;c75e92a2b3855c9e5b66f915308390d9db204aca\u0026#39; in submodule path \u0026#39;DbConnector\u0026#39; 如果你做了一些与上游改动冲突的改动,当运行更新时 Git 会让你知道。\n$ git submodule update --remote --merge Auto-merging scripts/setup.sh CONFLICT (content): Merge conflict in scripts/setup.sh Recorded preimage for \u0026#39;scripts/setup.sh\u0026#39; Automatic merge failed; fix conflicts and then commit the result. Unable to merge \u0026#39;c75e92a2b3855c9e5b66f915308390d9db204aca\u0026#39; in submodule path \u0026#39;DbConnector\u0026#39; 你可以进入子模块目录中然后就像平时那样修复冲突。\n发布子模块改动 现在我们的子模块目录中有一些改动。 其中有一些是我们通过更新从上游引入的,而另一些是本地生成的,由于我们还没有推送它们,所以对任何其他人都不可用。\n$ git diff Submodule DbConnector c87d55d..82d2ad3: \u0026gt; Merge from origin/stable \u0026gt; updated setup script \u0026gt; unicode support \u0026gt; remove unnecessary method \u0026gt; add new option for conn pooling 如果我们在主项目中提交并推送但并不推送子模块上的改动,其他尝试检出我们修改的人会遇到麻烦, 因为他们无法得到依赖的子模块改动。那些改动只存在于我们本地的拷贝中。\n为了确保这不会发生,你可以让 Git 在推送到主项目前检查所有子模块是否已推送。 git push 命令接受可以设置为 check`` 或 on-demand的\u0026ndash;recurse-submodules参数。 如果任何提交的子模块改动没有推送那么check选项会直接使push` 操作失败。\n$ git push --recurse-submodules=check The following submodule paths contain changes that can not be found on any remote: DbConnector Please try git push --recurse-submodules=on-demand or cd to the path and use git push to push them to a remote. 如你所见,它也给我们了一些有用的建议,指导接下来该如何做。 最简单的选项是进入每一个子模块中然后手动推送到远程仓库,确保它们能被外部访问到,之后再次尝试这次推送。 如果你想要对所有推送都执行检查,那么可以通过设置 git config push.recurseSubmodules check 让它成为默认行为。\n另一个选项是使用 “on-demand” 值,它会尝试为你这样做。\n$ git push --recurse-submodules=on-demand Pushing submodule \u0026#39;DbConnector\u0026#39; Counting objects: 9, done. Delta compression using up to 8 threads. Compressing objects: 100% (8/8), done. Writing objects: 100% (9/9), 917 bytes | 0 bytes/s, done. Total 9 (delta 3), reused 0 (delta 0) To https://github.com/chaconinc/DbConnector c75e92a..82d2ad3 stable -\u0026gt; stable Counting objects: 2, done. Delta compression using up to 8 threads. Compressing objects: 100% (2/2), done. Writing objects: 100% (2/2), 266 bytes | 0 bytes/s, done. Total 2 (delta 1), reused 0 (delta 0) To https://github.com/chaconinc/MainProject 3d6d338..9a377d1 master -\u0026gt; master 如你所见,Git 进入到 DbConnector 模块中然后在推送主项目前推送了它。 如果那个子模块因为某些原因推送失败,主项目也会推送失败。 你也可以通过设置 git config push.recurseSubmodules on-demand 让它成为默认行为。\n合并子模块改动 如果你其他人同时改动了一个子模块引用,那么可能会遇到一些问题。 也就是说,如果子模块的历史已经分叉并且在父项目中分别提交到了分叉的分支上,那么你需要做一些工作来修复它。\n如果一个提交是另一个的直接祖先(一个快进式合并),那么 Git 会简单地选择之后的提交来合并,这样没什么问题。\n不过,Git 甚至不会尝试去进行一次简单的合并。 如果子模块提交已经分叉且需要合并,那你会得到类似下面的信息:\n$ git pull remote: Counting objects: 2, done. remote: Compressing objects: 100% (1/1), done. remote: Total 2 (delta 1), reused 2 (delta 1) Unpacking objects: 100% (2/2), done. From https://github.com/chaconinc/MainProject 9a377d1..eb974f8 master -\u0026gt; origin/master Fetching submodule DbConnector warning: Failed to merge submodule DbConnector (merge following commits not found) Auto-merging DbConnector CONFLICT (submodule): Merge conflict in DbConnector Automatic merge failed; fix conflicts and then commit the result. 所以本质上 Git 在这里指出了子模块历史中的两个分支记录点已经分叉并且需要合并。 它将其解释为 “merge following commits not found” (未找到接下来需要合并的提交),虽然这有点令人困惑,不过之后我们会解释为什么是这样。\n为了解决这个问题,你需要弄清楚子模块应该处于哪种状态。 奇怪的是,Git 并不会给你多少能帮你摆脱困境的信息,甚至连两边提交历史中的 SHA-1 值都没有。 幸运的是,这很容易解决。 如果你运行 git diff,就会得到试图合并的两个分支中记录的提交的 SHA-1 值。\n$ git diff diff --cc DbConnector index eb41d76,c771610..0000000 --- a/DbConnector +++ b/DbConnector 所以,在本例中,eb41d76 是我们的子模块中大家共有的提交,而 c771610 是上游拥有的提交。 如果我们进入子模块目录中,它应该已经在 eb41d76 上了,因为合并没有动过它。 如果不是的话,无论什么原因,你都可以简单地创建并检出一个指向它的分支。\n来自另一边的提交的 SHA-1 值比较重要。 它是需要你来合并解决的。 你可以尝试直接通过 SHA-1 合并,也可以为它创建一个分支然后尝试合并。 我们建议后者,哪怕只是为了一个更漂亮的合并提交信息。\n所以,我们将会进入子模块目录,基于 git diff 的第二个 SHA-1 创建一个分支然后手动合并。\n$ cd DbConnector $ git rev-parse HEAD eb41d764bccf88be77aced643c13a7fa86714135 $ git branch try-merge c771610 (DbConnector) $ git merge try-merge Auto-merging src/main.c CONFLICT (content): Merge conflict in src/main.c Recorded preimage for 'src/main.c' Automatic merge failed; fix conflicts and then commit the result. 我们在这儿得到了一个真正的合并冲突,所以如果想要解决并提交它,那么只需简单地通过结果来更新主项目。\n$ vim src/main.c (1) $ git add src/main.c $ git commit -am 'merged our changes' Recorded resolution for 'src/main.c'. [master 9fd905e] merged our changes $ cd .. (2) $ git diff (3) diff --cc DbConnector index eb41d76,c771610..0000000 --- a/DbConnector +++ b/DbConnector @@@ -1,1 -1,1 +1,1 @@@ - Subproject commit eb41d764bccf88be77aced643c13a7fa86714135 -Subproject commit c77161012afbbe1f58b5053316ead08f4b7e6d1d ++Subproject commit 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a $ git add DbConnector (4) $ git commit -m \u0026quot;Merge Tom's Changes\u0026quot; (5) [master 10d2c60] Merge Tom's Changes 首先解决冲突\n 然后返回到主项目目录中\n 再次检查 SHA-1 值\n 解决冲突的子模块记录\n 提交我们的合并\n 这可能会让你有点儿困惑,但它确实不难。\n有趣的是,Git 还能处理另一种情况。 如果子模块目录中存在着这样一个合并提交,它的历史中包含了的两边的提交,那么 Git 会建议你将它作为一个可行的解决方案。 它看到有人在子模块项目的某一点上合并了包含这两次提交的分支,所以你可能想要那个。\n这就是为什么前面的错误信息是 “merge following commits not found”,因为它不能 这样 做。 它让人困惑是因为谁能想到它会尝试这样做?\n如果它找到了一个可以接受的合并提交,你会看到类似下面的信息:\n$ git merge origin/master warning: Failed to merge submodule DbConnector (not fast-forward) Found a possible merge resolution for the submodule: 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a: \u0026gt; merged our changes If this is correct simply add it to the index for example by using: git update-index --cacheinfo 160000 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a \u0026quot;DbConnector\u0026quot; which will accept this suggestion. Auto-merging DbConnector CONFLICT (submodule): Merge conflict in DbConnector Automatic merge failed; fix conflicts and then commit the result. Git 建议的命令是更新索引,就像你运行了 git add 那样,这样会清除冲突然后提交。 不过你可能不应该这样做。你可以轻松地进入子模块目录,查看差异是什么,快进到这次提交,恰当地测试,然后提交它。\n$ cd DbConnector/ $ git merge 9fd905e Updating eb41d76..9fd905e Fast-forward $ cd .. $ git add DbConnector $ git commit -am 'Fast forwarded to a common submodule child' 这些命令完成了同一件事,但是通过这种方式你至少可以验证工作是否有效,以及当你在完成时可以确保子模块目录中有你的代码。\n子模的块技巧 你可以做几件事情来让用子模块工作轻松一点儿。\n子模块遍历 有一个 foreach 子模块命令,它能在每一个子模块中运行任意命令。 如果项目中包含了大量子模块,这会非常有用。\n例如,假设我们想要开始开发一项新功能或者修复一些错误,并且需要在几个子模块内工作。 我们可以轻松地保存所有子模块的工作进度。\n$ git submodule foreach 'git stash' Entering 'CryptoLibrary' No local changes to save Entering 'DbConnector' Saved working directory and index state WIP on stable: 82d2ad3 Merge from origin/stable HEAD is now at 82d2ad3 Merge from origin/stable 然后我们可以创建一个新分支,并将所有子模块都切换过去。\n$ git submodule foreach 'git checkout -b featureA' Entering 'CryptoLibrary' Switched to a new branch 'featureA' Entering 'DbConnector' Switched to a new branch 'featureA' 你应该明白。 能够生成一个主项目与所有子项目的改动的统一差异是非常有用的。\n$ git diff; git submodule foreach 'git diff' Submodule DbConnector contains modified content diff --git a/src/main.c b/src/main.c index 210f1ae..1f0acdc 100644 --- a/src/main.c +++ b/src/main.c @@ -245,6 +245,8 @@ static int handle_alias(int *argcp, const char ***argv) commit_pager_choice(); + url = url_decode(url_orig); + /* build alias_argv */ alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1)); alias_argv[0] = alias_string + 1; Entering 'DbConnector' diff --git a/src/db.c b/src/db.c index 1aaefb6..5297645 100644 --- a/src/db.c +++ b/src/db.c @@ -93,6 +93,11 @@ char *url_decode_mem(const char *url, int len) return url_decode_internal(\u0026amp;url, len, NULL, \u0026amp;out, 0); } +char *url_decode(const char *url) +{ + return url_decode_mem(url, strlen(url)); +} + char *url_decode_parameter_name(const char **query) { struct strbuf out = STRBUF_INIT; 在这里,我们看到子模块中定义了一个函数并在主项目中调用了它。 这明显是个简化了的例子,但是希望它能让你明白这种方法的用处。\n有用的别名 你可能想为其中一些命令设置别名,因为它们可能会非常长而你又不能设置选项作为它们的默认选项。 我们在 Git 别名 介绍了设置 Git 别名, 但是如果你计划在 Git 中大量使用子模块的话,这里有一些例子。\n$ git config alias.sdiff '!'\u0026quot;git diff \u0026amp;\u0026amp; git submodule foreach 'git diff'\u0026quot; $ git config alias.spush 'push --recurse-submodules=on-demand' $ git config alias.supdate 'submodule update --remote --merge' 这样当你想要更新子模块时可以简单地运行 git supdate,或 git spush 检查子模块依赖后推送。\n子模块的问题 然而使用子模块还是有一些小问题。\n切换分支 例如,使用 Git 2.13 以前的版本时,在有子模块的项目中切换分支可能会造成麻烦。 如果你创建一个新分支,在其中添加一个子模块,之后切换到没有该子模块的分支上时,你仍然会有一个还未跟踪的子模块目录。\n$ git --version git version 2.12.2 $ git checkout -b add-crypto Switched to a new branch 'add-crypto' $ git submodule add https://github.com/chaconinc/CryptoLibrary Cloning into 'CryptoLibrary'... ... $ git commit -am 'adding crypto library' [add-crypto 4445836] adding crypto library 2 files changed, 4 insertions(+) create mode 160000 CryptoLibrary $ git checkout master warning: unable to rmdir CryptoLibrary: Directory not empty Switched to branch 'master' Your branch is up-to-date with 'origin/master'. $ git status On branch master Your branch is up-to-date with 'origin/master'. Untracked files: (use \u0026quot;git add \u0026lt;file\u0026gt;...\u0026quot; to include in what will be committed) CryptoLibrary/ nothing added to commit but untracked files present (use \u0026quot;git add\u0026quot; to track) 移除那个目录并不困难,但是有一个目录在那儿会让人有一点困惑。 如果你移除它然后切换回有那个子模块的分支,需要运行 submodule update --init 来重新建立和填充。\n$ git clean -fdx Removing CryptoLibrary/ $ git checkout add-crypto Switched to branch 'add-crypto' $ ls CryptoLibrary/ $ git submodule update --init Submodule path 'CryptoLibrary': checked out 'b8dda6aa182ea4464f3f3264b11e0268545172af' $ ls CryptoLibrary/ Makefile includes scripts src 再说一遍,这真的不难,只是会让人有点儿困惑。\n新版的 Git(\u0026gt;= 2.13)通过为 git checkout 命令添加 --recurse-submodules 选项简化了所有这些步骤, 它能为了我们要切换到的分支让子模块处于的正确状态。\n$ git --version git version 2.13.3 $ git checkout -b add-crypto Switched to a new branch 'add-crypto' $ git submodule add https://github.com/chaconinc/CryptoLibrary Cloning into 'CryptoLibrary'... ... $ git commit -am 'adding crypto library' [add-crypto 4445836] adding crypto library 2 files changed, 4 insertions(+) create mode 160000 CryptoLibrary $ git checkout --recurse-submodules master Switched to branch 'master' Your branch is up-to-date with 'origin/master'. $ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working tree clean 当你在父级项目的几个分支上工作时,对 git checkout 使用 --recurse-submodules 选项也很有用, 它能让你的子模块处于不同的提交上。确实,如果你在记录了子模块的不同提交的分支上切换, 那么在执行 git status 后子模块会显示为“已修改”并指出“新的提交”。 这是因为子模块的状态默认不会在切换分支时保留。\n这点非常让人困惑,因此当你的项目中拥有子模块时,可以总是使用 git checkout --recurse-submodules。 (对于没有 --recurse-submodules 选项的旧版 Git,在检出之后可使用 git submodule update --init --recursive 来让子模块处于正确的状态)。\n幸运的是,你可以通过 git config submodule.recurse true 设置 submodule.recurse 选项, 告诉 Git(\u0026gt;=2.14)总是使用 --recurse-submodules。 如上所述,这也会让 Git 为每个拥有 --recurse-submodules 选项的命令(除了 git clone) 总是递归地在子模块中执行。\n从子目录切换到子模块 另一个主要的告诫是许多人遇到了将子目录转换为子模块的问题。 如果你在项目中已经跟踪了一些文件,然后想要将它们移动到一个子模块中,那么请务必小心,否则 Git 会对你发脾气。 假设项目内有一些文件在子目录中,你想要将其转换为一个子模块。 如果删除子目录然后运行 submodule add,Git 会朝你大喊:\n$ rm -Rf CryptoLibrary/ $ git submodule add https://github.com/chaconinc/CryptoLibrary 'CryptoLibrary' already exists in the index 你必须要先取消暂存 CryptoLibrary 目录。 然后才可以添加子模块:\n$ git rm -r CryptoLibrary $ git submodule add https://github.com/chaconinc/CryptoLibrary Cloning into 'CryptoLibrary'... remote: Counting objects: 11, done. remote: Compressing objects: 100% (10/10), done. remote: Total 11 (delta 0), reused 11 (delta 0) Unpacking objects: 100% (11/11), done. Checking connectivity... done. 现在假设你在一个分支下做了这样的工作。 如果尝试切换回的分支中那些文件还在子目录而非子模块中时——你会得到这个错误:\n$ git checkout master error: The following untracked working tree files would be overwritten by checkout: CryptoLibrary/Makefile CryptoLibrary/includes/crypto.h ... Please move or remove them before you can switch branches. Aborting 你可以通过 checkout -f 来强制切换,但是要小心,如果其中还有未保存的修改,这个命令会把它们覆盖掉。\n$ git checkout -f master warning: unable to rmdir CryptoLibrary: Directory not empty Switched to branch 'master' 当你切换回来之后,因为某些原因你得到了一个空的 CryptoLibrary 目录,并且 git submodule update 也无法修复它。 你需要进入到子模块目录中运行 git checkout . 来找回所有的文件。 你也可以通过 submodule foreach 脚本来为多个子模块运行它。\n要特别注意的是,近来子模块会将它们的所有 Git 数据保存在顶级项目的 .git 目录中,所以不像旧版本的 Git,摧毁一个子模块目录并不会丢失任何提交或分支。\n拥有了这些工具,使用子模块会成为可以在几个相关但却分离的项目上同时开发的相当简单有效的方法。\n参考连接 Git 工具 - 子模块\n使用Git Submodule管理子模块\n ","id":5,"section":"posts","summary":"使用场景 基于公司的项目会越来越多,常常需要提取一个公共的类库提供给多个项目使用,但是这个library怎么和git在一起方便管理呢? 我们需要","tags":["Git"],"title":"Git submodule使用总结","uri":"https://fhxisdog.github.io/2020/04/git_submodule/","year":"2020"},{"content":" 本文记录本人安装Arch的过程以及后续配置,不做为教程,只作为备忘使用,仅供参考\n 前期准备 下载镜像 下载Rufus 由于我的电脑支持efi启动,制作efi/gpt格式的启动盘 系统安装 开机U盘启动 不同品牌型号的电脑,进入U盘启动的方式不一样,我这里的是F12选择U盘启动\n启动安装环境 MBR:\nUEFI:\n当屏幕上出现命令行提示符及闪烁的光标时即启动完毕。\n检查引导方式 ls /sys/firmware/efi/efivars 如果提示:\nls: cannot access \u0026#39;/sys/firmware/efi/efivars\u0026#39;: No such file or directory 表明你是以BIOS方式引导,否则为以EFI方式引导\n联网 我这里是插入网线了,所以执行命令:\ndhcpcd 如果你是wifi,请执行:\nwifi-menu 更新系统时间 timedatectl set-ntp true 分区与格式化 警告:请谨慎操作以防数据丢失\n 必要的分区如下:\n Arch Linux 要求至少一个分区分配给根目录 /。\n 在 UEFI 系统上,需要一个 UEFI 系统分区。\n 查看分区: fdisk -l 输出:\nDISK /dev/nvme0n1 xxxxxxxxxxxxx /dev/nvme0n1p1 xxx /dev/nvme0n1p2 xxx 输出像“Disk /dev/nvme0n1 xx GiB \u0026hellip;”这种的,xx也就是该存储设备的总容量,然后你判断哪个设备是有多余空间的。假如该设备是/dev/sda,这时候你还可以通过如下命令查看该存储设备下已有的分区情况:\nfdisk -l /dev/nvme0n1 格式化硬盘:(我硬盘没有重要数据所以格式化,如果你硬盘有数据,不要执行此命令!!!!!!!!!!!!) 输入:\nfdisk /dev/nvme0n1 d #这里省略输出,有几个分区执行几次,如果显示错误,提示硬盘已没有分区证明完成 y w #写入格式化结果,也可以不写入,下一步不执行fdisk /dev/nvme0n1就行,然后和新的分区结果一起写入 分区 如果你是MBR,只需要一个分区用于系统安装的分区: 创建根分区: fdisk /dev/nvme0n1 o #输入o来创建一个全新的MBR分区表。\n n #输入n创建一个新的分区,首先会让你选择起始扇区,一般直接回车使用默认数值即可,然后可以输入结束扇区或是分区大小,如果我们想要使创建的分区完全占满空闲的空间,可以直接回车使用默认结束扇区。\n 这里我的输入 p查看,我的引导分区名是:/dev/nvme0n1p1,所以我的引导分区是/dev/nvme0n1p1\n 输入w来将之前所有的操作写入磁盘生效,在这之前可以输入p来确认自己的分区表没有错误。\n 输入以下命令来格式化刚刚创建的根分区:\nmkfs.ext4 /dev/nvme0n1p1 #请将的sdxY替换为刚创建的分区,例如我的是 mkfs.ext4 /dev/nvme0n1p1 如果你是EFI模式,需要分两个分区: 第一个分区用于系统引导(512M) 第二个分区用于系统安装(49G) 操作步骤:\n创建引导分区 1.执行命令:\nfdisk /dev/nvme0n1 #自己的磁盘根据情况变化,名称可能不一样 2.输入n创建一个新的分区,首先会让你选择起始扇区,一般直接回车使用默认数值即可,然后可以输入结束扇区或是分区大小,这里我们输入+512M来创建一个512M的引导分区。\nCommand (? for help): n Partition number (1-128, default 1): ##直接回车 First sector (34-10485726, default = 2048) or {+-}size{KMGTP}: #直接回车 Last sector (2048-10485726, default = 10485726) or {+-}size{KMGTP}: +512M Current type is \u0026#39;Linux filesystem\u0026#39; Hex code or GUID (L to show codes, Enter = 8300): ef00 Changed type of partition to \u0026#39;EFI System\u0026#39; 3.这时我们可以输入p来查看新创建的分区。\n4.输入t并选择新创建的分区序号来更改分区的类型,输入l可以查看所有支持的类型,输入ef更改分区的类型为EFI。(这里我输入的是1)\n 这里我的输入 p查看,我的引导分区名是:/dev/nvme0n1p1,所以我的引导分区是/dev/nvme0n1p1\n 创建根分区: 输入n\n 一路回车\n 这里我的输入 p查看,我的引导分区名是:/dev/nvme0n1p2,所以我的分区是/dev/nvme0n1p2\n 写入: 输入w写入,退出fdisk\n格式化分区: mkfs.fat -F32 /dev/nvme0n1p1 #请将nvme0n1p1替换为刚创建的引导分区 这里我的分区名是 /dev/nvme0n1p1 mkfs.ext4 /dev/nvme0n1p2 #请将的nvme0n1p2替换为刚创建的分区 这里我的分区名是`/dev/nvme0n1p2` 挂载分区 如果你是MBR格式启动,只需挂在根分区即可: mount /dev/nvme0n1p1 /mnt #这里是MBR的根分区,注意别搞混了,上面的efi中 /dev/nvme0n1p1是引导分区 如果你是EFI启动,需要挂载启动分区和根分区 mount /dev/nvme0n1p2 /mnt #挂在根分区 mkdir /mnt/boot mount /dev/nvme0n1p1 /mnt/boot #上面的efi中 /dev/nvme0n1p1是引导分区 选择镜像源 编辑 /etc/pacman.d/mirrorlist,变更为:\nServer = http://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch Server = http://mirrors.zju.edu.cn/archlinux/$repo/os/$arch 安装基本包 下面就要安装最基本的ArchLinux包到磁盘上了。这是一个联网下载并安装的过程。\n执行以下命令:\npacstrap /mnt base base-devel linux linux-firmware dhcpcd 根据下载速度的不同在这里需要等待一段时间,当命令提示符重新出现的时候就可以进行下一步操作了。\n配置Fstab 生成自动挂载分区的fstab文件,执行以下命令:\ngenfstab -L /mnt \u0026gt;\u0026gt; /mnt/etc/fstab 由于这步比较重要,所以我们需要输出生成的文件来检查是否正确,执行以下命令:\ncat /mnt/etc/fstab Chroot Chroot意为Change root,相当于把操纵权交给我们新安装(或已经存在)的Linux系统,执行了这步以后,我们的操作都相当于在磁盘上新装的系统中进行。\n执行如下命令:\narch-chroot /mnt 这里顺便说一下,如果以后我们的系统出现了问题,只要插入U盘并启动, 将我们的系统根分区挂载到了/mnt下(如果有efi分区也要挂载到/mnt/boot下),再通过这条命令就可以进入我们的系统进行修复操作。\n设置时区 依次执行如下命令设置我们的时区为上海并生成相关文件:\nln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime hwclock --systohc 提前安装必须软件包 因为我们现在已经Chroot到了新的系统中,只有一些最基本的包(组件),这时候我们就需要自己安装新的包了,下面就要介绍一下ArchLinux下非常强大的包管理工具pacman,大部分情况下,一行命令就可以搞定包与依赖的问题。\n安装包的命令格式为pacman -S 包名,pacman会自动检查这个包所需要的其他包(即为依赖)并一起装上。下面我们就通过pacman来安装一些包,这些包在之后会用上,在这里先提前装好。\n执行如下命令(注意大小写,大小写错误会导致包无法找到):\npacman -S vim dialog wpa_supplicant networkmanager 设置Locale 设置我们使用的语言选项,编辑/etc/locale.gen文件,在文件中找到zh_CN.UTF-8 UTF-8 zh_HK.UTF-8 UTF-8 zh_TW.UTF-8 UTF-8 en_US.UTF-8 UTF-8这四行,去掉行首的#号.\n然后执行:\nlocale-gen 打开(不存在时会创建)/etc/locale.conf文件:\\\nvim /etc/locale.conf 在文件的第一行加入以下内容:\nLANG=en_US.UTF-8 设置主机名 打开(不存在时会创建)/etc/hostname文件:\nvim /etc/hostname 在文件的第一行输入你自己设定的一个myhostname,我这里输入的是Arch\n编辑/etc/hosts文件:\nvim /etc/hosts 在文件末添加如下内容(将Arch替换成你自己设定的主机名):\n127.0.0.1 localhost ::1 localhost 127.0.1.1 Arch.localdomain Arch 设置Root密码 Root是Linux中具有最高权限帐户,有些敏感的操作必须通过Root用户进行,比如使用pacman,我们之前进行所有的操作也都是以Root用户进行的,也正是因为Root的权限过高,如果使用不当会造成安全问题,所以我们之后会新建一个普通用户来进行日常的操作。在这里我们需要为Root帐户设置一个密码:\n执行如下命令:\npasswd 按提示设置并确认就可以了。\n安装Intel-ucode(非IntelCPU可以跳过此步骤) pacman -S intel-ucode 安装Bootloader systemd bootctl方式(仅efi引导方式可用): 安装bootctl: bootctl install 编辑/boot/loader/loader.conf(没有则新建): default archtimeout 4 vim编辑/boot/loader/entries/arch.conf(没有则新建): title Arch Linux linux /vmlinuz-linux initrd /initramfs-linux.img options root= 棘手的一步来了,最优解来自新加坡程序员Kai Hendry:\nesc键退出insert模式,打 :r !blkid\n将blkid读取到 你文件里,找到/dev/nvmen01p2(就是你根分区)里面的PARTUUID, 然后将其拼凑出如下的格式。:\ntitle Arch Linux linux /vmlinuz-linux initrd /intel-ucode.img initrd /initramfs-linux.img options root=PARTUUID=12345-67890-1234-123456-7890 rw grub方式 如果为BIOS/MBR引导方式: 安装grub包: pacman -S grub 部署grub: grub-install --target=i386-pc /dev/nvme0n1 (将sdx换成你安装的硬盘) 注意这里的sdx应该为硬盘(例如/dev/sda),而不是形如/dev/sda1这样的分区。\n 生成配置文件:\n grub-mkconfig -o /boot/grub/grub.cfg 如果你没有看到成功的提示信息,请仔细检查是否正确完成上面的过程。常见问题如下:\n1.如果报warning failed to connect to lvmetad,falling back to device scanning.错误。参照wiki中搜索关键词use_lvmetad所在位置,简单的方法是编辑/etc/lvm/lvm.conf这个文件,找到use_lvmetad = 1将1修改为0,保存,重新配置grub。\n如果为EFI/GPT引导方式: 安装grub与efibootmgr两个包: pacman -S grub efibootmgr 部署grub: grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=grub 生成配置文件: grub-mkconfig -o /boot/grub/grub.cfg 提示信息应与上面的类似,如果你发现错误,请仔细检查是否正确完成上面的过程。\n如果报warning failed to connect to lvmetad,falling back to device scanning.错误。参照这篇文章,简单的方法是编辑/etc/lvm/lvm.conf这个文件,找到use_lvmetad = 1将1修改为0,保存,重新配置grub\n如果报grub-probe: error: cannot find a GRUB drive for /dev/sdb1, check your device.map类似错误,并且sdb1这个地方是你的u盘,这是u盘uefi分区造成的错误,对我们的正常安装没有影响,可以不用理会这条错误。\n安装后检查 如果你是多系统,请注意上面一节中对os-prober这个包的安装。\n强烈建议使用如下命令检查是否成功生成各系统的入口,如果没有正常生成会出现开机没有系统入口的情况:\nvim /boot/grub/grub.cfg 检查接近末尾的menuentry部分是否有windows或其他系统名入口。下图例子中是Arch Linux入口与检测到的windows10入口(安装在/dev/sda1)\n如果你没有看到Arch Linux系统入口或者该文件不存在,请先检查/boot目录是否正确部署linux内核:\ncd /boot ls 查看是否有initramfs-linux-fallback.img initramfs-linux.img intel-ucode.img vmlinuz-linux这几个文件,如果都没有,说明linux内核没有被正确部署,很有可能是/boot目录没有被正确挂载导致的,确认/boot目录无误后,可以重新部署linux内核:\npacman -S linux 再重新生成配置文件,就可以找到系统入口。\n如果你已经安装os-prober包并生成配置文件后还是没有生成其他系统的入口:\n你目前处的U盘安装环境下有可能无法检测到其他系统的入口,请在下一步中重启登陆之后重新运行:\ngrub-mkconfig -o /boot/grub/grub.cfg 重启 接下来,你需要进行重启来启动已经安装好的系统,执行如下命令:\nexit 如果挂载了/mnt/boot,先umount /mnt/boot,再umount /mnt,否则直接umount /mnt:\numount /mnt/bootumount /mntreboot 注意这个时候你可能会卡在有两行提示的地方无法正常关机,长按电源键强制关机即可,没有影响。\n安装后基本配置 连接网络 现在我们是在新安装的系统上进行操作,所以我们要重新联网,我们在之前安装系统时已经提前装好了相关的包。所以现在只要跟之前一样:\n 如果你是有线网并且路由器支持DHCP的话插上网线后先执行以下命令获取IP地址: dhcpcd 无线网: wifi-menu 创建交换文件 交换文件可以在物理内存不足的时候将部分内存暂存到交换文件中,避免系统由于内存不足而完全停止工作。\n之前我们通常采用单独一个分区的方式作为交换分区,现在更推荐采用交换文件的方式,更便于我们的管理。\n分配一块空间用于交换文件,执行:\nfallocate -l 512M /swapfile (请将512M换成需要的大小,只能以M或G为单位) 交换文件的大小可以自己决定,推荐4G以下的物理内存,交换文件与物理内存一致,4G以上的物理内存,交换文件4-8G。\n更改权限,执行:\nchmod 600 /swapfile 设置交换文件,执行:\nmkswap /swapfile 启用交换文件,执行:\nswapon /swapfile 最后我们需要编辑/etc/fstab为交换文件设置一个入口,使用vim打开文件:\nvim /etc/fstab 注意编辑fstab文件的时候要格外注意不要修改之前的内容,直接在最后新起一行加入以下内容:\n/swapfile none swap defaults 0 0 新建用户 在这之前所有操作都是以root用户的身份进行的,由于root的权限过高,日常使用root用户是不安全的。Linux为我们提供了强大的用户与组的权限管理,提高了整个系统的安全性。这里我们就来新建一个用户。\n执行以下命令来创建一个名为username的用户(请自行替换username为你的用户名):\nuseradd -m -G wheel username (请自行替换username为你的用户名) 在这里稍微解释一下各参数的含义:\n-m:在创建时同时在/home目录下创建一个与用户名同名的文件夹,这个目录就是你的家目录啦!家目录有一个别名是~,你可以在任何地方使用~来代替家目录路径。这个神奇的目录将会用于存放你所有的个人资料、配置文件等所有跟系统本身无关的资料。这种设定带来了诸多优点:\n 只要家目录不变,你重装系统后只需要重新安装一下软件包(它们一般不存放在家目录),然后所有的配置都会从家目录中读取,完全不用重新设置软件着。 你可以在家目录不变的情况下更换你的发行版而不用重新配置你的环境。 切换用户后所有的设置会从新的用户的家目录中读取,将不同用户的资料与软件设置等完全隔离。 有些著名的配置文件比如vim的配置文件~/.vimrc,只要根据自己的使用习惯配置一次, 在另一个Linux系统下(例如你的服务器)把这个文件复制到家目录下,就可以完全恢复你的配置。 -G wheel:-G代表把用户加入一个组,对用户与组的概念感兴趣的同学可以自行查找有关资料学习。后面跟着的wheel就是加入的组名,至于为什么要加入这个组,后面会提到。\n当然记得为新用户设置一个密码,执行如下命令:\npasswd username (请自行替换username为你的用户名) 根据提示输入两次密码就可以了,注意,这是你的用户密码,推荐与之前设置的root用户的密码不同。\n配置sudo 我们已经创建好了一个新的用户,以后我们将会使用这个用户来登录,那么如果我们需要执行一些只有root用户才能执行的命令(例如修改系统文件、安装软件包)怎么办?当然我们可以通过:\nsu 命令来切换到root用户执行命令后再通过:\nexit 返回普通用户。\n但是sudo为我们提供了一个更快捷的办法,使用sudo,我们只要在需要root权权限执行的命令之前加上sudo就可以了,例如安装软件包:\nsudo pacman -S something 下面我们就来安装并配置sudo。\nsudo本身也是一个软件包,所以我们需要通过pacman来安装:\npacman -S sudo 接下来我们需要用专门的visudo命令来编辑sudo的配置文件:\nvisudo 实际上就是vim的操作,使用它是为了对编辑后的文件进行检查防止格式的错误。\n找到:\n# %wheel ALL=(ALL)ALL 这行,去掉之前的#注释符,保存并退出就可以了。\n这里的%wheel就是代表wheel组,意味着wheel组中的所有用户都可以使用sudo命令。\n当然为了安全使用sudo命令还是需要输入当前用户的密码的。\n配置好sudo以后,我们进行一次重启,执行:\nreboot 来重启你的电脑。\n重启以后输入你刚创建的用户名与密码来登录。注意登录后要重新进行联网操作(sudo dhcpcd)。\n图形界面的安装 安装集显与桌面环境以及启动器: sudo pacman -S xf86-video-intel xorg palsma sddm kconsole 设置开机启动服务和网络 sudo systemctl enable sddm #设置开机启动sddm sudo systemctl disable netctl #配置网络 sudo systemctl enable NetworkManager #注意大小写 sudo pacman -S network-manager-applet #安装工具栏工具来显示网络设置图标 重启后即可进入桌面环境\n设置中国源(如果不需要可以不设置) 升级系统到最新\nsudo pacman -Syyu 编辑/etc/pacman.conf配置源:\n[multilib] Include = /etc/pacman.d/mirrorlist [archlinuxcn] Server = https://mirrors.ustc.edu.cn/archlinuxcn/$arch #或者: [archlinuxcn] Server = https://mirrors.tuna.tsinghua.edu.cn/archlinuxcn/$arch 安装 archlinuxcn-keyring 导入 GPG key:\nsudo pacman -S archlinuxcn-keyring 直接这样会出错 sudo pacman -Syu haveged sudo systemctl start haveged sudo systemctl enable haveged sudo rm -fr /etc/pacman.d/gnupg sudo pacman-key --init sudo pacman-key --populate archlinux sudo pacman-key --populate archlinuxcn sudo pacman -S archlinuxcn-keyring sudo pacman -Syu 安装中文字体 sudo pacman -S adobe-source-han-sans-cn-fonts (思源黑体) sudo pacman -S ttf-dejavu sudo pacman -S wqy-zenhei sudo pacman -S wqy-microhei 编辑 ~/.xprofile或~/.xinitrc或/etc/profile(如果不需要系统语言是中文可以不设置):\nexport LANG=zh_CN.UTF-8 export LANGUAGE=zh_CN:en_US 安装输入法 安装fcitx sudo pacman -S fcitx fcitx-configtool fcitx-libpinyin 配置环境变量 编辑/etc/profile(也可以设置~/.pam_enviroment,但是只设置~/.pam_enviroment会导致emacs不能使用输入法):\nGTK_IM_MODULE=fcitx QT_IM_MODULE=fcitx XMODIFIERS=@im=fcitx 安装git并设置代理 sudo pacman -S git 设置代理\ngit config --global https.proxy http://127.0.0.1:1080 # 如果你的代理是http git config --global http.proxy http://127.0.0.1:1080 git config --global https.proxy socks5://127.0.0.1:1080 git config --global http.proxy socks5://127.0.0.1:1080 #如果你的代理是socks5 取消代理\ngit config --global --unset http.proxy git config --global --unset https.proxy 安装yay 安装git和base-devel 和 go sudo pacman -S git base-devel go 由于yay需要go的module,由于我大清国情,需要设置下go的proxy go env -w GO111MODULE=on go env -w GOPROXY=\u0026#34;https://goproxy.io,direct\u0026#34; 下载快照并安装 mkdir -P temp/yay cd temp/yay git clone https://aur/archlinux.org/yay.git makepkg si 设置清华源:\nyay --aururl \u0026#34;https://aur.tuna.tsinghua.edu.cn\u0026#34; --save yay -P -g #查看配置 安装wps yay -S wps-office ttf-wps-fonts 安装oh-my-zsh sudo pacman -S zsh 安装oh-my-zsh:\nsudo pacman -S sh sh -c \u0026#34;$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\u0026#34; #如果不行,把install.sh下载下来手动执行sh -c 重启即可\n参考连接: Archlinux从安装到基本配置\n以官方Wiki的方式安装ArchLinux\nArchLinux安装后的必须配置与图形界面安装教程\nArch Linux 安装指南 2019.12.01\n女生程序员教你15分钟安装Arch Linux\n ","id":6,"section":"posts","summary":"本文记录本人安装Arch的过程以及后续配置,不做为教程,只作为备忘使用,仅供参考 前期准备 下载镜像 下载Rufus 由于我的电脑支持efi启动,制","tags":["Arch","linux"],"title":"Arch安装记录","uri":"https://fhxisdog.github.io/2020/04/arch_install/","year":"2020"},{"content":" 本文转自xirong,已获得原作者授权,如需转载,请与原作者联系\n 说明: 个人在学习Git工作流的过程中,从原有的 SVN 模式很难完全理解Git的协作模式,直到有一天我看到了下面的文章,好多遗留在心中的困惑迎刃而解:\n 我们以使用SVN的工作流来使用Git有什么不妥? Git方便的branch在哪里,团队多人如何协作?冲突了怎么办?如何进行发布控制? 经典的master-发布、develop-主开发、hotfix-bug修复如何避免代码不经过验证上线? 如何在GitHub上面与他人一起协作,star-fork-pull request是怎样的流程? 我个人很感激这篇文章,所以进行了整理,希望能帮到更多的人。整篇文章由 xirong 整理自 oldratlee 的GitHub,方便统一的学习回顾,在此感谢下面两位的贡献。\n原文链接:Git Workflows and Tutorials 简体中文:由 oldratlee 翻译在 GitHub 上 Git工作流指南\n在第三部分 企业日常开发模式探索,xirong 结合自己所在公司使用git的版本分支开发过程,进行了总结,欢迎大家提出更好的建议。\n在第四部分 开发工作流的讨论中,引用了几篇文章,包括 Github 的开发流程以及 Thoughtworkers 工程师发表的「Gitflow 有害论」,旨在表名流程并不是唯一的,适合自己当前团队的才是最好的。\n 一、译序 这篇指南以大家在SVN中已经广为熟悉使用的集中式工作流作为起点,循序渐进地演进到其它高效的分布式工作流,还介绍了如何配合使用便利的Pull Request功能,系统地讲解了各种工作流的应用。 如果你Git用的还不多,可以从前面的讲的工作流开始操练。在操作过程中去感受指南的讲解:解决什么问题、如何解决问题,这样理解就深了,也方便活用。\n行文中实践原则和操作示例并重,对于Git的资深玩家可以梳理思考提升,而新接触的同学,也可以跟着step-by-step操练学习并在实际工作中上手使用。\n工作流其实不是一个初级主题,背后的本质问题是 有效的项目流程管理 和 高效的开发协同约定,而不仅仅是Git或SVN等VCS或SCM工具的使用。\n关于Git工作流主题,网上体系的中文资料不多,主要是零散的操作说明,希望这篇文章能让你更深入理解并在工作中灵活有效地使用起来。\nGitflow工作流是经典模型,处于核心位置,体现了工作流的经验和精髓。随着项目过程复杂化,你会感受到这个工作流中的深思熟虑和威力!\nForking工作流是分布式协作的(GitHub风格)可以先看看GitHub的Help:Fork A Repo和Using pull requests 。照着操作,给一个GitHub项目贡献你的提交,有操作经验再看指南容易意会。指南中给了自己实现Fork的方法:Fork就是服务端的克隆。在指南的操练中使用代码托管服务(如GitHub、Bitbucket),可以点一下按钮就让开发者完成仓库的fork操作。\nPS:\n文中Pull Request的介绍用的是Bitbucket代码托管服务,由于和GitHub基本一样,如果你用的是GitHub(我自己也主要使用GitHub托管代码),不影响理解和操作。\nPPS:\n更多Git学习资料参见\n Git的资料整理 by @xirong 自己整理的分享PPT Git使用与实践 @ 个人整理一些Git 🙈 自己理解粗浅,翻译中不足和不对之处,欢迎 👏 建议,提交Issue 指正,Fork后提通过Pull Requst贡献修改 如有文章理解上有疑问 或是 使用过程中碰到些疑惑,请随时🙌提交Issue ,一起交流学习讨论! 二、Git工作流指南 👉 工作流有各式各样的用法,但也正因此使得在实际工作中如何上手使用变得很头大。这篇指南通过总览公司团队中最常用的几种Git工作流让大家可以上手使用。\n在阅读的过程中请记住,本文中的几种工作流是作为方案指导而不是条例规定。在展示了各种工作流可能的用法后,你可以从不同的工作流中挑选或揉合出一个满足你自己需求的工作流。\n2.1 集中式工作流 如果你的开发团队成员已经很熟悉Subversion,集中式工作流让你无需去适应一个全新流程就可以体验Git带来的收益。这个工作流也可以作为向更Git风格工作流迁移的友好过渡。 转到分布式版本控制系统看起来像个令人生畏的任务,但不改变已用的工作流,你也可以用上Git带来的收益。团队可以用和Subversion完全不变的方式来开发项目。\n但使用Git加强开发的工作流,相比SVN,Git有以下两个优势: 首先,每个开发者可以有属于自己的整个工程的本地拷贝。隔离的环境让各个开发者的工作和项目的其他部分修改独立开来 —— 即自由地提交到自己的本地仓库,先完全忽略上游的开发,直到方便的时候再把修改反馈上去。\n其次,Git提供了强壮的分支和合并模型。不像SVN,Git的分支设计成可以做为一种用来在仓库之间集成代码和分享修改的『失败安全』的机制。\n2.1.1 工作方式 像Subversion一样,集中式工作流以中央仓库作为项目所有修改的单点实体。相比SVN缺省的开发分支trunk,Git叫做master,所有修改提交到这个分支上。本工作流只用到master这一个分支。\n首先,开发者克隆中央仓库。在自己的项目拷贝中,像SVN一样的编辑文件和提交修改;但修改是存在本地的,和中央仓库是完全隔离的。开发者可以把和上游的同步延后到一个方便时间点。\n然后,开发者发布修改到正式项目中,开发者要把本地master分支的修改『推』到中央仓库中。这相当于svn commit操作,但push操作会把所有还不在中央仓库的本地提交都推上去。\n2.1.2 冲突解决 中央仓库代表了正式项目,所以提交历史应该被尊重且是稳定不变的。如果开发者本地的提交历史和中央仓库有分歧,Git会拒绝push提交否则会覆盖已经在中央库的正式提交。\n在开发者提交自己功能修改到中央库前,需要先fetch在中央库的新增提交,rebase自己提交到中央库提交历史之上。 这样做的意思是在说,『我要把自己的修改加到别人已经完成的修改上。』最终的结果是一个完美的线性历史,就像以前的SVN的工作流中一样。\n如果本地修改和上游提交有冲突,Git会暂停rebase过程,给你手动解决冲突的机会。Git解决合并冲突,用和生成提交一样的git status和git add命令,很一致方便。还有一点,如果解决冲突时遇到麻烦,Git可以很简单中止整个rebase操作,重来一次(或者让别人来帮助解决)。\n2.1.3 示例 让我们一起逐步分解来看看一个常见的小团队如何用这个工作流来协作的。有两个开发者小明和小红,看他们是如何开发自己的功能并提交到中央仓库上的。\n有人先初始化好中央仓库 第一步,有人在服务器上创建好中央仓库。如果是新项目,你可以初始化一个空仓库;否则你要导入已有的Git或SVN仓库。\n中央仓库应该是个裸仓库(bare repository),即没有工作目录(working directory)的仓库。可以用下面的命令创建:\nssh user@host git init --bare /path/to/repo.git 确保写上有效的user(SSH的用户名),host(服务器的域名或IP地址),/path/to/repo.git(你想存放仓库的位置)。 注意,为了表示是一个裸仓库,按照约定加上.git扩展名到仓库名上。\n所有人克隆中央仓库 下一步,各个开发者创建整个项目的本地拷贝。通过git clone命令完成:\ngit clone ssh://user@host/path/to/repo.git 基于你后续会持续和克隆的仓库做交互的假设,克隆仓库时Git会自动添加远程别名origin指回『父』仓库。\n小明开发功能 在小明的本地仓库中,他使用标准的Git过程开发功能:编辑、暂存(Stage)和提交。 如果你不熟悉暂存区(Staging Area),这里说明一下:暂存区用来准备一个提交,但可以不用把工作目录中所有的修改内容都包含进来。 这样你可以创建一个高度聚焦的提交,尽管你本地修改很多内容。\ngit status # 查看本地仓库的修改状态 git add # 暂存文件 git commit # 提交文件 请记住,因为这些命令生成的是本地提交,小明可以按自己需求反复操作多次,而不用担心中央仓库上有了什么操作。 对需要多个更简单更原子分块的大功能,这个做法是很有用的。\n小红开发功能 与此同时,小红在自己的本地仓库中用相同的编辑、暂存和提交过程开发功能。和小明一样,她也不关心中央仓库有没有新提交; 当然更不关心小明在他的本地仓库中的操作,因为所有本地仓库都是私有的。\n小明发布功能 一旦小明完成了他的功能开发,会发布他的本地提交到中央仓库中,这样其它团队成员可以看到他的修改。他可以用下面的git push命令:\ngit push origin master 注意,origin是在小明克隆仓库时Git创建的远程中央仓库别名。master参数告诉Git推送的分支。 由于中央仓库自从小明克隆以来还没有被更新过,所以push操作不会有冲突,成功完成。\n小红试着发布功能 一起来看看在小明发布修改后,小红push修改会怎么样?她使用完全一样的push命令:\ngit push origin master 但她的本地历史已经和中央仓库有分岐了,Git拒绝操作并给出下面很长的出错消息:\nerror: failed to push some refs to '/path/to/repo.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Merge the remote changes (e.g. 'git pull') hint: before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details. 这避免了小红覆写正式的提交。她要先pull小明的更新到她的本地仓库合并上她的本地修改后,再重试。\n小红在小明的提交之上rebase 小红用git pull合并上游的修改到自己的仓库中。 这条命令类似svn update——拉取所有上游提交命令到小红的本地仓库,并尝试和她的本地修改合并:\ngit pull --rebase origin master --rebase选项告诉Git把小红的提交移到同步了中央仓库修改后的master分支的顶部,如下图所示:\n如果你忘加了这个选项,pull操作仍然可以完成,但每次pull操作要同步中央仓库中别人修改时,提交历史会以一个多余的『合并提交』结尾。 对于集中式工作流,最好是使用rebase而不是生成一个合并提交。\n小红解决合并冲突 rebase操作过程是把本地提交一次一个地迁移到更新了的中央仓库master分支之上。 这意味着可能要解决在迁移某个提交时出现的合并冲突,而不是解决包含了所有提交的大型合并时所出现的冲突。 这样的方式让你尽可能保持每个提交的聚焦和项目历史的整洁。反过来,简化了哪里引入Bug的分析,如果有必要,回滚修改也可以做到对项目影响最小。\n如果小红和小明的功能是不相关的,不大可能在rebase过程中有冲突。如果有,Git在合并有冲突的提交处暂停rebase过程,输出下面的信息并带上相关的指令:\nCONFLICT (content): Merge conflict in \u0026lt;some-file\u0026gt; Git很赞的一点是,任何人可以解决他自己的冲突。在这个例子中,小红可以简单的运行git status命令来查看哪里有问题。 冲突文件列在Unmerged paths(未合并路径)一节中:\n# Unmerged paths: # (use \u0026quot;git reset HEAD \u0026lt;some-file\u0026gt;...\u0026quot; to unstage) # (use \u0026quot;git add/rm \u0026lt;some-file\u0026gt;...\u0026quot; as appropriate to mark resolution) # # both modified: \u0026lt;some-file\u0026gt; 接着小红编辑这些文件。修改完成后,用老套路暂存这些文件,并让git rebase完成剩下的事:\ngit add \u0026lt;some-file\u0026gt; git rebase --continue 要做的就这些了。Git会继续一个一个地合并后面的提交,如其它的提交有冲突就重复这个过程。\n如果你碰到了冲突,但发现搞不定,不要惊慌。只要执行下面这条命令,就可以回到你执行git pull --rebase命令前的样子:\ngit rebase --abort 小红成功发布功能 小红完成和中央仓库的同步后,就能成功发布她的修改了:\ngit push origin master 如你所见,仅使用几个Git命令我们就可以模拟出传统Subversion开发环境。对于要从SVN迁移过来的团队来说这太好了,但没有发挥出Git分布式本质的优势。\n如果你的团队适应了集中式工作流,但想要更流畅的协作效果,绝对值得探索一下 功能分支工作流 的收益。 通过为一个功能分配一个专门的分支,能够做到一个新增功能集成到正式项目之前对新功能进行深入讨论。\n 2.2 功能分支工作流 功能分支工作流以集中式工作流为基础,不同的是为各个新功能分配一个专门的分支来开发。这样可以在把新功能集成到正式项目前,用Pull Requests的方式讨论变更。\n一旦你玩转了集中式工作流,在开发过程中可以很简单地加上功能分支,用来鼓励开发者之间协作和简化交流。\n功能分支工作流背后的核心思路是所有的功能开发应该在一个专门的分支,而不是在master分支上。 这个隔离可以方便多个开发者在各自的功能上开发而不会弄乱主干代码。 另外,也保证了master分支的代码一定不会是有问题的,极大有利于集成环境。\n功能开发隔离也让pull requests工作流成功可能, pull requests工作流能为每个分支发起一个讨论,在分支合入正式项目之前,给其它开发者有表示赞同的机会。 另外,如果你在功能开发中有问题卡住了,可以开一个pull requests来向同学们征求建议。 这些做法的重点就是,pull requests让团队成员之间互相评论工作变成非常方便!\n2.2.1 工作方式 功能分支工作流仍然用中央仓库,并且master分支还是代表了正式项目的历史。 但不是直接提交本地历史到各自的本地master分支,开发者每次在开始新功能前先创建一个新分支。 功能分支应该有个有描述性的名字,比如animated-menu-items或issue-#1061,这样可以让分支有个清楚且高聚焦的用途。\n对于master分支和功能分支,Git是没有技术上的区别,所以开发者可以用和集中式工作流中完全一样的方式编辑、暂存和提交修改到功能分支上。\n另外,功能分支也可以(且应该)push到中央仓库中。这样不修改正式代码就可以和其它开发者分享提交的功能。 由于master是仅有的一个『特殊』分支,在中央仓库上存多个功能分支不会有任何问题。当然,这样做也可以很方便地备份各自的本地提交。\n2.2.2 Pull Requests 功能分支除了可以隔离功能的开发,也使得通过Pull Requests讨论变更成为可能。 一旦某个开发者完成一个功能,不是立即合并到master,而是push到中央仓库的功能分支上并发起一个Pull Request请求,将修改合并到master。 在修改成为主干代码前,这让其它的开发者有机会先去Review变更。\nCode Review是Pull Requests的一个重要的收益,而Pull Requests则是讨论代码的一个通用方式。 你可以把Pull Requests作为专门给某个分支的讨论。这意味着可以在更早的开发过程中就可以进行Code Review。 比如,一个开发者开发功能需要帮助时,要做的就是发起一个Pull Request,相关的人就会自动收到通知,在相关的提交旁边能看到需要帮助解决的问题。\n一旦Pull Request被接受了,发布功能要做的就和集中式工作流就很像了。 首先,确定本地的master分支和上游的master分支是同步的。然后合并功能分支到本地master分支并push已经更新的本地master分支到中央仓库。\n仓库管理的产品解决方案像Bitbucket或Stash,可以良好地支持Pull Requests。可以看看Stash的Pull Requests文档。\n2.2.3 示例 下面的示例演示了如何把Pull Requests作为Code Review的方式,但注意Pull Requests可以用于很多其它的目的。\n小红开始开发一个新功能 在开始开发功能前,小红需要一个独立的分支。使用下面的命令新建一个分支:\ngit checkout -b marys-feature master 这个命令检出一个基于master名为marys-feature的分支,Git的-b选项表示如果分支还不存在则新建分支。 这个新分支上,小红按老套路编辑、暂存和提交修改,按需要提交以实现功能:\ngit status git add \u0026lt;some-file\u0026gt; git commit 小红要去吃个午饭 早上小红为新功能添加一些提交。 去吃午饭前,push功能分支到中央仓库是很好的做法,这样可以方便地备份,如果和其它开发协作,也让他们可以看到小红的提交。\ngit push -u origin marys-feature 这条命令push marys-feature分支到中央仓库(origin),-u选项设置本地分支去跟踪远程对应的分支。 设置好跟踪的分支后,小红就可以使用git push命令省去指定推送分支的参数。\n小红完成功能开发 小红吃完午饭回来,完成整个功能的开发。在合并到master之前, 她发起一个Pull Request让团队的其它人知道功能已经完成。但首先,她要确认中央仓库中已经有她最近的提交:\ngit push 然后,在她的Git GUI客户端中发起Pull Request,请求合并marys-feature到master,团队成员会自动收到通知。 Pull Request很酷的是可以在相关的提交旁边显示评注,所以你可以对某个变更集提问。\n小黑收到Pull Request 小黑收到了Pull Request后会查看marys-feature的修改。决定在合并到正式项目前是否要做些修改,且通过Pull Request和小红来回地讨论。\n小红再做修改 要再做修改,小红用和功能第一个迭代完全一样的过程。编辑、暂存、提交并push更新到中央仓库。小红这些活动都会显示在Pull Request上,小黑可以断续做评注。\n如果小黑有需要,也可以把marys-feature分支拉到本地,自己来修改,他加的提交也会一样显示在Pull Request上。\n小红发布她的功能 一旦小黑可以的接受Pull Request,就可以合并功能到稳定项目代码中(可以由小黑或是小红来做这个操作):\ngit checkout master git pull git pull origin marys-feature git push 无论谁来做合并,首先要检出master分支并确认是它是最新的。然后执行git pull origin marys-feature合并marys-feature分支到和已经和远程一致的本地master分支。 你可以使用简单git merge marys-feature命令,但前面的命令可以保证总是最新的新功能分支。 最后更新的master分支要重新push回到origin。\n这个过程常常会生成一个合并提交。有些开发者喜欢有合并提交,因为它像一个新功能和原来代码基线的连通符。 但如果你偏爱线性的提交历史,可以在执行合并时rebase新功能到master分支的顶部,这样生成一个快进(fast-forward)的合并。\n一些GUI客户端可以只要点一下『接受』按钮执行好上面的命令来自动化Pull Request接受过程。 如果你的不能这样,至少在功能合并到master分支后能自动关闭Pull Request。\n与此同时,小明在做和小红一样的事 当小红和小黑在marys-feature上工作并讨论她的Pull Request的时候,小明在自己的功能分支上做完全一样的事。\n通过隔离功能到独立的分支上,每个人都可以自主的工作,当然必要的时候在开发者之间分享变更还是比较繁琐的。\n到了这里,但愿你发现了功能分支可以很直接地在 集中式工作流 的仅有的master分支上完成多功能的开发。 另外,功能分支还使用了Pull Request,使得可以在你的版本控制GUI客户端中讨论某个提交。\n功能分支工作流是开发项目异常灵活的方式。问题是,有时候太灵活了。对于大型团队,常常需要给不同分支分配一个更具体的角色。 Gitflow工作流是管理功能开发、发布准备和维护的常用模式。\n 2.3 Gitflow工作流 Gitflow工作流通过为功能开发、发布准备和维护分配独立的分支,让发布迭代过程更流畅。严格的分支模型也为大型项目提供了一些非常必要的结构。\n这节介绍的Gitflow工作流借鉴自在nvie的Vincent Driessen。\nGitflow工作流定义了一个围绕项目发布的严格分支模型。虽然比功能分支工作流复杂几分,但提供了用于一个健壮的用于管理大型项目的框架。\nGitflow工作流没有用超出功能分支工作流的概念和命令,而是为不同的分支分配一个明确的角色,并定义分支之间如何和什么时候进行交互。 除了使用功能分支,在做准备、维护和记录发布时,也定义了各自的分支。 当然你可以用上功能分支工作流所有的好处:Pull Requests、隔离实验性开发和更高效的协作。\n2.3.1 工作方式 Gitflow工作流仍然用中央仓库作为所有开发者的交互中心。和其它的工作流一样,开发者在本地工作并push分支到要中央仓库中。\n2.3.2 历史分支 相对于使用仅有的一个master分支,Gitflow工作流使用两个分支来记录项目的历史。master分支存储了正式发布的历史,而develop分支作为功能的集成分支。 这样也方便master分支上的所有提交分配一个版本号。\n剩下要说明的问题围绕着这2个分支的区别展开。\n2.3.3 功能分支 每个新功能位于一个自己的分支,这样可以push到中央仓库以备份和协作。 但功能分支不是从master分支上拉出新分支,而是使用develop分支作为父分支。当新功能完成时,合并回develop分支。 新功能提交应该从不直接与master分支交互。\n注意,从各种含义和目的上来看,功能分支加上develop分支就是功能分支工作流的用法。但Gitflow工作流没有在这里止步。\n2.3.4 发布分支 一旦develop分支上有了做一次发布(或者说快到了既定的发布日)的足够功能,就从develop分支上checkout一个发布分支。 新建的分支用于开始发布循环,所以从这个时间点开始之后新的功能不能再加到这个分支上—— 这个分支只应该做Bug修复、文档生成和其它面向发布任务。 一旦对外发布的工作都完成了,发布分支合并到master分支并分配一个版本号打好Tag。 另外,这些从新建发布分支以来的做的修改要合并回develop分支。\n使用一个用于发布准备的专门分支,使得一个团队可以在完善当前的发布版本的同时,另一个团队可以继续开发下个版本的功能。 这也打造定义良好的开发阶段(比如,可以很轻松地说,『这周我们要做准备发布版本4.0』,并且在仓库的目录结构中可以实际看到)。\n常用的分支约定:\n用于新建发布分支的分支: develop 用于合并的分支: master 分支命名: release-* 或 release/* 2.3.5 维护分支 维护分支或说是热修复(hotfix)分支用于给产品发布版本(production releases)快速生成补丁,这是唯一可以直接从master分支fork出来的分支。 修复完成,修改应该马上合并回master分支和develop分支(当前的发布分支),master分支应该用新的版本号打好Tag。\n为Bug修复使用专门分支,让团队可以处理掉问题而不用打断其它工作或是等待下一个发布循环。 你可以把维护分支想成是一个直接在master分支上处理的临时发布。\n2.3.6 示例 下面的示例演示本工作流如何用于管理单个发布循环。假设你已经创建了一个中央仓库。\n创建开发分支 第一步为master分支配套一个develop分支。简单来做可以本地创建一个空的develop分支,push到服务器上:\ngit branch develop git push -u origin develop 以后这个分支将会包含了项目的全部历史,而master分支将只包含了部分历史。其它开发者这时应该克隆中央仓库,建好develop分支的跟踪分支:\ngit clone ssh://user@host/path/to/repo.git git checkout -b develop origin/develop 现在每个开发都有了这些历史分支的本地拷贝。\n小红和小明开始开发新功能 这个示例中,小红和小明开始各自的功能开发。他们需要为各自的功能创建相应的分支。新分支不是基于master分支,而是应该基于develop分支:\ngit checkout -b some-feature develop 他们用老套路添加提交到各自功能分支上:编辑、暂存、提交:\ngit status git add \u0026lt;some-file\u0026gt; git commit 小红完成功能开发 添加了提交后,小红觉得她的功能OK了。如果团队使用Pull Requests,这时候可以发起一个用于合并到develop分支。 否则她可以直接合并到她本地的develop分支后push到中央仓库:\ngit pull origin develop git checkout develop git merge some-feature git push git branch -d some-feature 第一条命令在合并功能前确保develop分支是最新的。注意,功能决不应该直接合并到master分支。 冲突解决方法和集中式工作流一样。\n小红开始准备发布 这个时候小明正在实现他的功能,小红开始准备她的第一个项目正式发布。 像功能开发一样,她用一个新的分支来做发布准备。这一步也确定了发布的版本号:\ngit checkout -b release-0.1 develop 这个分支是清理发布、执行所有测试、更新文档和其它为下个发布做准备操作的地方,像是一个专门用于改善发布的功能分支。\n只要小红创建这个分支并push到中央仓库,这个发布就是功能冻结的。任何不在develop分支中的新功能都推到下个发布循环中。\n小红完成发布 一旦准备好了对外发布,小红合并修改到master分支和develop分支上,删除发布分支。合并回develop分支很重要,因为在发布分支中已经提交的更新需要在后面的新功能中也要是可用的。 另外,如果小红的团队要求Code Review,这是一个发起Pull Request的理想时机。\ngit checkout master git merge release-0.1 git push git checkout develop git merge release-0.1 git push git branch -d release-0.1 发布分支是作为功能开发(develop分支)和对外发布(master分支)间的缓冲。只要有合并到master分支,就应该打好Tag以方便跟踪。\ngit tag -a 0.1 -m \u0026quot;Initial public release\u0026quot; master git push --tags Git有提供各种勾子(hook),即仓库有事件发生时触发执行的脚本。 可以配置一个勾子,在你push中央仓库的master分支时,自动构建好版本,并对外发布。\n最终用户发现Bug 对外版本发布后,小红小明一起开发下一版本的新功能,直到有最终用户开了一个Ticket抱怨当前版本的一个Bug。 为了处理Bug,小红(或小明)从master分支上拉出了一个维护分支,提交修改以解决问题,然后直接合并回master分支:\ngit checkout -b issue-#001 master # Fix the bug git checkout master git merge issue-#001 git push 就像发布分支,维护分支中新加这些重要修改需要包含到develop分支中,所以小红要执行一个合并操作。然后就可以安全地删除这个分支了:\ngit checkout develop git merge issue-#001 git push git branch -d issue-#001 到了这里,但愿你对集中式工作流、功能分支工作流和Gitflow工作流已经感觉很舒适了。 你应该也牢固的掌握了本地仓库的潜能,push/pull模式和Git健壮的分支和合并模型。\n记住,这里演示的工作流只是可能用法的例子,而不是在实际工作中使用Git不可违逆的条例。 所以不要畏惧按自己需要对工作流的用法做取舍。不变的目标就是让Git为你所用。\n 2.4 Forking工作流 Forking工作流是分布式工作流,充分利用了Git在分支和克隆上的优势。可以安全可靠地管理大团队的开发者(developer),并能接受不信任贡献者(contributor)的提交。\nForking工作流和前面讨论的几种工作流有根本的不同,这种工作流不是使用单个服务端仓库作为『中央』代码基线,而让各个开发者都有一个服务端仓库。这意味着各个代码贡献者有2个Git仓库而不是1个:一个本地私有的,另一个服务端公开的。\nForking工作流的一个主要优势是,贡献的代码可以被集成,而不需要所有人都能push代码到仅有的中央仓库中。 开发者push到自己的服务端仓库,而只有项目维护者才能push到正式仓库。 这样项目维护者可以接受任何开发者的提交,但无需给他正式代码库的写权限。\n效果就是一个分布式的工作流,能为大型、自发性的团队(包括了不受信的第三方)提供灵活的方式来安全的协作。 也让这个工作流成为开源项目的理想工作流。\n2.4.1 工作方式 和其它的Git工作流一样,Forking工作流要先有一个公开的正式仓库存储在服务器上。 但一个新的开发者想要在项目上工作时,不是直接从正式仓库克隆,而是fork正式项目在服务器上创建一个拷贝。\n这个仓库拷贝作为他个人公开仓库 —— 其它开发者不允许push到这个仓库,但可以pull到修改(后面我们很快就会看这点很重要)。 在创建了自己服务端拷贝之后,和之前的工作流一样,开发者执行git clone命令克隆仓库到本地机器上,作为私有的开发环境。\n要提交本地修改时,push提交到自己公开仓库中 —— 而不是正式仓库中。 然后,给正式仓库发起一个pull request,让项目维护者知道有更新已经准备好可以集成了。 对于贡献的代码,pull request也可以很方便地作为一个讨论的地方。\n为了集成功能到正式代码库,维护者pull贡献者的变更到自己的本地仓库中,检查变更以确保不会让项目出错, 合并变更到自己本地的master分支, 然后pushmaster分支到服务器的正式仓库中。 到此,贡献的提交成为了项目的一部分,其它的开发者应该执行pull操作与正式仓库同步自己本地仓库。\n2.4.2 正式仓库 在Forking工作流中,『官方』仓库的叫法只是一个约定,理解这点很重要。 从技术上来看,各个开发者仓库和正式仓库在Git看来没有任何区别。 事实上,让正式仓库之所以正式的唯一原因是它是项目维护者的公开仓库。\n2.4.3 Forking工作流的分支使用方式 所有的个人公开仓库实际上只是为了方便和其它的开发者共享分支。 各个开发者应该用分支隔离各个功能,就像在功能分支工作流和Gitflow工作流一样。 唯一的区别是这些分支被共享了。在Forking工作流中这些分支会被pull到另一个开发者的本地仓库中,而在功能分支工作流和Gitflow工作流中是直接被push到正式仓库中。\n2.4.4 示例 项目维护者初始化正式仓库 和任何使用Git项目一样,第一步是创建在服务器上一个正式仓库,让所有团队成员都可以访问到。 通常这个仓库也会作为项目维护者的公开仓库。\n公开仓库应该是裸仓库,不管是不是正式代码库。 所以项目维护者会运行像下面的命令来搭建正式仓库:\nssh user@host git init --bare /path/to/repo.git Bitbucket和Stash提供了一个方便的GUI客户端以完成上面命令行做的事。 这个搭建中央仓库的过程和前面提到的工作流完全一样。 如果有现存的代码库,维护者也要push到这个仓库中。\n开发者fork正式仓库 其它所有的开发需要fork正式仓库。 可以用git clone命令用SSH协议连通到服务器, 拷贝仓库到服务器另一个位置 —— 是的,fork操作基本上就只是一个服务端的克隆。 Bitbucket和Stash上可以点一下按钮就让开发者完成仓库的fork操作。\n这一步完成后,每个开发都在服务端有一个自己的仓库。和正式仓库一样,这些仓库应该是裸仓库。\n开发者克隆自己fork出来的仓库 下一步,各个开发者要克隆自己的公开仓库,用熟悉的git clone命令。\n在这个示例中,假定用Bitbucket托管了仓库。记住,如果这样的话各个开发者需要有各自的Bitbucket账号, 使用下面命令克隆服务端自己的仓库:\ngit clone https://user@bitbucket.org/user/repo.git 相比前面介绍的工作流只用了一个origin远程别名指向中央仓库,Forking工作流需要2个远程别名 —— 一个指向正式仓库,另一个指向开发者自己的服务端仓库。别名的名字可以任意命名,常见的约定是使用origin作为远程克隆的仓库的别名 (这个别名会在运行git clone自动创建),upstream(上游)作为正式仓库的别名。\ngit remote add upstream https://bitbucket.org/maintainer/repo 需要自己用上面的命令创建upstream别名。这样可以简单地保持本地仓库和正式仓库的同步更新。 注意,如果上游仓库需要认证(比如不是开源的),你需要提供用户:\ngit remote add upstream https://user@bitbucket.org/maintainer/repo.git 这时在克隆和pull正式仓库时,需要提供用户的密码。\n开发者开发自己的功能 在刚克隆的本地仓库中,开发者可以像其它工作流一样的编辑代码、提交修改和新建分支:\ngit checkout -b some-feature # Edit some code git commit -a -m \u0026quot;Add first draft of some feature\u0026quot; 所有的修改都是私有的直到push到自己公开仓库中。如果正式项目已经往前走了,可以用git pull命令获得新的提交:\ngit pull upstream master 由于开发者应该都在专门的功能分支上工作,pull操作结果会都是快进合并。\n开发者发布自己的功能 一旦开发者准备好了分享新功能,需要做二件事。 首先,通过push他的贡献代码到自己的公开仓库中,让其它的开发者都可以访问到。 他的origin远程别名应该已经有了,所以要做的就是:\ngit push origin feature-branch 这里和之前的工作流的差异是,origin远程别名指向开发者自己的服务端仓库,而不是正式仓库。\n第二件事,开发者要通知项目维护者,想要合并他的新功能到正式库中。 Bitbucket和Stash提供了Pull Request按钮,弹出表单让你指定哪个分支要合并到正式仓库。 一般你会想集成你的功能分支到上游远程仓库的master分支中。\n项目维护者集成开发者的功能 当项目维护者收到pull request,他要做的是决定是否集成它到正式代码库中。有二种方式来做:\n 直接在pull request中查看代码 pull代码到他自己的本地仓库,再手动合并 第一种做法更简单,维护者可以在GUI中查看变更的差异,做评注和执行合并。 但如果出现了合并冲突,需要第二种做法来解决。这种情况下,维护者需要从开发者的服务端仓库中fetch功能分支, 合并到他本地的master分支,解决冲突:\ngit fetch https://bitbucket.org/user/repo feature-branch # 查看变更 git checkout master git merge FETCH_HEAD 变更集成到本地的master分支后,维护者要push变更到服务器上的正式仓库,这样其它的开发者都能访问到:\ngit push origin master 注意,维护者的origin是指向他自己公开仓库的,即是项目的正式代码库。到此,开发者的贡献完全集成到了项目中。\n开发者和正式仓库做同步 由于正式代码库往前走了,其它的开发需要和正式仓库做同步:\ngit pull upstream master 如果你之前是使用SVN,Forking工作流可能看起来像是一个激进的范式切换(paradigm shift)。 但不要害怕,这个工作流实际上就是在功能分支工作流之上引入另一个抽象层。 不是直接通过单个中央仓库来分享分支,而是把贡献代码发布到开发者自己的服务端仓库中。\n示例中解释了,一个贡献如何从一个开发者流到正式的master分支中,但同样的方法可以把贡献集成到任一个仓库中。 比如,如果团队的几个人协作实现一个功能,可以在开发之间用相同的方法分享变更,完全不涉及正式仓库。\n这使得Forking工作流对于松散组织的团队来说是个非常强大的工具。任一开发者可以方便地和另一开发者分享变更,任何分支都能有效地合并到正式代码库中。\n 2.5 Pull Requests Pull requests是Bitbucket提供的让开发者更方便地进行协作的功能,提供了友好的Web界面可以在提议的修改合并到正式项目之前对修改进行讨论。\n开发者向团队成员通知功能开发已经完成,Pull Requests是最简单的用法。 开发者完成功能开发后,通过Bitbucket账号发起一个Pull Request。 这样让涉及这个功能的所有人知道要去做Code Review和合并到master分支。\n但是,Pull Request远不止一个简单的通知,而是为讨论提交的功能的一个专门论坛。 如果变更有任何问题,团队成员反馈在Pull Request中,甚至push新的提交微调功能。 所有的这些活动都直接跟踪在Pull Request中。\n相比其它的协作模型,这种分享提交的形式有助于打造一个更流畅的工作流。 SVN和Git都能通过一个简单的脚本收到通知邮件;但是,讨论变更时,开发者通常只能去回复邮件。 这样做会变得杂乱,尤其还要涉及后面的几个提交时。 Pull Requests把所有相关功能整合到一个和Bitbucket仓库界面集成的用户友好Web界面中。\n2.5.1 解析Pull Request 当要发起一个Pull Request,你所要做的就是请求(Request)另一个开发者(比如项目的维护者) 来pull你仓库中一个分支到他的仓库中。这意味着你要提供4个信息以发起Pull Request: 源仓库、源分支、目的仓库、目的分支。\n这几值多数Bitbucket都会设置上合适的缺省值。但取决你用的协作工作流,你的团队可能会要指定不同的值。 上图显示了一个Pull Request请求合并一个功能分支到正式的master分支上,但可以有多种不同的Pull Request用法。\n2.5.2 工作方式 Pull Request可以和功能分支工作流、Gitflow工作流或Forking工作流一起使用。 但一个Pull Request要求要么分支不同要么仓库不同,所以不能用于集中式工作流。 在不同的工作流中使用Pull Request会有一些不同,但基本的过程是这样的:\n 开发者在本地仓库中新建一个专门的分支开发功能。 开发者push分支修改到公开的Bitbucket仓库中。 开发者通过Bitbucket发起一个Pull Request。 团队的其它成员review code,讨论并修改。 项目维护者合并功能到官方仓库中并关闭Pull Request。 本文后面内容说明,Pull Request在不同协作工作流中如何应用。\n2.5.3 在功能分支工作流中使用Pull Request 功能分支工作流用一个共享的Bitbucket仓库来管理协作,开发者在专门的分支上开发功能。 但不是立即合并到master分支上,而是在合并到主代码库之前开发者应该开一个Pull Request发起功能的讨论。\n功能分支工作流只有一个公开的仓库,所以Pull Request的目的仓库和源仓库总是同一个。 通常开发者会指定他的功能分支作为源分支,master分支作为目的分支。\n收到Pull Request后,项目维护者要决定如何做。如果功能没问题,就简单地合并到master分支,关闭Pull Request。 但如果提交的变更有问题,他可以在Pull Request中反馈。之后新加的提交也会评论之后接着显示出来。\n在功能还没有完全开发完的时候,也可能发起一个Pull Request。 比如开发者在实现某个需求时碰到了麻烦,他可以发一个包含正在进行中工作的Pull Request。 其它的开发者可以在Pull Request提供建议,或者甚至直接添加提交来解决问题。\n2.5.4 在Gitflow工作流中使用Pull Request Gitflow工作流和功能分支工作流类似,但围绕项目发布定义一个严格的分支模型。 在Gitflow工作流中使用Pull Request让开发者在发布分支或是维护分支上工作时, 可以有个方便的地方对关于发布分支或是维护分支的问题进行交流。\nGitflow工作流中Pull Request的使用过程和上一节中完全一致: 当一个功能、发布或是热修复分支需要Review时,开发者简单发起一个Pull Request, 团队的其它成员会通过Bitbucket收到通知。\n新功能一般合并到develop分支,而发布和热修复则要同时合并到develop分支和master分支上。 Pull Request可能用做所有合并的正式管理。\n2.5.5 在Forking工作流中使用Pull Request 在Forking工作流中,开发者push完成的功能到他自己的仓库中,而不是共享仓库。 然后,他发起一个Pull Request,让项目维护者知道他的功能已经可以Review了。\n在这个工作流,Pull Request的通知功能非常有用, 因为项目维护者不可能知道其它开发者在他们自己的仓库添加了提交。\n由于各个开发有自己的公开仓库,Pull Request的源仓库和目标仓库不是同一个。 源仓库是开发者的公开仓库,源分支是包含了修改的分支。 如果开发者要合并修改到正式代码库中,那么目标仓库是正式仓库,目标分支是master分支。\nPull Request也可以用于正式项目之外的其它开发者之间的协作。 比如,如果一个开发者和一个团队成员一起开发一个功能,他们可以发起一个Pull Request, 用团队成员的Bitbucket仓库作为目标,而不是正式项目的仓库。 然后使用相同的功能分支作为源和目标分支。\n2个开发者之间可以在Pull Request中讨论和开发功能。 完成开发后,他们可以发起另一个Pull Request,请求合并功能到正式的master分支。 在Forking工作流中,这样的灵活性让Pull Request成为一个强有力的协作工具。\n2.5.6 示例 下面的示例演示了Pull Request如何在在Forking工作流中使用。 也同样适用于小团队的开发协作和第三方开发者向开源项目的贡献。\n在示例中,小红是个开发,小明是项目维护者。他们各自有一个公开的Bitbucket仓库,而小明的仓库包含了正式工程。\n小红fork正式项目 小红先要fork小明的Bitbucket仓库,开始项目的开发。她登陆Bitbucket,浏览到小明的仓库页面, 点Fork按钮。\n然后为fork出来的仓库填写名字和描述,这样小红就有了服务端的项目拷贝了。\n小红克隆她的Bitbucket仓库 下一步,小红克隆自己刚才fork出来的Bitbucket仓库,以在本机上准备出工作拷贝。命令如下:\ngit clone https://user@bitbucket.org/user/repo.git 请记住,git clone会自动创建origin远程别名,是指向小红fork出来的仓库。\n小红开发新功能 在开始改代码前,小红要为新功能先新建一个新分支。她会用这个分支作为Pull Request的源分支。\ngit checkout -b some-feature # 编辑代码 git commit -a -m \u0026quot;Add first draft of some feature\u0026quot; 在新功能分支上,小红按需要添加提交。甚至如果小红觉得功能分支上的提交历史太乱了,她可以用交互式rebase来删除或压制提交。 对于大型项目,整理功能分支的历史可以让项目维护者更容易看出在Pull Request中做了什么内容。\n小红push功能到她的Bitbucket仓库中 小红完成了功能后,push功能到她自己的Bitbucket仓库中(不是正式仓库),用下面简单的命令:\ngit push origin some-branch 这时她的变更可以让项目维护者看到了(或者任何想要看的协作者)。\n小红发起Pull Request Bitbucket上有了她的功能分支后,小红可以用她的Bitbucket账号浏览到她的fork出来的仓库页面, 点右上角的【Pull Request】按钮,发起一个Pull Request。 弹出的表单自动设置小红的仓库为源仓库,询问小红以指定源分支、目标仓库和目标分支。\n小红想要合并功能到正式仓库,所以源分支是她的功能分支,目标仓库是小明的公开仓库, 而目标分支是master分支。另外,小红需要提供Pull Request的标题和描述信息。 如果需要小明以外的人审核批准代码,她可以把这些人填在【Reviewers】文本框中。\n创建好了Pull Request,通知会通过Bitbucket系统消息或邮件(可选)发给小明。\n小明review Pull Request 在小明的Bitbucket仓库页面的【Pull Request】Tab可以看到所有人发起的Pull Request。 点击小红的Pull Request会显示出Pull Request的描述、功能的提交历史和每个变更的差异(diff)。\n如果小明想要合并到项目中,只要点一下【Merge】按钮,就可以同意Pull Request并合并到master分支。\n但如果像这个示例中一样小明发现了在小红的代码中的一个小Bug,要小红在合并前修复。 小明可以在整个Pull Request上加上评注,或是选择历史中的某个提交加上评注。\n小红补加提交 如果小红对反馈有任何疑问,可以在Pull Request中响应,把Pull Request当作是她功能讨论的论坛。\n小红在她的功能分支新加提交以解决代码问题,并push到她的Bitbucket仓库中,就像前一轮中的做法一样。 这些提交会进入的Pull Request,小明在原来的评注旁边可以再次review变更。\n小明接受Pull Request 最终,小明接受变更,合并功能分支到Master分支,并关闭Pull Request。 至此,功能集成到项目中,其它的项目开发者可以用标准的git pull命令pull这些变更到自己的本地仓库中。\n到了这里,你应该有了所有需要的工具来集成Pull Request到你自己的工作流。 请记住,Pull Request并不是为了替代任何 基于Git的协作工作流, 而是它们的一个便利的补充,让团队成员间的协作更轻松方便。\n 三、企业日常开发模式探索 在看这部分前,请先回顾阅读业界认可的成功的 Git Branch Work Flow 模型 A Successful Git Branching Model ,了解日常开发中的场景,有助于熟悉下面的使用过程。\n在企业开发中,使用 Git 作为版本控制软件最看重的还是结合公司自己搭建的 Gitlab,将 Code Review 加入打包部署持续集成的流程中,这样,代码开发完成,提交测试前,便可以对开发人员提交的代码进行 Review,发现潜在的问题,及时指导,对于新人来讲,也能更快更好的学习。\n解决的需求场景如下:\n 能支持日常迭代开发、紧急线上bug修复、多功能并行开发 大概50人左右的团队,平日迭代项目较多,且周期短(1~2周一个迭代) 能够通过tag重建整个系统 支持code review 所有上线的代码必须都是经过测试保证,且能自动同步到下一次的迭代中 能和公司的项目管理/持续集成系统整合 上图就是 xirong 团队在日常开发中总结出来的适合企业开发的模式,下面进行简单的介绍,方便大家学习了解,欢迎提交 Issue 进行讨论。(本模式适合敏捷开发流程,小迭代上线,传统的瀑布开发模型并没有进行测试)\n 迭代需求会、冲刺会后确定本次迭代的目标后,将迭代内容视为一个项目,在 Gitlab 上创建一个 Repository,初始化工程代码结构,根据上线日期,比如20150730上线,开出分支 release20150730、dev20150730 两个分支,dev 分支作为日常开发主干分支,release 分支作为提测打包、Code Review 的分支。 迭代开始,日常开发进行中,开发人员在 dev 分支上进行 Commit、Push 代码,并且解决掉日常协同开发中的冲突等问题,等到达到提测条件的时候,提测者,首先 Merge Master 分支上的最新代码 git merge --no-ff origin/master ,使得 Master 分支上的变更更新到迭代开发分支dev上面,之后,在 Gitlab 上面发起 pull request 请求,并指定 Code Review 人,请求的分支选择本次上线的 release 分支,即 release20150730。 被指定 Code Review 的人,对发起者的代码 Review 后,决定是否可以提交测试,若有问题,评论注释代码后,提交者对代码进行进行修改,重复步骤2,直到代码 Review 者认为 Ok。之后便可以借助自己公司的打包部署,对这些代码发布到测试环境验证。 步骤2-3重复多次后,就会达到一个稳定可发布的版本,即上线版本,上线后,将 release 版本上面最后的提交(图中0.2.4上线对应处)合并到 Master 分支上面,并打 Tag0.3。至此,一次完整的迭代开发完成。 若此次上线后,不久发现生产环境有 Bug 需要修复,则从 Tag 处新开分支 release_bugfix_20150731、dev_bugfix_20150731 ,开发人员从 dev_bugfix_20150731分支上进行开发,提测code review在 release_bugfix_20150731 分支上,具体步骤参考2-3,测试环境验证通过后,发布到线上,验证OK,合并到 Master 分支,并打 Tag0.2.3,此次 Bug 修复完毕,专为解 Bug 而生的这两个分支可以退伍了,删除release_bugfix_20150731、dev_bugfix_20150731两分支即可。(所有的历史 Commit 信息均已经提交到了 Master 分支上,不用担心丢失) 这样经过上面的1-5步骤,企业日常迭代开发中的代码版本控制基本上就 Ok 了,有问题欢迎 Issue 讨论。\n2016-11月 更新 Git 分支开发部署模型 的一些使用原则如下:\n master:master永远是线上代码,最稳定的分支,存放的是随时可供在生产环境中部署的代码,当开发活动告一段落,产生了一份新的可供部署的代码时,发布成功之后,代码才会由 aone2 提交到 master,master 分支上的代码会被更新。应用上 aone2 后禁掉所有人的 master的写权限 develop:保存当前最新开发成果的分支。通常这个分支上的代码也是可进行每日夜间发布的代码,只对开发负责人开放develop权限。 feature: 功能特性分支,每个功能特性一个 feature/ 分支,开发完成自测通过后合并入 develop 分支。可以从 master 或者develop 中拉出来。 hotfix: 紧急bug分支修复分支。修复上线后,可以直接合并入master。 Git-Develop 分支模式是基于 Git 代码库设计的一种需要严格控制发布质量和发布节奏的开发模式。develop 作为固定的持续集成和发布分支,并且分支上的代码必须经过 CodeReview 后才可以提交到 Develop 分支。它的基本流程如下:\n 每一个需求/变更都单独从Master上创建一条Branch分支; 用户在这个Branch分支上进行Codeing活动; 代码达到发布准入条件后aone上提交Codereview,Codereview通过后代码自动合并到Develop分支; 待所有计划发布的变更分支代码都合并到Develop后,系统再 rebase master 代码到Develop 分支,然后自行构建,打包,部署等动作。 应用发布成功后Aone会基于Develop分支的发布版本打一个“当前线上版本Tag”基线; 应用发布成功后Aone会自动把Develop分支的发布版本合并回master; 四、开发工作流的讨论 几篇业界的讨论文章\n Gitflow 有害论 作者对 Gitflow 流程的使用过程中的吐槽,文章留言引起了强烈的讨论,可以关注下。 GitHub Flow scottchacon 讲述在 GitHub 工作中日常流程以及对每一点进行详细的介绍。 谷歌的代码管理 谷歌和 Facebook 都只有一个代码仓库,全公司的代码都放在这个库里,这里是阮一峰老师写的文章。 Why Google Stores Billions of Lines of Code in a Single Repository ","id":7,"section":"posts","summary":"本文转自xirong,已获得原作者授权,如需转载,请与原作者联系 说明: 个人在学习Git工作流的过程中,从原有的 SVN 模式很难完全理解Git的协作","tags":["Git"],"title":"Git几种常见工作流","uri":"https://fhxisdog.github.io/2020/04/git_work_flow/","year":"2020"},{"content":"基本概念 四度空间: 工作区:Workspace\n 就是你在电脑里能看到的目录\n暂存区: Index/Stage/Cached\n英文叫stage, 或index。一般存放在 \u0026ldquo;.git目录下\u0026rdquo; 下的index文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)\n版本库:Repository\n工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。\n远程仓库: Remote Repository\n远程仓库则被设置在一个能被所有团队成员访问到的远程服务器上,基本上都是在互联网上或者是本地局域网中\n五种状态: 未修改(Unmodify):\n记录在工作区,没有被修改的文件\n未追踪(Untracked):\n新增的文件的状态,未受Git管理,记录在工作区\n已修改(Modified):\n受Git管理过的文件的改动状态(包括改动内容、删除文件),记录在工作区\n已暂存(Staged):\n将记录在工作区的文件变动状态通知了Git,记录在暂存区\n已提交(Commited):\n已提交到本地仓库\n已推送(pushed):\n已推送到远程仓库\n一张图概括: 新建一个仓库 git init #在当前目录新建一个git仓库 git clone #clone一个远程的仓库到当前目录 当你创建一个带有.git文件夹的目录后,这个目录中的所有操作都将会被git记录,这就故事开始的地方,让我们开启这个游戏!\n加入暂存区 这个目录就是被git记录的工作区,在这个目录下的所有增删改都会被git纳入到版本控制.\n可以使用git status查看当前目录的哪些文件的变化:\ngit status #查看当前工作区文件状态 git status -s #简短形式输出当前工作状态 修改完后使用git add 加入到缓存区.\ngit add . #将当前目录所有文件做修改都加入缓存区 git add \u0026lt;file name\u0026gt; #将某个文件的修改就如缓存区 如果有操作想被git忽略有3钟种方式:\n .gitignore文件 .gitignore只会忽略尚未加入版本控制的文件,如果文件已被加入版本控制,写入.gitignore文件git仍会记录文件的变化.如果已经纳入版本控制的文件想忽略,例如:在远程服务器中的配置文件,想本地修改,又不想被git记录可以使用下面两种命令\n git update-index --assume-unchanged git update-index --assume-unchanged \u0026lt;file name\u0026gt; # 这个会关闭文件与远程仓库的跟踪,认为这个文件远程仓库是不会修改,所以每次pull都是本地的文件 反向操作\ngit update-index --no-assume-unchanged \u0026lt;file name\u0026gt; git update-index --skip-worktree git update-index --skip-worktree \u0026lt;file name\u0026gt; # 这个不会关闭文件与远程仓库的跟踪,只是告诉Git不要跟踪对本地文件/文件夹的更改,每次pull时会拉取最新的变化会提示冲突,但因为没有跟踪本地更改,所以需要no-skip-worktree再合并最新的变化 或者使用git stash将本地的更改保存起来,再git pull,然后再git stash pop,再合并冲突,比较麻烦 反向操作:\ngit update-index --no-skip-worktree \u0026lt;file name\u0026gt; 提交 改写提交 如果提交完发现自己漏提了文件,或者提交信息写错了可以使用--amend来改写提交\ngit commit --amend #如果有漏提的文件,add到缓存区即可,然后git会打开默认的编辑器让你重新编写提交信息 复制提交 cherry-pick可以将其它提交复制到当前再提交一遍\n执行:\ngit cherry-pick c2 c4 恢复 文件已被修改尚未加入到缓存区 git restore \u0026lt;file name\u0026gt; #将文件恢复到和缓存区(index)一样的状态 文件被修改并提交到缓存区 git restore --staged \u0026lt;file name\u0026gt; #将文件从缓存区(index)移动出来 新增的文件删除(untracked状态) git clean -f \u0026lt;file name\u0026gt; #将untracked状态的文件删除 Git指针 指针是git的精髓,理解的指针的移动对于很多命令的理解和记忆更胜一筹.\nGit中指针大致有五类:\n HEAD\n指向当前正在操作的 commit。\n 分支指针\n指向当前分支所在的commit\n ORIG_HEAD\n当使用一些在 Git 看来比较危险的操作去移动 HEAD 指针的时候,ORIG_HEAD 就会被创建出来,记录危险操作之前的 HEAD,方便 HEAD 的恢复,有点像修改前的备份。\n FETCH_HEAD\n记录从远程仓库拉取的记录。\n MERGE_HEAD\n当运行 git merge 时,MERGE_HEAD 记录你正在合并到你的分支中的提交。MERGE_HEAD在合并的时候会出现,合并结束,就删除了这个文件。\n CHERRY_PICK_HEAD\n记录您在运行 git cherry-pick时要合并的提交。同上,这个文件只在 cherry-pick 期间存在。\n 这里我们主要理解HEAD指针和分支指针.\n正常情况下HEAD指针是指向当前分支指针,如图:\ngit checkout \u0026lt;commit id\u0026gt; git checkout 本质就是移动HEAD指针 (个人理解,待验证)\n当执行git checkout \u0026lt;commit id\u0026gt; 或 git checkout HEAD^时,进入指针分离模式,HEAD指针指向一条commit id,不再指向分支指针,此时如果提交代码,只有HEAD指针会指向新提交的commit id,而分支指针不会移动.\n此时如果想让HEAD重新指向分支指针,执行git checkout \u0026lt;branch name\u0026gt;即可.\ngit reset reset 的本质:移动 HEAD 以及它所指向的 branch\n实质上,reset 这个指令虽然可以用来撤销 commit ,但它的实质行为并不是撤销,而是移动 HEAD ,并且「捎带」上 HEAD 所指向的 branch(如果有的话)。也就是说,reset 这个指令的行为其实和它的字面意思 \u0026ldquo;reset\u0026rdquo;(重置)十分相符:它是用来重置 HEAD 以及它所指向的 branch 的位置的。\n而 reset --hard HEAD^ 之所以起到了撤销 commit 的效果,是因为它把 HEAD 和它所指向的 branch 一起移动到了当前 commit 的父 commit 上,从而起到了「撤销」的效果:\n git reset 各个选项解释:\n \u0026ndash;soft – 缓存区和工作目录都不会被改变 \u0026ndash;mixed – 默认选项。缓存区和你指定的提交同步,但工作目录不受影响 \u0026ndash;hard – 缓存区和工作目录都同步到你指定的提交 把这些标记想成定义 git reset 操作的作用域就容易理解多了。\n git branch -f \u0026lt;branch name\u0026gt; 这条命令是将分支指针移动到指定位置.运行git branch -f master c1:\n匿名分支与头指针分离处理 匿名分支 当git进入头指针分离状态,进行提交会产生一个匿名分支,如果一直匿名分支,就会被git清理掉,\n可以使用`git branch ``起个名字,来避免这种情况.\n 强制回退丢失 commit id 如果意外使用如:git reset --hard HEAD^时,可能会使commit消失,这时候可以使用git reflog来找回命令\n分支 几乎所有的版本控制系统都以某种形式支持分支。 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。 在很多版本控制系统中,这是一个略微低效的过程——常常需要完全创建一个源代码目录的副本。对于大项目来说,这样的过程会耗费很多时间。\n有人把 Git 的分支模型称为它的“必杀技特性”,也正因为这一特性,使得 Git 从众多版本控制系统中脱颖而出。 为何 Git 的分支模型如此出众呢? Git 处理分支的方式可谓是难以置信的轻量,创建新分支这一操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷。 与许多其它版本控制系统不同,Git 鼓励在工作流程中频繁地使用分支与合并,哪怕一天之内进行许多次。 理解和精通这一特性,你便会意识到 Git 是如此的强大而又独特,并且从此真正改变你的开发方式。\n 命令 git branch #列出仓库中所有分支。 git branch \u0026lt;branch\u0026gt; #创建一个名为 \u0026lt;branch\u0026gt; 的分支。不会 自动切换到那个分支去。 git branch -d \u0026lt;branch\u0026gt; #删除指定分支。这是一个安全的操作,Git 会阻止你删除包含未合并更改的分支。 git branch -D \u0026lt;branch\u0026gt; #强制删除指定分支,即使包含未合并更改。如果你希望永远删除某条开发线的所有提交,你应该用这个命令。 git branch -m \u0026lt;branch\u0026gt; #将当前分支命名为 \u0026lt;branch\u0026gt;。 git checkout \u0026lt;existing-branch\u0026gt; #查看特定分支,分支应该已经通过 git branch 创建。这使得 \u0026lt;existing-branch\u0026gt; 成为当前的分支,并更新工作目录的版本。 git checkout -b \u0026lt;new-branch\u0026gt; #创建并查看 \u0026lt;new-branch\u0026gt;,-b 选项是一个方便的标记,告诉Git在运行 git checkout \u0026lt;new-branch\u0026gt; 之前运行 git branch \u0026lt;new-branch\u0026gt;。 git checkout -b \u0026lt;new-branch\u0026gt; \u0026lt;existing-branch\u0026gt; #和上一条相同,但将 \u0026lt;existing-branch\u0026gt; 作为新分支的基,而不是当前分支。 分支合并的两种姿势 git merge merge的三种特殊情况 冲突\nmerge 在做合并的时候,是有一定的自动合并能力的:如果一个分支改了 A 文件,另一个分支改了 B 文件,那么合并后就是既改 A 也改 B,这个动作会自动完成;如果两个分支都改了同一个文件,但一个改的是第 1 行,另一个改的是第 2 行,那么合并后就是第 1 行和第 2 行都改,也是自动完成。\n 但,如果两个分支修改了同一部分内容,merge 的自动算法就搞不定了。这种情况 Git 称之为:冲突(Conflict)。\n解决方法:\n 解决掉冲突 手动 commit 一下 或者放弃合并:\ngit merge --abort HEAD 领先于目标 commit\n如果 merge 时的目标 commit 和 HEAD 处的 commit 并不存在分叉,而是 HEAD 领先于目标 commit:\n 那么 merge 就没必要再创建一个新的 commit 来进行合并操作,因为并没有什么需要合并的。在这种情况下, Git 什么也不会做,merge 是一个空操作。\n HEAD 落后于 目标 commit——fast-forward\n而另一种情况:如果 HEAD 和目标 commit 依然是不存在分叉,但 HEAD 不是领先于目标 commit,而是落后于目标 commit.\n那么 Git 会直接把 HEAD(以及它所指向的 branch,如果有的话)移动到目标 commit\n git rebase rebase 的意思是,给你的 commit 序列重新设置基础点(也就是父 commit)。展开来说就是,把你指定的 commit 以及它所在的 commit 串,以指定的目标 commit 为基础,依次重新提交一次。例如下面这个 merge:\ngit merge branch1 如果把 merge 换成 rebase,可以这样操作:\ngit checkout branch1 git rebase master 另外,在 rebase 之后,记得切回 master 再 merge 一下,把 master 移到最新的 commit:\ngit checkout master git merge branch1 为什么要从 branch1 来 rebase,然后再切回 master 再 merge 一下这么麻烦,而不是直接在 master 上执行 rebase?\n从图中可以看出,rebase 后的 commit 虽然内容和 rebase 之前相同,但它们已经是不同的 commits 了。如果直接从 master 执行 rebase 的话,就会是下面这样:\n这就导致 master 上之前的两个最新 commit 被剔除了。如果这两个 commit 之前已经在中央仓库存在,这就会导致没法 push 了:\n 它会把整个 branch1分支移动到 master 分支的后面,有效地把所有 master 分支上新的提交并入过来。但是,rebase 为原分支上每一个提交创建一个新的提交,重写了项目历史,并且不会带来合并提交。\nrebase最大的好处是你的项目历史会非常整洁。首先,它不像 git merge 那样引入不必要的合并提交。其次,如上图所示,rebase 导致最后的项目历史呈现出完美的线性——你可以从项目终点到起点浏览而不需要任何的 fork。这让你更容易使用 git log、git bisect 和 gitk 来查看项目历史。\n不过,这种简单的提交历史会带来两个后果:安全性和可跟踪性。如果你违反了 rebase 黄金法则,重写项目历史可能会给你的协作工作流带来灾难性的影响。此外,rebase 不会有合并提交中附带的信息——你看不到 branch分支中并入了上游的哪些更改。\n交互式rebase 交互式的 rebase 允许你更改并入新分支的提交。这比自动的 rebase 更加强大,因为它提供了对分支上提交历史完整的控制。一般来说,这被用于将 feature 分支并入 master 分支之前,清理混乱的历史。\n把 -i 传入 git rebase 选项来开始一个交互式的rebase过程:\ngit checkout feature git rebase -i master 它会打开一个文本编辑器,显示所有将被移动的提交:\npick 33d5b7a Message for commit #1 pick 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3 这个列表定义了 rebase 将被执行后分支会是什么样的。更改 pick 命令或者重新排序,这个分支的历史就能如你所愿了。比如说,如果第二个提交修复了第一个提交中的小问题,你可以用 fixup 命令把它们合到一个提交中:\npick 33d5b7a Message for commit #1 fixup 9480b3d Message for commit #2 pick 5c67e61 Message for commit #3 忽略不重要的提交会让你的 feature 分支的历史更清晰易读。这是 git merge 做不到的。\nRebase的黄金法则 当你理解 rebase 是什么的时候,最重要的就是什么时候 不能 用 rebase。git rebase 的黄金法则便是,绝不要在公共的分支上使用它。\n这次 rebase 将 master 分支上的所有提交都移到了 feature 分支后面。问题是它只发生在你的代码仓库中,其他所有的开发者还在原来的 master 上工作。因为 rebase 引起了新的提交,Git 会认为你的 master 分支和其他人的 master 已经分叉了。\n同步两个 master 分支的唯一办法是把它们 merge 到一起,导致一个额外的合并提交和两堆包含同样更改的提交。不用说,这会让人非常困惑。\n所以,在你运行 git rebase 之前,一定要问问你自己「有没有别人正在这个分支上工作?」。如果答案是肯定的,那么把你的爪子放回去,重新找到一个无害的方式(如 git revert)来提交你的更改。不然的话,你可以随心所欲地重写历史。\n强制推送 如果你想把 rebase 之后的 master 分支推送到远程仓库,Git 会阻止你这么做,因为两个分支包含冲突。但你可以传入 --force 标记来强行推送。就像下面一样:\n# 小心使用这个命令! git push --force 它会重写远程的 master 分支来匹配你仓库中 rebase 之后的 master 分支,对于团队中其他成员来说这看上去很诡异。所以,务必小心这个命令,只有当你知道你在做什么的时候再使用。\n仅有的几个强制推送的使用场景之一是,当你在想向远程仓库推送了一个私有分支之后,执行了一个本地的清理(比如说为了回滚)。这就像是在说「哦,其实我并不想推送之前那个 feature 分支的。用我现在的版本替换掉吧。」同样,你要注意没有别人正在这个 feature 分支上工作。\n本地清理 在你工作流中使用 rebase 最好的用法之一就是清理本地正在开发的分支。隔一段时间执行一次交互式 rebase,你可以保证你 feature 分支中的每一个提交都是专注和有意义的。你在写代码时不用担心造成孤立的提交——因为你后面一定能修复。\n调用 git rebase 的时候,你有两个基(base)可以选择:上游分支(比如 master)或者你 feature 分支中早先的一个提交。我们在「交互式 rebase」一节看到了第一种的例子。后一种在当你只需要修改最新几次提交时也很有用。比如说,下面的命令对最新的 3 次提交进行了交互式 rebase:\ngit checkout feature git rebase -i HEAD~3 通过指定 HEAD~3 作为新的基提交,你实际上没有移动分支——你只是将之后的 3 次提交重写了。注意它不会把上游分支的更改并入到 feature 分支中。\n如果你想用这个方法重写整个 feature 分支,git merge-base 命令非常方便地找出 feature 分支开始分叉的基。下面这段命令返回基提交的 ID,你可以接下来将它传给 git rebase:\ngit merge-base feature master 交互式 rebase 是在你工作流中引入 git rebase 的的好办法,因为它只影响本地分支。其他开发者只能看到你已经完成的结果,那就是一个非常整洁、易于追踪的分支历史。\n但同样的,这只能用在私有分支上。如果你在同一个 feature 分支和其他开发者合作的话,这个分支是公开的,你不能重写这个历史。\n用带有交互式的 rebase 清理本地提交,这是无法用 git merge 命令代替的。\n保存当前工作状态 此时我在 feature_666 分支,非常聚精会神加持高专注地实现一个功能 666 模块,简直键盘如飞的编写代码~~~ 然后这时,客户反馈出一个 bug , 非常严重,必须立马解决,优先级为 0 !!! 于是,我需要去到 release 分支去 checkout 新的分支去工作了,但是 666 功能还没完成怎么办? 此时我面临着一个选择题:\nA:提交后切换,代码保存到分支 feature_666,却产生一个无意义的提交\nB:不提交直接切换,然而这个选项根本没人会选。(因为不提交没办法切换分支)\n是不是很难选,此时,别忘记还有 C 选项!\nC:使用 git stash , 将当前修改(未提交的代码)存入缓存区,切换分支修改 bug ,回来再通过 git stash pop 取出来。\n———————————————— 版权声明:本文为CSDN博主「DRPrincess」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_32452623/article/details/76100140\n 命令:\ngit stash #将修改储存到暂存区,工作区会删除修改 git stash show #查看刚才暂存的修改 git stash pop #取出修改并删除记录列表中对应记录 git stash list #查看暂存区的所有暂存修改记录 git stash save \u0026lt;message\u0026gt; #给stash 存储的修改起个名字并保存 git stash apply stash@{X} #取出相应的暂存 git stash drop stash@{X} #将记录列表中取出的对应暂存记录删除 标签 命令:\ngit tag \u0026lt;tag name\u0026gt; #打一个标签 git checkout \u0026lt;tag name\u0026gt; #检出一个标签 git tag -d \u0026lt;tag name\u0026gt; #删除一个标签 远程交互 添加远程仓库(git init方式) 初始化 git init 添加远程仓库 git remote add \u0026lt;repository name\u0026gt; \u0026lt;url\u0026gt; \ngit remote add origin xxx.git 拉取代码 git fetch 将当前分支关联远程分支 git branch -u origin/master 关联关系 git merge origin/master --allwo-unrelated-histories 合并当前与远程分支 git merge origin/dev 克隆方式(git clone) 克隆仓库 git clone url 检出分支 git checkout -b \u0026lt;local branch name\u0026gt; \u0026lt;remote branch name\u0026gt; #拉取本地分支与远程分支关联 有关远程分支的命令 git branch -a #查看远程分支 git branch -vv #查看本地分支与远程分支的关联 本地分支与远程分支名称问题 本地分支与远程对应的分支尽量同名,如果不同命会出现问题:\nfatal: The upstream branch of your current branch does not match the name of your current branch. To push to the upstream branch on the remote, use git push origin HEAD:dev To push to the branch of the same name on the remote, use git push origin HEAD To choose either option permanently, see push.default in \u0026#39;git help config\u0026#39;. 可以使用命令提交:\ngit push origin dev2:dev 或者更改git 配置:\ngit config --global push.default upstream 操纵远程分支 将本地分支推送到远程 git checkout -b feature-branch #创建并切换到分支feature-branch git push origin feature-branch:feature-branch #推送本地的feature-branch(冒号前面的)分支到远程origin的feature-branch(冒号后面的)分支(没有会自动创建) 删除远程分支 git push origin --delete \u0026lt;branch name\u0026gt; # 或者 git branch -r -d origin/\u0026lt;branch name\u0026gt; git push origin :\u0026lt;branch name\u0026gt; ","id":8,"section":"posts","summary":"基本概念 四度空间: 工作区:Workspace 就是你在电脑里能看到的目录 暂存区: Index/Stage/Cached 英文叫stage, 或index。一般存放在 \u0026ldquo;.g","tags":["Git"],"title":"Git不完全使用指南","uri":"https://fhxisdog.github.io/2020/03/git_guide/","year":"2020"},{"content":"什么是Restful? REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。\nFielding是一个非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席。所以,他的这篇论文一经发表,就引起了关注,并且立即对互联网开发产生了深远的影响。\n他这样介绍论文的写作目的:\n \u0026ldquo;本文研究计算机科学两大前沿\u0026mdash;-软件和网络\u0026mdash;-的交叉点。长期以来,软件研究主要关注软件设计的分类、设计方法的演化,很少客观地评估不同的设计选择对系统行为的影响。而相反地,网络研究主要关注系统之间通信行为的细节、如何改进特定通信机制的表现,常常忽视了一个事实,那就是改变应用程序的互动风格比改变互动协议,对整体表现有更大的影响。我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。\u0026rdquo;\n(This dissertation explores a junction on the frontiers of two research disciplines in computer science: software and networking. Software research has long been concerned with the categorization of software designs and the development of design methodologies, but has rarely been able to objectively evaluate the impact of various design choices on system behavior. Networking research, in contrast, is focused on the details of generic communication behavior between systems and improving the performance of particular communication techniques, often ignoring the fact that changing the interaction style of an application can have more impact on performance than the communication protocols used for that interaction. My work is motivated by the desire to understand and evaluate the architectural design of network-based application software through principled use of architectural constraints, thereby obtaining the functional, performance, and social properties desired of an architecture. )\n Fielding将他对互联网软件的架构原则,定名为REST,即Representational State Transfer的缩写。(\u0026ldquo;表现层状态转化\u0026rdquo;)。\n如果一个架构符合REST原则,就称它为RESTful架构。\n要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义。 如果你把这个名称搞懂了,也就不难体会REST是一种什么样的设计。\n总的来说,Restful架构就是:\n (1)每一个URI代表一种资源;\n (2)客户端和服务器之间,传递这种资源的某种表现层;\n (3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现\u0026quot;表现层状态转化\u0026quot;。\n什么是Rsocket? RSocket is a binary protocol for use on byte stream transports such as TCP, WebSockets, and Aeron.\n\u0026ldquo;RSocket是一种二进制协议,用于TCP、websocket和Aeron等字节流传输。\u0026rdquo;\nIt enables the following symmetric interaction models via async message passing over a single connection:\n\u0026ldquo;它支持以下对称交互模型通过异步消息传递一个单一的连接:\u0026rdquo;\n request/response (stream of 1) request/stream (finite stream of many) fire-and-forget (no response) channel (bi-directional streams) It supports session resumption, to allow resuming long-lived streams across different transport connections. This is particularly useful for mobile⬄server communication when network connections drop, switch, and reconnect frequently.\n\u0026ldquo;它支持会话恢复,允许在不同的传输连接之间恢复长生命周期的流。当网络连接频繁地断开、切换和重新连接时,这对于移动服务器通信尤其有用。\u0026rdquo;\n 模式 说明 请求-响应(request/response) 这是最典型也最常见的模式。发送方在发送消息给接收方之后,等待与之对应的响应消息。 请求-响应流(request/stream) 发送方的每个请求消息,都对应于接收方的一个消息流作为响应。 发后不管(fire-and-forget) forget) 发送方的请求消息没有与之对应的响应。 通道模式(channel) 在发送方和接收方之间建立一个双向传输的通道。 什么是gRpc? gRPC是一个高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协议开发,且支持众多开发语言。\ngRPC提供了一种简单的方法来精确地定义服务和为iOS、Android和后台支持服务自动生成可靠性很强的客户端功能库。\n客户端充分利用高级流和链接功能,从而有助于节省带宽、降低的TCP链接次数、节省CPU使用、和电池寿命。\ngRPC具有以下重要特征:\n强大的IDL特性 RPC使用ProtoBuf来定义服务,ProtoBuf是由Google开发的一种数据序列化协议,性能出众,得到了广泛的应用。\n支持多种语言 支持C++、Java、Go、Python、Ruby、C#、Node.js、Android Java、Objective-C、PHP等编程语言。 3.基于HTTP/2标准设计\n谷歌机翻时间 Representational State Transfer (REST) has become the de facto standard for communicating between microservices. That is not a good thing — in fact, it’s a very bad thing. How did this come to pass? Well, at the time REST emerged, there were even worse options. When Roy Fielding proposed REST in 2000, REST was a kale sandwich in a field of much worse tasting sandwiches.\n\u0026ldquo;代表性状态转移(REST)已成为微服务之间进行通信的事实上的标准。 那不是一件好事-实际上,这是一件非常不好的事。 这是怎么发生的? 好吧,在REST出现时,还有更糟糕的选择。 当Roy Fielding于2000年提出REST时,REST是羽衣甘蓝三明治,在口味更差的三明治领域。\u0026rdquo;\nPeople were using SOAP, RMI, CORBA, and EJBs. JSON was a welcome respite from XML. It was easy to use URLs to spit out some text. Plus, JavaScript started to really take off in browsers and it was much easier to deal with REST, than it was SOAP. Unlike the recent microservice trend, most applications were the traditional monolithic 3-tier application. The source of most of the external traffic they were talking with was a browser, so when they had to produce something REST was an easy choice. Many people began to move from bigger commercial offerings like WebSphere to Jetty and Tomcat. These didn’t even have the facilities to deal with EJBs, so REST was a convenient choice.\n\u0026ldquo;人们使用SOAP、RMI、CORBA和ejb。JSON是XML的一个很好的缓冲。使用url输出一些文本是很容易的。另外,JavaScript开始在浏览器中流行起来,它比SOAP更容易处理REST。与最近的微服务趋势不同,大多数应用程序是传统的单片3层应用程序。他们所谈论的大多数外部流量的来源是一个浏览器,所以当他们不得不产生一些东西时,REST是一个简单的选择。许多人开始从WebSphere这样的大型商业产品转向Jetty和Tomcat。它们甚至没有处理ejb的设施,因此REST是一个方便的选择。\u0026rdquo;\nWhat does this have to do with microservices? Early microservice pioneers moved to microservices for a different reason than people are doing it today. They moved to them because they had to deal with massive scale. They started to get so many users that they couldn’t serve everything in a single monolith. And unlike many enterprises today, cost wasn’t the motivating factor — time was. They needed to get their services out yesterday. As they got more and more users their monolith wasn’t cutting it, so they cut their app up into smaller pieces. They could deploy these applications on thousands of servers, and then eventually virtual machines.\n\u0026ldquo;这与微服务有什么关系? 早期的微服务先驱之所以转向微服务,其原因不同于今天的人们。 他们之所以搬到他们这里,是因为他们不得不面对大规模的交易。 他们开始吸引大量用户,以至于无法在一个整体中提供所有服务。 而且与今天的许多企业不同,成本不是动力,而是时间。 他们需要昨天取消服务。 随着他们越来越多的用户,他们的独裁者并没有削减它,因此他们将他们的应用程序分成了较小的部分。 他们可以将这些应用程序部署在数千台服务器上,然后再部署在虚拟机上。\u0026rdquo;\nFurthermore, they could deploy their applications very quickly. Companies that adopted this model were able to survive. During this race though, there wasn’t much time to consider what they were doing. These early pioneers had to deal with exponential user growth and competition, so it makes sense they would opt for tactical solutions. One of these was using REST to communicate between services.\n\u0026ldquo;此外,他们可以非常快速地部署应用程序。 采用这种模式的公司能够生存。 不过在这场比赛中,没有太多时间去考虑他们在做什么。 这些早期的开拓者必须应对指数级的用户增长和竞争,因此他们选择战术解决方案是有道理的。 其中之一是使用REST在服务之间进行通信。\u0026rdquo;\n Why REST is bad for Microservices When programming an application, your programing language eventually ends up as machine code. This is obvious. Even an “interpreted” language like Java or JavaScript does, as well. Instead of compiling directly to machine code, they use a JIT or just-in-time compiler. In some cases, JIT’ed code can be faster than what an engineer can write and tune by hand — VMs are truly a miracle of modern computer science.\n\u0026ldquo;在编写应用程序时,您的编程语言最终会变成机器码。这是显而易见的。甚至像Java或JavaScript这样的“解释型”语言也可以。他们使用JIT或即时编译器,而不是直接编译成机器码。在某些情况下,JIT代码的速度比工程师手工编写和调优的速度还要快——vm确实是现代计算机科学的一个奇迹。\u0026rdquo;\nWhy then do we waste this miracle? Instead of sending binary messages optimized for machines, on a protocol optimized for services, we send messages optimized for humans. We send around things like JSON and XML using a protocol that was designed for sending books. Think how ridiculous this is! You have a program that is binary, that turns a binary structure to text, sends it over the network in text, to a machine that parses and turns it back into binary structure to be processed in an application.\n\u0026ldquo;那为什么我们要浪费这个奇迹呢? 我们不是根据针对服务优化的协议发送针对机器优化的二进制消息,而是发送针对人员优化的消息。 我们使用旨在发送书籍的协议来发送JSON和XML之类的东西。 认为这是多么荒谬! 您有一个二进制程序,该程序将二进制结构转换为文本,然后通过网络将其以文本形式发送到计算机,该计算机将其解析并转换回二进制结构,以在应用程序中进行处理。\u0026rdquo;\nAvoiding cache misses on a modern CPU is critical. Unfortunately, parsing tons of JSON and Strings is going to cause cache misses!\n\u0026ldquo;避免现代CPU上的高速缓存未命中至关重要。 不幸的是,解析大量的JSON和Strings将导致缓存未命中!\u0026rdquo;\nAn often-cited reason to use REST is that it’s easy to debug because its “human readable”. Not being easy to read is a tooling issue. JSON text is only human readable because there are tools that allow you to read it – otherwise it’s just bytes on a wire. Furthermore, half the time the data being sent around is either compressed or encrypted — both of which aren’t human readable. Besides, how much of this can a person “debug” by reading? If you have a service that averages a tiny 10 requests per second with a 1 kilobyte JSON that is the equivalent to 860 megabytes of data a day, or 250 copies of War and Peace every day. There is no one who can read that, so you’re just wasting money.\n经常使用REST的原因是它易于调试,因为它的“可读性”。 不容易阅读是一个工具问题。 JSON文本只有人类可以阅读,因为有一些工具可让您读取JSON文本;否则,它只是线路上的字节。 此外,一半左右的数据被压缩或加密-两者都不是人类可读的。 此外,一个人可以通过阅读“调试”多少? 如果您有一项服务,平均每秒钟平均有10个请求,并带有1 KB的JSON,则相当于每天860兆字节的数据,或每天250份《战争与和平》。 没有人可以阅读,所以您只是在浪费金钱.\nThen, there is the case where you need to send binary data around, or you want to use a binary format instead of JSON. To do that, you must Base64 encode the data. This means that you essentially serialize the data twice — again, not an efficient way to use modern hardware.\n然后,在某些情况下,您需要发送二进制数据,或者您想要使用二进制格式而不是JSON。 为此,您必须对数据进行Base64编码。 这意味着您实际上要对数据进行两次序列化,这又不是使用现代硬件的有效方法。\nAt the end of the day, REST was implemented as a hack on top of HTTP. And HTTP is being used as a hack to send transport data between services. HTTP was designed to schlep books around the Internet. It shouldn’t be used for services to communicate with one other. Instead, use a format that is optimized for your application — the thing that is processing all the data.\n归根结底,REST被实现为基于HTTP的黑客。 HTTP被用作在服务之间发送传输数据的黑客。 HTTP的设计目的是使Internet上的书籍不被窃取。 不应将其用于服务之间的通信。 相反,请使用针对您的应用程序优化的格式-处理所有数据的事物。\n What is good Microservice Communication? If we suppose for a moment that REST isn’t the best choice for service to service communication, then what is? Let’s look at some of the things we would want in a protocol designed for microservice communication.\n如果我们假设REST不是服务到服务通信的最佳选择,那么什么才是呢?让我们来看看为微服务通信设计的协议中需要的一些东西。\nFor starters, we want things to be bi-directional. That’s a huge problem with REST — clients can only call servers. When both sides have equal ability to call each other, you can create interactions between applications in a natural manner. Otherwise you are forced to devise clunky workarounds such as long-polling to simulate server-initiated calls. You can partially get around it with HTTP/2, but the call still needs to be initiated by the client. What you want is the ability for clients and servers to be free to call each other as necessary.\n首先,我们希望事情是双向的。这是REST的一个大问题——客户端只能调用服务器。当双方具有同等的调用能力时,您可以以自然的方式在应用程序之间创建交互。否则,您将被迫设计笨拙的工作区,例如长轮询,以模拟服务器发起的调用。您可以使用HTTP/2部分地绕过它,但是调用仍然需要由客户机发起。您想要的是客户机和服务器能够在必要时自由地相互调用。\nAnother requirement is the connection between services must support multiple requests on same connection – at the same time. This is called multiplexing. Now, with a single connection, there needs to be some way to distinguish one request from another. This is unlike HTTP where one request starts when another one ends. With multiplexing, you are going to need keep track of the different requests. A good way to do this is having each request represented with a binary frame. Each frame can hold the request, as well as metadata about the request. Then, it can be used to get the frame to the correct location.\n另一个要求是服务之间的连接必须同时支持同一连接上的多个请求。这叫做多路复用。现在,对于单个连接,需要有一些方法来区分不同的请求。这与HTTP不同,一个请求在另一个请求结束时开始。使用多路复用,您将需要跟踪不同的请求。实现此目的的一种好方法是用二进制帧表示每个请求。每个帧可以保存请求,以及关于请求的元数据。然后,它可以用来获得帧的正确位置。\nWhen sending data over a single connection, you need the ability to fragment requests. A large request with a single connection will block all the other requests behind it, aka head-of-the-line blocking. What is needed, instead, is to fragment the requests into smaller sizes and send those over the network. Since data being sent is framed, it can be broken into smaller frame fragments, and then reassembled on the other side. This way, requests can interleave with each other. No longer can a large request block a smaller request. This will create a much more responsive system.\n当通过单个连接发送数据时,您需要能够分段请求。带有单个连接的大请求将阻塞它后面的所有其他请求,也称为前端阻塞。相反,需要的是将请求分割成更小的大小并通过网络发送。由于发送的数据是有框架的,所以可以将其分解成更小的框架片段,然后在另一端重新组装。这样,请求就可以相互交错。一个大的请求不再能阻塞一个小的请求。这将创建一个响应性更强的系统。\nAlso, the ability to exchange metadata about a connection is useful. Sometimes there is data to send that isn’t necessarily part of a business transaction — things like configuring the overall tracing level or exchanging information for dictionary-based compression. These are things that don’t have to do with business logic but could be controlled at a connection level. The ability to exchange metadata would provide for that.\n另外,交换关于连接的元数据的能力也很有用。有时需要发送的数据不一定是业务事务的一部分—比如配置整个跟踪级别或为基于字典的压缩交换信息。这些事情与业务逻辑无关,但是可以在连接级别进行控制。交换元数据的能力将为此提供支持。\nOften in application code, a function or method will be called that takes a list, returns a list, or both. This happens in microservices all the time, as well. REST doesn’t deal with these situations well and this leads to all sorts of hacks and complexity.\n通常在应用程序代码中,一个函数或方法将被调用,它接受一个列表,返回一个列表,或者两者兼而有之。这在微服务中也经常发生。REST不能很好地处理这些情况,这导致了各种各样的技巧和复杂性。\nWhat’s needed is a protocol that can deal with iterative data easily and naturally — like you do in your application. It doesn’t make sense to read an entire list of data, process it and then return a list of data once everything is processed. What you want is the ability to process data as it comes. You want to be able to stream data. If there is a long list of data, you don’t want to wait for that data to be processed — you want to send the data off as it becomes available and get the responses back as they occur.\n我们需要的是一种协议,它可以轻松而自然地处理迭代数据—就像您在应用程序中所做的那样。读取整个数据列表、处理它并在处理完所有内容后返回数据列表是没有意义的。你想要的是处理数据的能力。您希望能够流数据。如果有一个很长的数据列表,您不希望等待处理数据—您希望在数据可用时发送数据,并在响应出现时将其返回。\nThis will create a much more responsive system. It can be used for all sorts of things from reading bytes from a file and streaming it over the network, to returning results from a database query, to feeding browser click-stream data to a back-end. If first-class streaming support is present in the protocol, it’s not necessary to include another system like Spark to do stream processing. Nor is it necessary to include something like Kafka unless you want to store data.\n这将创建一个响应性更强的系统。它可以用于各种各样的事情,从从文件读取字节并通过网络传输,到从数据库查询返回结果,再到将浏览器点击流数据提供给后端。如果协议中提供了一流的流支持,则不需要包含其他系统(如Spark)来进行流处理。除非您想存储数据,否则也没有必要包含诸如Kafka之类的东西。\nFor data that is sent via streams, the next thing needed is application flow control. Byte-level flow control works for something like TCP because everything is the same size, and generally, the same cost to process from the perspective of the network card. However, in an application, not everything is the same cost. There could be a message that is 10 kilobytes that takes 10 milliseconds to process, but another message that is 10 bytes that takes 10 seconds.\n对于通过流发送的数据,接下来需要的是应用程序流控制。字节级流控制适用于TCP之类的东西,因为所有东西的大小都是相同的,而且从网卡的角度来看,处理的成本通常是相同的。然而,在应用程序中,并不是所有东西的成本都是相同的。可能有一条消息是10千字节,需要10毫秒来处理,但是另一条消息是10字节,需要10秒。\nAnother scenario found in microservices is that downstream services process data at slower rates than the data can be processed. This means that TCP buffers are never full. There needs to be some way to control the flow of traffic to keep from overwhelming downstream services in order to keep them responsive.\n微服务中的另一个场景是,下游服务处理数据的速度比处理数据的速度慢。这意味着TCP缓冲区永远不会满。需要有某种方式来控制流量,以避免下游服务无法承受,从而保持它们的响应能力。\nThe application must be able to control the rate that messages can flow independent of the underlying network bytes. For an application developer it is difficult to reason how many bytes a message is especially between languages. On the other hand, it is simple for a developer to reason about how many messages they are sending. This way, the service can arbitrage between the network flow control and the application flow control. Sometimes an application can process data faster than the network, and other times, the network can process data faster than the application. Having application flow control will ensure that tail latency is stable as well — again creating a responsive application. It also prevents the need for unbounded queues, a dangerous hack that is found in other applications.\n应用程序必须能够控制消息可以独立于底层网络字节流动的速率。对于应用程序开发人员来说,很难推断消息的字节数,尤其是在不同语言之间。另一方面,对于开发人员来说,推断他们发送了多少消息是很简单的。通过这种方式,服务可以在网络流控制和应用程序流控制之间进行仲裁。有时应用程序处理数据的速度比网络快,有时网络处理数据的速度比应用程序快。应用程序流控制将确保尾延迟也是稳定的—再次创建响应型应用程序。它还防止了对无界队列的需要,这是在其他应用程序中发现的一种危险的攻击。\nAs mentioned above, a huge drawback of RESTful web services is that they are (de facto) implemented as text-based. To send any binary data requires you Base64 encode the data —and serialize everything twice. What you really want is something that is binary — because it can represent anything — including text. Also, it is significantly more efficient for your application to process binary data than text, especially numbers. Additionally, they are naturally more compact — they don’t have extra braces, curly brackets, or angle brackets in them. Finally, if your data is binary, there is a possibility too for zero copy serialization and deserialization, depending on the format. This is a little out of the scope of this article, but check things out like Simple Binary Encoding (SBE), and Flatbuffers. They are significantly faster than using JSON.\n如上所述,rest式web服务的一个巨大缺点是它们(实际上)是作为基于文本的实现的。要发送任何二进制数据,需要使用Base64对数据进行编码,并对所有内容进行两次序列化。你真正想要的是二进制的东西——因为它可以表示任何东西——包括文本。而且,应用程序处理二进制数据的效率要比处理文本(尤其是数字)高得多。此外,它们自然更紧凑——它们没有额外的大括号、花括号或尖括号。最后,如果您的数据是二进制的,也有可能进行零拷贝序列化和反序列化,这取决于格式。这有点超出了本文的范围,但是可以查看简单二进制编码(SBE)和Flatbuffers。它们比使用JSON要快得多。\nFinally, you want to be able to send your requests over different transports. RESTful web services typically use HTTP, which uses only TCP. What you really want is a way to abstract the networking away, so that you only program to a specification and don’t have to worry about the transport. At the same time, if it’s talking to browsers your application should be able to run over WebSocket. You should not have to switch to a new networking toolkit every time you want to change where your application is deployed, it should be easy to swap out transports without any applications changes.\n最后,您希望能够通过不同的传输传输请求。RESTful web服务通常使用HTTP,而HTTP只使用TCP。您真正想要的是一种抽象网络的方法,这样您就可以只根据规范进行编程,而不必担心传输。同时,如果它与浏览器对话,你的应用程序应该能够在WebSocket上运行。您不应该每次想要更改应用程序的部署位置时都必须切换到新的网络工具包,应该很容易在不更改任何应用程序的情况下交换传输。\n Which Protocol Fits the Bill? Some would suggest that REST and HTTP/2 are a better fit. HTTP/2 is better than HTTP/1 but if you read the specs, its sole purpose is to create a better web browser protocol. It was never designed or intended for use in microservices. And that is what it should be used for — server HTML to web browsers. Again, it was never intended for microservices communication. Furthermore, you still must deal with URLs and matching the different HTTP methods to your application — these methods were never really intended for server to server communication.\n有些人认为REST和HTTP/2是更好的选择。HTTP/2比HTTP/1更好,但如果您阅读规范,它的唯一目的是创建更好的web浏览器协议。它从未被设计或打算用于微服务。这是它应该用于服务器HTML到web浏览器。同样,它也从未打算用于微服务通信。此外,您还必须处理url并将不同的HTTP方法与您的应用程序相匹配——这些方法从未真正用于服务器之间的通信。\nHTTP/2 does provide streaming, but it only provides it for server push. So, using REST over HTTP/2 requires initiating a request on a client and then pushing the data to the server. HTTP/2 flow control is byte-based flow control. This is good for a web browser, but not good for an application. There is still no way to control the flow of an application by the way that work is being done on an application.\nHTTP/2确实提供了流,但它只提供了服务器推送。因此,在HTTP/2上使用REST需要在客户机上启动一个请求,然后将数据推送到服务器。HTTP/2流控制是基于字节的流控制。这对于web浏览器很好,但是对于应用程序就不好了。仍然无法通过对应用程序执行工作的方式来控制应用程序流。\nThere has been a lot of noise lately about using gRPC. gRPC is very similar in concept to SOAP. Instead of using XML to define services, it uses Protobuf. Like SOAP, it’s a hodge-podge of URL and Header magic — this time using HTTP/2. This means gRPC is explicitly tied to HTTP/2, a protocol designed for web browsers. And what is worse, it isn’t supported in a web browser.\n最近有很多关于使用gRPC的噪音。gRPC在概念上与SOAP非常相似。它使用Protobuf而不是XML来定义服务。像SOAP一样,它是URL和报头魔力的大杂烩——这次使用的是HTTP/2。这意味着gRPC被明确地绑定到HTTP/2,一个为web浏览器设计的协议。更糟糕的是,它在web浏览器中不受支持。\nInstead you must use a proxy to turn your gRPC calls in to REST calls, thus defeating the purpose for using it. This highlights how poorly designed gRPC is. Why would you use HTTP/2 for a protocol and not make sure it works in a browser? You are forever limited by its original purpose, yet not able to use it where it was intended. This leads to my next point: the biggest limitation of REST is the fact it’s tied to HTTP.\n相反,您必须使用代理将gRPC调用转换为REST调用,从而破坏了使用它的目的。这凸显了gRPC的设计有多么糟糕。为什么使用HTTP/2作为协议,却不能确保它在浏览器中工作?你永远被它最初的用途所限制,却不能在它原本的地方使用它。这就引出了我的下一个观点:REST最大的限制是它与HTTP绑定。\nWhat you want is a protocol that is designed for service-to-service communication. Using a protocol that is specifically-designed for services to talk to each other will create significantly simpler and more reliable applications. There will not be any hacks, workarounds, or impedance mismatches.\n您需要的是为服务到服务通信设计的协议。使用专门为服务相互通信而设计的协议将创建更简单、更可靠的应用程序。不会有任何黑客,工作区,或阻抗不匹配。\nConstruction materials are a good analogy. Wood is great for building small bridges. You can use it to span a small stream or creek and it isn’t a problem.\n建筑材料是一个很好的类比。木材是建造小桥的好材料。你可以用它来跨越一条小溪,这不是问题。\nWhen engineers started using it to span wider distances things got complicated.\n当工程师们开始用它来跨越更宽的距离时,事情变得复杂起来\nWood bridges like this worked. But, they had a very high failure rate compared to modern bridges made of better materials. They were also very complicated and took much, much longer to build. This why we now use steel and concrete. They are easier to maintain, cheaper to build, last longer, and can span far greater distances.\n这样的木桥很管用。但是,与用更好的材料建造的现代桥梁相比,它们的失败率非常高。它们也很复杂,需要花很长时间来建造。这就是为什么我们现在使用钢铁和混凝土。它们更容易维护,建造成本更低,寿命更长,可以跨越更远的距离。\nWe need a modern material to replace HTTP for creating modern services. Open source RSocket is designed for services. It is a connection-oriented, message-driven protocol with built-in flow control at the application level. It works in a browser equally as well as on a server. In fact, a web browser can serve traffic to backend microservices. It is also binary. It works equally well with text and binary data, and the payloads can be fragmented. It models all the interactions that you do in your application as network primitives. This means you can stream data or do Pub/Sub without having to setup an application queue.\n我们需要一种现代材料来代替HTTP来创建现代服务。开源RSocket是为服务而设计的。它是一个面向连接的消息驱动协议,在应用程序级别具有内置的流控制。它在浏览器和服务器上都能工作。实际上,web浏览器可以为后端微服务提供流量。它也是二进制的。它同样适用于文本和二进制数据,并且有效负载可以被分割。它将应用程序中进行的所有交互建模为网络原语。这意味着您可以流数据或做发布/订阅,而不必设置应用程序队列。\nREST is a decent solution where it makes sense. One place it doesn’t make sense is microservices. Distributed systems are difficult enough on their own. The last thing that we need is to make them more complex by using something not designed for them.\n在有意义的地方,休息是一个不错的解决方案。微服务是一个没有意义的地方。分布式系统本身就够难的了。我们需要做的最后一件事就是使用一些不是为它们设计的东西来让它们变得更复杂。\n参考链接 理解RESTful架构\n使用 RSocket 进行反应式数据传输\nRsocket.io\ngRPC的那些事 - streaming\nGive REST a Rest with RSocket \n","id":9,"section":"posts","summary":"什么是Restful? REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。 Fielding是一个非常重要的人,他是H","tags":["java","spring boot","smicroservices"],"title":"Restful、gRpc、Rsocket","uri":"https://fhxisdog.github.io/2019/11/restful_grpc_rsocket/","year":"2019"},{"content":"问题描述 如果项目是多模块,如果只想打包某一模块,会出现找不到依赖的错误提示。\n项目结构如下:\nA: ----B ----C ----D 如果想只打包C模块,可以执行以下命令:\nmvn -pl -am C package -DskipTests -Ptest\n多模块工程的打包命令参考:\n-am --also-make 同时构建所列模块的依赖模块; -amd -also-make-dependents 同时构建依赖于所列模块的模块; -pl --projects \u0026lt;arg\u0026gt; 构建制定的模块,模块间用逗号分隔; -rf -resume-from \u0026lt;arg\u0026gt; 从指定的模块恢复反应堆。 看英文的更助于理解:\n-am,--also-make If project list is specified, also build projects required by the list -amd,--also-make-dependents If project list is specified, also build projects that depend on projects on the list -pl,--projects \u0026lt;arg\u0026gt; Comma-delimited list of specified reactor projects to build instead of all projects. A project can be specified by [groupId]:artifactId or by its relative path. -rf,--resume-from \u0026lt;arg\u0026gt; Resume reactor from specified project 参考: https://my.oschina.net/ccor/blog/704365\n @\n","id":10,"section":"posts","summary":"问题描述 如果项目是多模块,如果只想打包某一模块,会出现找不到依赖的错误提示。 项目结构如下: A: ----B ----C ----D 如果想只打包C模块,可以执行以下命令: mvn -pl","tags":["maven","java","spring boot"],"title":"多模块Maven工程单独打包某一模块工程","uri":"https://fhxisdog.github.io/2019/11/multi_module_maven/","year":"2019"},{"content":"\u0026gt; Configure project :core Checking the license for package Android SDK Platform 28 in /opt/android/sdk/licenses Warning: License for package Android SDK Platform 28 not accepted. FAILURE: Build failed with an exception. * What went wrong: A problem occurred configuring project ':core'. \u0026gt; Failed to install the following Android SDK packages as some licences have not been accepted. platforms;android-28 Android SDK Platform 28 To build this project, accept the SDK license agreements and install the missing components using the Android Studio SDK Manager. Alternatively, to transfer the license agreements from one workstation to another, see http://d.android.com/r/studio-ui/export-licenses.html Using Android SDK: /opt/android/sdk * Try: Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights. * Get more help at https://help.gradle.org BUILD FAILED in 28s Exited with code 1 解决方法:\n进入 $Andorid_HOME/tools/bin,执行yes | ./sdkmanager --licenses\n","id":11,"section":"posts","summary":"\u0026gt; Configure project :core Checking the license for package Android SDK Platform 28 in /opt/android/sdk/licenses Warning: License for package Android SDK Platform 28 not accepted. FAILURE: Build failed with an exception. * What went wrong: A problem occurred configuring project ':core'. \u0026gt; Failed to install the following Android SDK packages as some licences have not been accepted. platforms;android-28 Android SDK Platform 28 To build this project, accept the SDK license","tags":["flutter","android"],"title":"Android Platform 28 SDK License Not Accepted","uri":"https://fhxisdog.github.io/2019/11/adb_error/","year":"2019"},{"content":"安装python必须的依赖 sudo pip install jedi elpy rope pylint\nemacs配置 在~/.emacs.d/路径新建文件init.el\n添加以下配置:\n(require 'package) (package-initialize) ;;配置包管理列表的源 (setq package-archives '((\u0026quot;gnu\u0026quot; . \u0026quot;http://elpa.emacs-china.org/gnu/\u0026quot;) (\u0026quot;melpa\u0026quot; . \u0026quot;https://stable.melpa.org/packages/\u0026quot;))) (require 'cl) (defvar my/packages '( company better-deafults material-theme elpy company-jedi switch-window projectile neotree ) ) ;;jedi配置 (setq jedi:setup-keys t) (setq jedi:complete-on-dot t) (setq elpy-rpc-backend \u0026quot;jedi\u0026quot;) (when (fboundp 'jedi:setup) (jedi:setup)) ;; 自动补全 (global-company-mode t) (add-hook 'after-init-hook 'global-company-mode) ;; 使用material主题 (load-theme 'material t) ;; enable python插件 (elpy-enable) ;;设置切换窗口为a-z (setq switch-window-shortcut-style 'qwerty) (global-set-key (kbd \u0026quot;C-x o\u0026quot;) 'switch-window) ;;projectilep配置 (projectile-mode +1) (define-key projectile-mode-map (kbd \u0026quot;C-c p\u0026quot;) 'projectile-command-map) ;;neotree配置 (setq neo-smart-open t) (setq projectile-switch-project-action 'neotree-projectile-action) (defun neotree-project-dir () \u0026quot;Open NeoTree using the git root.\u0026quot; (interactive) (let ((project-dir (projectile-project-root)) (file-name (buffer-file-name))) (neotree-toggle) (if project-dir (if (neo-global--window-exists-p) (progn (neotree-dir project-dir) (neotree-find file-name))) (message \u0026quot;Could not find git project root.\u0026quot;)))) emacs配置 进入Emacs,ALT-X输入list-packages\n安装必须的包:\n\tcompany better-deafults material-theme elpy company-jedi switch-window projectile neotree 解释:\n company ;;自动补全 better-deafults material-theme ;;material主题 elpy ;;python开发必须环境 company-jedi ::jedi switch-window ::快速切换窗口 projectile ;;项目管理 neotree ;;树形文件结构 ","id":12,"section":"posts","summary":"安装python必须的依赖 sudo pip install jedi elpy rope pylint emacs配置 在~/.emacs.d/路径新建文件init.el 添加以下配置: (require 'package) (package-initialize) ;;配置包管理列","tags":["linux","emacs"],"title":"Emacs搭建python开发环境","uri":"https://fhxisdog.github.io/2019/11/emacs_python_development_env/","year":"2019"},{"content":"含义 C -\u0026gt; CTRL\nM -\u0026gt; ALT\nS-\u0026gt;SHIFT\n基本命令 快捷键 含义 快捷键 含义 C-x C-f 打开文件 C-x C-c 关闭emacs C-x C-s 保存文件 C-x C-w 当前缓冲区另存为 C-g 取消命令 C-x C-b 打开当前缓冲区列表 C-x b 打开一个缓冲区 C-x K 关闭当前缓冲区 C-x C-v 关闭当前缓冲区并打开新的文件 移动命令 快捷键 含义 快捷键 含义 C-n 向下移动一行 C-p 向上移动一行 C-f 向前移动一个字符 C-b 向后移动一个字符 C-a 移动到行首 C-e 移动到行尾 M-b 向后移动一个单词 M-f 向前移动一个单词 M-v 向上移动半屏 C-v 向下移动半屏 M-\u0026gt; 到文件末尾 M-\u0026lt; 到文件开头 M-g M-g 跳到xx行 窗口命令 快捷键 含义 快捷键 含义 C-x 2 水平分割窗口 C-x 3 垂直分割窗口 C-x 0 关闭当前窗口 C-x 1 关闭其他窗口 C-x 4 f 新tab打开文件 C-x 5 f 新窗口打开文件 C-x o 切换窗口 C-x 5 2 新建窗口 C-M-V 向下滚动其他窗口 C-M-S-V 向上滚动其他窗口 编辑命令 快捷键 含义 快捷键 含义 C-space 开始设置标记 C-@ 开始设置标记 M-w 复制标记内容 C-w 剪切选中内容 M-d 删除后面一个单词 C-d 删除后面一个字符 C-y 粘贴 C-k 删除一行 M-u 后面一个单词变成大写 M-l 后一个单词变成小写 M-c 后面一个单词首字母大写 替换命令 快捷键 含义 快捷键 含义 C-s 向下搜索 C-r 向上搜索 M-% 替换 文件夹命令 快捷键 含义 快捷键 含义 C-s d 打开文件夹列表 n 向下移动 p 向上移动 v 编辑光标所在文件 d 标记为删除 D 马上删除 x 执行标记 C 拷贝当前文件 R 重命名 + 新建文件夹 z 压缩文件 ! 对光标所在文件执行shell命令 q 退出 ","id":13,"section":"posts","summary":"含义 C -\u0026gt; CTRL M -\u0026gt; ALT S-\u0026gt;SHIFT 基本命令 快捷键 含义 快捷键 含义 C-x C-f 打开文件 C-x C-c 关闭emacs C-x C-s 保存文件 C-x C-w 当前缓冲区另存为 C-g 取消命令 C-x C-b 打开当前缓冲区列表","tags":["linux","emacs"],"title":"Emacs快捷键","uri":"https://fhxisdog.github.io/2019/11/emacs_shortcuts/","year":"2019"},{"content":"项目中遇到需要将vue打包到spring boot 中的static中,直接访问,不需要使用Nginx来访问nginx.具体做法如下:\n将vue工程放到spring boot工程最外层 修改pom文件 \u0026lt;plugin\u0026gt; \u0026lt;groupId\u0026gt;org.codehaus.mojo\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;exec-maven-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;executions\u0026gt; \u0026lt;!--程序打包的时候,利用本地已安装的node环境,同时编译管理后台前端的工程文件--\u0026gt; \u0026lt;execution\u0026gt; \u0026lt;id\u0026gt;npm-build-background\u0026lt;/id\u0026gt; \u0026lt;phase\u0026gt;verify\u0026lt;/phase\u0026gt; \u0026lt;goals\u0026gt; \u0026lt;goal\u0026gt;exec\u0026lt;/goal\u0026gt; \u0026lt;/goals\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;executable\u0026gt;${npm.command}\u0026lt;/executable\u0026gt; \u0026lt;arguments\u0026gt; \u0026lt;argument\u0026gt;run\u0026lt;/argument\u0026gt; \u0026lt;argument\u0026gt;prod-package\u0026lt;/argument\u0026gt; \u0026lt;/arguments\u0026gt; \u0026lt;workingDirectory\u0026gt;${basedir}/consumable-background\u0026lt;/workingDirectory\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/execution\u0026gt; \u0026lt;/executions\u0026gt; \u0026lt;/plugin\u0026gt; \u0026lt;plugin\u0026gt; \u0026lt;artifactId\u0026gt;maven-resources-plugin\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;3.1.0\u0026lt;/version\u0026gt; \u0026lt;executions\u0026gt; \u0026lt;!--添加管理后台前端工程的静态文件到jar包中--\u0026gt; \u0026lt;execution\u0026gt; \u0026lt;id\u0026gt;copy-consumable-background\u0026lt;/id\u0026gt; \u0026lt;phase\u0026gt;prepare-package\u0026lt;/phase\u0026gt; \u0026lt;goals\u0026gt; \u0026lt;goal\u0026gt;copy-resources\u0026lt;/goal\u0026gt; \u0026lt;/goals\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;outputDirectory\u0026gt;${basedir}/target/classes/static/background\u0026lt;/outputDirectory\u0026gt; \u0026lt;resources\u0026gt; \u0026lt;resource\u0026gt; \u0026lt;directory\u0026gt;${basedir}/consumable-background/dist\u0026lt;/directory\u0026gt; \u0026lt;/resource\u0026gt; \u0026lt;/resources\u0026gt; \u0026lt;/configuration\u0026gt; \u0026lt;/execution\u0026gt; \u0026lt;/executions\u0026gt; \u0026lt;/plugin\u0026gt; \u0026lt;/plugins\u0026gt; 配置拦截器(此步可以省略,应为springboot static 路径是默认放开的) @Override public void addResourceHandlers(org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry registry) { registry.addResourceHandler(\u0026quot;/static/**\u0026quot;) .addResourceLocations(\u0026quot;classpath:/static/\u0026quot;); } ","id":14,"section":"posts","summary":"项目中遇到需要将vue打包到spring boot 中的static中,直接访问,不需要使用Nginx来访问nginx.具体做法如下: 将vue工程放到","tags":["vue","java","spring boot"],"title":"将vue打包到springboot静态资源中","uri":"https://fhxisdog.github.io/2019/09/springbootvuepackagejar/","year":"2019"},{"content":"使用spring boot工程时,虽然官方提供了application.profiles.active参数去激活不同环境,但是如果打包时忘了修改还是比较麻烦的时。可以使用maven插件,自动的更新不同环境的 的配置文件。\n配置方法如下:\n这里列举三个环境:dev,test,prod\nresource目录结构如下(custom-config.properties为自定义的配置文件,区分不同环境也是以dev,test,prod结尾,如:custom-config-dev.properties):\n--- resource --- application.yml --- application-dev.yml --- application-test.yml --- application-prod.yml --- custom-config.properties --- custom-config-dev.properties --- custom-config-test.properties --- custom-config-prod.properties --- application-common.yml 修改pom文件: \u0026lt;profiles\u0026gt; \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;dev\u0026lt;/id\u0026gt; \u0026lt;activation\u0026gt; \u0026lt;activeByDefault\u0026gt;true\u0026lt;/activeByDefault\u0026gt; \u0026lt;/activation\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;profile.active\u0026gt;dev\u0026lt;/profile.active\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;/profile\u0026gt; \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;test\u0026lt;/id\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;profile.active\u0026gt;test\u0026lt;/profile.active\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;/profile\u0026gt; \u0026lt;profile\u0026gt; \u0026lt;id\u0026gt;prod\u0026lt;/id\u0026gt; \u0026lt;properties\u0026gt; \u0026lt;profile.active\u0026gt;prod\u0026lt;/profile.active\u0026gt; \u0026lt;/properties\u0026gt; \u0026lt;/profile\u0026gt; \u0026lt;/profiles\u0026gt; \u0026lt;build\u0026gt; \u0026lt;finalName\u0026gt;app\u0026lt;/finalName\u0026gt; \u0026lt;plugins\u0026gt; \u0026lt;/plugins\u0026gt; \u0026lt;resources\u0026gt; \u0026lt;resource\u0026gt; \u0026lt;directory\u0026gt;src/main/resources\u0026lt;/directory\u0026gt; \u0026lt;excludes\u0026gt; \u0026lt;exclude\u0026gt;application-*.yml\u0026lt;/exclude\u0026gt; \u0026lt;exclude\u0026gt;custom-config.properties\u0026lt;/exclude\u0026gt; \u0026lt;/excludes\u0026gt; \u0026lt;/resource\u0026gt; \u0026lt;resource\u0026gt; \u0026lt;directory\u0026gt;src/main/resources\u0026lt;/directory\u0026gt; \u0026lt;!-- 是否替换@xx@表示的maven properties属性值 --\u0026gt; \u0026lt;filtering\u0026gt;true\u0026lt;/filtering\u0026gt; \u0026lt;includes\u0026gt; \u0026lt;include\u0026gt;application.yml\u0026lt;/include\u0026gt; \u0026lt;include\u0026gt;application-${profile.active}.yml\u0026lt;/include\u0026gt; \u0026lt;include\u0026gt;application-common.yml\u0026lt;/include\u0026gt; \u0026lt;include\u0026gt;custom-config.properties\u0026lt;/include\u0026gt; \u0026lt;/includes\u0026gt; \u0026lt;/resource\u0026gt; \u0026lt;/resources\u0026gt; \u0026lt;filters\u0026gt; \u0026lt;filter\u0026gt; src/main/resources/custom-config-${profile.active}.properties \u0026lt;/filter\u0026gt; \u0026lt;/filters\u0026gt; \u0026lt;/build\u0026gt; 修改配置文件: application.ym文件修改:\nspring: profiles: active: @profile.active@ include: common 这里 @xxx@相当于占位符,打包时会自动替换成dev,test,prod\ncustom-config.propertis文件修改如下:\ncustomA=@customA@ customB=@customB@ custom-config-dev.properties文件:\ncustomA=dev-customA customB=dev-customB 这样就可以替换自定义的配置文件\n","id":15,"section":"posts","summary":"使用spring boot工程时,虽然官方提供了application.profiles.active参数去激活不同环境,但是如果打包时忘了修","tags":["java","spring boot"],"title":"Spring Boot 分环境打包","uri":"https://fhxisdog.github.io/2019/09/springboot-profile/","year":"2019"},{"content":"spring boot 设置contextPath后,如果js中的请求路径不设置,会造成404的问题。 设置方法:\n在html页面header中添加配置:\n\u0026lt;meta name=\u0026quot;ctx\u0026quot; th:content=\u0026quot;${#httpServletRequest.getContextPath()}\u0026quot; \u0026gt; js中引用:\n var ctx = $(\u0026quot;meta[name='ctx']\u0026quot;).attr(\u0026quot;content\u0026quot;); 变量ctx就是contextPath\n","id":16,"section":"posts","summary":"spring boot 设置contextPath后,如果js中的请求路径不设置,会造成404的问题。 设置方法: 在html页面header中添加配置: \u0026lt;meta name=\u0026quot;ctx\u0026quot; th:content=\u0026quot;${#httpServletRequest.getContextPath()}\u0026quot; \u0026gt; j","tags":["java","spring boot"],"title":"Spring Boot Thymeleaf 引用contexpath","uri":"https://fhxisdog.github.io/2019/09/springbootthymeleafcontextpath/","year":"2019"},{"content":" 使用华硕的便携笔记本需要安装displaylink驱动,官方又只提供windows驱动,在manjaro上使用需要自己安装驱动,经过研究,安装方法如下: 准备工作 uname -r查看内核版本,输出:4.19.23-1-MANJARO,说明我的内核版本是419,执行pacman -Ss linux-headers查看库中的依赖,执行sudo pacman -S core/liux419-headers安装头文件 安装evdi:yay -S evdi-git\t3 安装displaylink yay -S displaylink 开始配置 装载evdi: sudo modprobe evdi 装载udl: sudo modprobe udl 启用displaylink.sevice: systemctl enable display link 开启displaylink.service:systemctl start displaylink 新增配置文件 /usr/share/X11/xorg.conf.d/20-evdidevice.conf --- Section \u0026quot;OutputClass\u0026quot; Identifier \u0026quot;DisplayLink\u0026quot; MatchDriver \u0026quot;evdi\u0026quot; Driver \u0026quot;modesetting\u0026quot; Option \u0026quot;AccelMethod\u0026quot; \u0026quot;none\u0026quot; EndSection 执行xandr --listproviders,可以看到输出: Providers: number : 2 Provider 0: id: 0x45 cap: 0xb, Source Output, Sink Output, Sink Offload crtcs: 4 outputs: 2 associated providers: 1 name:Intel Provider 1: id: 0x14d cap: 0x2, Sink Output crtcs: 1 outputs: 1 associated providers: 1 name:modesetting lxrandr开启屏幕 ","id":17,"section":"posts","summary":"使用华硕的便携笔记本需要安装displaylink驱动,官方又只提供windows驱动,在manjaro上使用需要自己安装驱动,经过研究,安","tags":["manjaro","linux"],"title":"Manjaro安装displaylink","uri":"https://fhxisdog.github.io/2019/06/manjaro_install_displaylink/","year":"2019"},{"content":" 在Docker中,容器之间的链接是一种很常见的操作:它提供了访问其中的某个容器的网络服务而不需要将所需的端口暴露给Docker Host主机的功能。Docker Compose中对该特性的支持同样是很方便的。然而,如果需要链接的容器没有定义在同一个docker-compose.yml中的时候,这个时候就稍微麻烦复杂了点。\n 在不使用Docker Compose的时候,将两个容器链接起来使用—link参数,相对来说比较简单,以nginx镜像为例子:\ndocker run --rm --name test1 -d nginx #开启一个实例test1 docker run --rm --name test2 --link test1 -d nginx #开启一个实例test2并与test1建立链接 这样,test2与test1便建立了链接,就可以在test2中使用访问test1中的服务了。\n如果使用Docker Compose,那么这个事情就更简单了,还是以上面的nginx镜像为例子,编辑docker-compose.yml文件为:\nversion: \u0026quot;3\u0026quot; services: test2: image: nginx depends_on: - test1 links: - test1 test1: image: nginx 最终效果与使用普通的Docker命令docker run xxxx建立的链接并无区别。这只是一种最为理想的情况。\n 如果容器没有定义在同一个docker-compose.yml文件中,应该如何链接它们呢? 又如果定义在docker-compose.yml文件中的容器需要与docker run xxx启动的容器链接,需要如何处理? 针对这两种典型的情况,下面给出我个人测试可行的办法: 方式一:让需要链接的容器同属一个外部网络 我们还是使用nginx镜像来模拟这样的一个情景:假设我们需要将两个使用Docker Compose管理的nignx容器(test1和test2)链接起来,使得test2能够访问test1中提供的服务,这里我们以能ping通为准。\n首先,我们定义容器test1的docker-compose.yml文件内容为:\nversion: \u0026quot;3\u0026quot; services: test2: image: nginx container_name: test1 networks: - default - app_net networks: app_net: external: true 容器test2内容与test1基本一样,只是多了一个external_links,需要特别说明的是:最近发布的Docker版本已经不需要使用external_links来链接容器,容器的DNS服务可以正确的作出判断,因此如果你你需要兼容较老版本的Docker的话,那么容器test2的docker-compose.yml文件内容为:\nversion: \u0026quot;3\u0026quot; services: test2: image: nginx networks: - default - app_net external_links: - test1 container_name: test2 networks: app_net: external: true 否则的话,test2的docker-compose.yml和test1的定义完全一致,不需要额外多指定一个external_links。相关的问题请参见stackoverflow上的相关问题:docker-compose + external container\n正如你看到的那样,这里两个容器的定义里都使用了同一个外部网络app_net,因此,我们需要在启动这两个容器之前通过以下命令再创建外部网络:\ndocker network create app_net 之后,通过docker-compose up -d命令启动这两个容器,然后执行docker exec -it test2 ping test1,你将会看到如下的输出:\ndocker exec -it test2 ping test1 PING test1 (172.18.0.2): 56 data bytes 64 bytes from 172.18.0.2: icmp_seq=0 ttl=64 time=0.091 ms 64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.146 ms 64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.150 ms 64 bytes from 172.18.0.2: icmp_seq=3 ttl=64 time=0.145 ms 64 bytes from 172.18.0.2: icmp_seq=4 ttl=64 time=0.126 ms 64 bytes from 172.18.0.2: icmp_seq=5 ttl=64 time=0.147 ms 证明这两个容器是成功链接了,反过来在test1中pingtest2也是能够正常ping通的。\n如果我们通过docker run \u0026ndash;rm \u0026ndash;name test3 -d nginx这种方式来先启动了一个容器(test3)并且没有指定它所属的外部网络,而需要将其与test1或者test2链接的话,这个时候手动链接外部网络即可:\ndocker network connect app_net test3 这样,三个容器都可以相互访问了。 方式二:更改需要链接的容器的网络模式 通过更改你想要相互链接的容器的网络模式为bridge,并指定需要链接的外部容器(external_links)即可。与同属外部网络的容器可以相互访问的链接方式一不同,这种方式的访问是单向的。\n还是以nginx容器镜像为例子,如果容器实例nginx1需要访问容器实例nginx2,那么nginx2的doker-compose.yml定义为:\nversion: \u0026quot;3\u0026quot; services: nginx2: image: nginx container_name: nginx2 network_mode: bridge 与其对应的,nginx1的docker-compose.yml定义为:\nversion: \u0026quot;3\u0026quot; services: nginx1: image: nginx external_links: - nginx2 container_name: nginx1 network_mode: bridge 需要特别说明的是,这里的external_links是不能省略的,而且nginx1的启动必须要在nginx2之后,否则可能会报找不到容器nginx2的错误。\n 接着我们使用ping来测试下连通性:\n$ docker exec -it nginx1 ping nginx2 # nginx1 to nginx2 PING nginx2 (172.17.0.4): 56 data bytes 64 bytes from 172.17.0.4: icmp_seq=0 ttl=64 time=0.141 ms 64 bytes from 172.17.0.4: icmp_seq=1 ttl=64 time=0.139 ms 64 bytes from 172.17.0.4: icmp_seq=2 ttl=64 time=0.145 ms $ docker exec -it nginx2 ping nginx1 #nginx2 to nginx1 ping: unknown host 以上也能充分证明这种方式是属于单向联通的。\n在实际应用中根据自己的需要灵活的选择这两种链接方式,如果想偷懒的话,大可选择第二种。不过我更推荐第一种,不难看出无论是联通性还是灵活性,较为更改网络模式的第二种都更为友好。\n 本文转载自夏末的博客\n ","id":18,"section":"posts","summary":"在Docker中,容器之间的链接是一种很常见的操作:它提供了访问其中的某个容器的网络服务而不需要将所需的端口暴露给Docker Host主机的","tags":["docker"],"title":"Docker Compose:链接外部容器的几种方式","uri":"https://fhxisdog.github.io/2019/06/docker_compose_external_image_method/","year":"2019"},{"content":" Alpha:是内部测试版,一般不向外部发布,会有很多Bug.一般只有测试人员使用。 Beta:也是测试版,这个阶段的版本会一直加入新的功能。在Alpha版之后推出。 RC:(Release Candidate) 顾名思义么 ! 用在软件上就是候选版本。系统平台上就是发行候选版本。RC版不会再加入新的功能了,主要着重于除错。 GA:General Availability,正式发布的版本,在国外都是用GA来说明release版本的。 RTM:(Release to Manufacture)是给工厂大量压片的版本,内容跟正式版是一样的,不过RTM版也有出限制、评估版的。但是和正式版本的主要程序代码都是一样的。 OEM:是给计算机厂商随着计算机贩卖的,也就是随机版。只能随机器出货,不能零售。只能全新安装,不能从旧有操作系统升级。包装不像零售版精美,通常只有一面CD和说明书(授权书)。 RVL:号称是正式版,其实RVL根本不是版本的名称。它是中文版/英文版文档破解出来的。 EVAL:而流通在网络上的EVAL版,与“评估版”类似,功能上和零售版没有区别。 RTL:Retail(零售版)是真正的正式版,正式上架零售版。在安装盘的i386文件夹里有一个eula.txt,最后有一行EULAID,就是你的版本。比如简体中文正式版是EULAID:WX.4_PRO_RTL_CN,繁体中文正式版是WX.4_PRO_RTL_TW。其中:如果是WX.开头是正式版,WB.开头是测试版。_PRE,代表家庭版;_PRO,代表专业版。 α、β、λ常用来表示软件测试过程中的三个阶段,α是第一阶段,一般只供内部测试使用;β是第二个阶段,已经消除了软件中大部分的不完善之处,但仍有可能还存在缺陷和漏洞,一般只提供给特定的用户群来测试使用;λ是第三个阶段,此时产品已经相当成熟,只需在个别地方再做进一步的优化处理即可上市发行。 ","id":19,"section":"posts","summary":"Alpha:是内部测试版,一般不向外部发布,会有很多Bug.一般只有测试人员使用。 Beta:也是测试版,这个阶段的版本会一直加入新的功能。在","tags":["orther"],"title":"Alpha、Beta、RC、GA版本的区别","uri":"https://fhxisdog.github.io/2019/06/version_name/","year":"2019"},{"content":"服务启动 本地单机启动 下载源码 从github上下载源码方式: git clone https://github.com/alibaba/nacos.git cd nacos/ mvn -Prelease-nacos clean install -U ls -al distribution/target/ // change the $version to your actual path cd distribution/target/nacos-server-$version/nacos/bin 下载编译后压缩包方式 从 最新稳定版本 下载 nacos-server-$version.zip 包\n unzip nacos-server-$version.zip 或者 tar -xvf nacos-server-$version.tar.gz cd nacos/bin 启动服务 Linux/Unix/Mac sh startup.sh -m standalone windows cmd startup.cmd 或者双击startup.cmd运行文件。\n测试 浏览器访问:localhost:8848/nacos(默认用户名/密码:nacos/nacos)\n单机无数据库docker启动 下载镜像 docker pull nacos/nacos-server 启动docker docker run -d --name nacos -e PREFER_HOST_NAME=hostname -e MODE=standalone -e SPRING_DATASOURCE_PLATFORM=empty -p 8848:8848 nacos/nacos-server docker mysql启动 新建nacos-standalone文件夹,新建standalone.env文件: PREFER_HOST_NAME=hostname MODE=standalone SPRING_DATASOURCE_PLATFORM=mysql DB.NUM=1 MYSQL_MASTER_SERVICE_HOST=iotechina-dev-mysql-5.7.26 MYSQL_MASTER_SERVICE_DB_NAME=nacos MYSQL_MASTER_SERVICE_PORT=3306 MYSQL_MASTER_SERVICE_USER=root MYSQL_MASTER_SERVICE_PASSWORD=you know 将mysql连接到nacos网络 docker network create nacos docker network connect nacos iotechina-dev-mysql-5.7.26 启动 docker run -d --name nacos --env-file=standalone.env --network nacos -p 8848:8848 nacos/nacos-server docker-compose启动 新建nacos文件夹 新建docker-compose.yml文件 version: \u0026quot;3\u0026quot; networks: nacos: external: true services: nacos1: hostname: nacos1 container_name: nacos1 image: nacos/nacos-server:latest volumes: - ./cluster-logs/nacos1:/home/nacos/logs - ./init.d/custom.properties:/home/nacos/init.d/custom.properties ports: - \u0026quot;8848:8848\u0026quot; - \u0026quot;9555:9555\u0026quot; env_file: - ./env/nacos-hostname.env external_links: - iotechina-dev-mysql-5.7.26 networks: - nacos restart: on-failure nacos2: hostname: nacos2 image: nacos/nacos-server:latest container_name: nacos2 volumes: - ./cluster-logs/nacos2:/home/nacos/logs - ./init.d/custom.properties:/home/nacos/init.d/custom.properties ports: - \u0026quot;8849:8848\u0026quot; external_links: - iotechina-dev-mysql-5.7.26 env_file: - ./env/nacos-hostname.env restart: on-failure networks: - nacos nacos3: hostname: nacos3 image: nacos/nacos-server:latest container_name: nacos3 volumes: - ./cluster-logs/nacos3:/home/nacos/logs - ./init.d/custom.properties:/home/nacos/init.d/custom.properties ports: - \u0026quot;8850:8848\u0026quot; external_links: - iotechina-dev-mysql-5.7.26 env_file: - ./env/nacos-hostname.env restart: on-failure networks: - nacos 复制官方docker配置到 nacos目录对应的文件夹 编辑nacos/env/nacos-hostname.env #nacos dev env PREFER_HOST_MODE=hostname NACOS_SERVERS=nacos1:8848 nacos2:8848 nacos3:8848 MYSQL_MASTER_SERVICE_HOST=iotechina-dev-mysql-5.7.26 MYSQL_MASTER_SERVICE_DB_NAME= DB_NAME MYSQL_MASTER_SERVICE_PORT=3306 MYSQL_SLAVE_SERVICE_HOST=iotechina-dev-mysql-5.7.26 MYSQL_SLAVE_SERVICE_PORT=3306 MYSQL_MASTER_SERVICE_USER=you know MYSQL_MASTER_SERVICE_PASSWORD=you know mysql 连接 nacos network docker network connect nacos iotechina-dev-mysql-5.7.26 启动 docker-compose up -d 单机集群mysql启动 复制mysql驱动 在nacos/plugins/新建文件夹mysql,复制对应数据库的jar驱动到这个文件夹\n修改配置文件 编辑nacos/application.properties文件,加入如下内容:\nspring.datasource.platform=mysql db.num=1 db.url.0=jdbc:mysql://dev.iotechina.com:13306/nacos?characterEncoding=utf8\u0026amp;connectTimeout=1000\u0026amp;socketTimeout=3000\u0026amp;autoReconnect=true db.user=you know db.password=you know 编辑nacos/conf/cluster.conf(如果没有新建一个)\n192.168.0.118:8848 #注意这里和`conf/application.properties`中的`server.port`相对应 192.168.0.118:8847 192.168.0.118:8846 启动 sh startup.sh spring cloud 版本对应关系 由于nacos还没有纳入spring cloud体系,所以需要手动管理依赖关系。\n版本对应关系:版本说明 Wiki\n服务发现 Demo 启动nacos-server 配置应用 新建两个spring-boot应用:service-consumer,service-provider 分别配置两个应用 引入pom依赖:\n\u0026lt;!--注意这里版本依赖关系,根据自己的springcloud版本引入对应的版本--\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.cloud\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-cloud-starter-alibaba-nacos-discovery\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${spring-cloud-nacos-version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; 启动类添加注解:@EnableDiscoveryClient\n编辑application.properties:\nspring.application.name=${application-name} #应用名称,根据自己需要填写 spring.cloud.nacos.discovery.server-addr=dev.iotechina.com:8848 使用feign进行调用测试 配置中心 Demo 启动nacos-server 添加配置 打开nacos网页端: 配置管理-\u0026gt;配置列表-\u0026gt;点击加号添加配置\nData ID: service-consumer.properties #注意,此处一定要有.properties,并且名字和应用的${spring.application.name}一致 Group : DEFAULT_GROUP 配置格式: properties 配置内容: server.port=8845 点击发布\n应用配置 引入pom依赖\n\u0026lt;!--注意这里版本依赖关系,根据自己的springcloud版本引入对应的版本--\u0026gt; \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.springframework.cloud\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;spring-cloud-starter-alibaba-nacos-config\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;${spring-cloud-nacos-version}\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; 编辑bootstrap.properties:\nspring.application.name=service-consumer spring.cloud.nacos.config.server-addr=dev.iotechina.com:8848 启动应用,发现启动端口已变为8845\n2019-05-29 15:13:35.493 INFO 1593 --- [ main] o.s.c.a.n.c.NacosPropertySourceBuilder : Loading nacos data, dataId: 'service-consumer.properties', group: 'DEFAULT_GROUP' 2019-05-29 15:13:35.494 INFO 1593 --- [ main] b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource {name='NACOS', propertySources=[NacosPropertySource {name='service-consumer.properties'}]} 2019-05-29 15:13:35.498 INFO 1593 --- [ main] c.d.n.NacosConsumerApplication : No active profile set, falling back to default profiles: default 2019-05-29 15:13:35.974 INFO 1593 --- [ main] o.s.cloud.context.scope.GenericScope : BeanFactory id=825b518e-28f8-3c01-8579-67d040c18160 2019-05-29 15:13:35.991 INFO 1593 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'com.demo.nacosconsumer.feign.EchoFeign' of type [org.springframework.cloud.openfeign.FeignClientFactoryBean] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 2019-05-29 15:13:36.033 INFO 1593 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$6f581f58] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 2019-05-29 15:13:36.188 INFO 1593 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8845 (http) profile粒度的配置 nacos控制台新建service-consumer-test.properties文件,写入内容server.port=8847。\n编辑bootstrap.properties:\nspring.profiles.active=test 重启应用,查看端口变化\n自定义 namespace 的配置 Nacos 的 Namespace 的概念:\n 用于进行租户粒度的配置隔离。不同的命名空间下,可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。\n 在没有明确指定 ${spring.cloud.nacos.config.namespace} 配置的情况下, 默认使用的是 Nacos 上 Public 这个namespae。如果需要使用自定义的命名空间,可以通过以下配置来实现:\nspring.cloud.nacos.config.namespace=b3404bc0-d7dc-4855-b519-570ed34b62d7 *** 该配置必须放在 bootstrap.properties 文件中。此外 spring.cloud.nacos.config.namespace 的值是 namespace 对应的 id,id 值可以在 Nacos 的控制台获取。并且在添加配置时注意不要选择其他的 namespae,否则将会导致读取不到正确的配置。 ***\n自定义 Group 的配置 在没有明确指定 ${spring.cloud.nacos.config.group} 配置的情况下, 默认使用的是 DEFAULT_GROUP 。如果需要自定义自己的 Group,可以通过以下配置来实现:\nspring.cloud.nacos.config.group=DEVELOP_GROUP *** 该配置必须放在 bootstrap.properties 文件中。并且在添加配置时 Group 的值一定要和 spring.cloud.nacos.config.group 的配置值一致。***\n自定义扩展的 Data Id 配置 配置Demo:\nspring.application.name=opensource-service-provider spring.cloud.nacos.config.server-addr=127.0.0.1:8848 # config external configuration # 1、Data Id 在默认的组 DEFAULT_GROUP,不支持配置的动态刷新 spring.cloud.nacos.config.ext-config[0].data-id=ext-config-common01.properties # 2、Data Id 不在默认的组,不支持动态刷新 spring.cloud.nacos.config.ext-config[1].data-id=ext-config-common02.properties spring.cloud.nacos.config.ext-config[1].group=GLOBALE_GROUP # 3、Data Id 既不在默认的组,也支持动态刷新 spring.cloud.nacos.config.ext-config[2].data-id=ext-config-common03.properties spring.cloud.nacos.config.ext-config[2].group=REFRESH_GROUP spring.cloud.nacos.config.ext-config[2].refresh=true 可以看到:\n 通过 spring.cloud.nacos.config.ext-config[n].data-id 的配置方式来支持多个 Data Id 的配置。 通过 spring.cloud.nacos.config.ext-config[n].group 的配置方式自定义 Data Id 所在的组,不明确配置的话,默认是 DEFAULT_GROUP。 通过 spring.cloud.nacos.config.ext-config[n].refresh 的配置方式来控制该 Data Id 在配置变更时,是否支持应用中可动态刷新, 感知到最新的配置值。默认是不支持的。 Note: 多个 Data Id 同时配置时,他的优先级关系是 spring.cloud.nacos.config.ext-config[n].data-id 其中 n 的值越大,优先级越高。\nNote:*** spring.cloud.nacos.config.ext-config[n].data-id 的值必须带文件扩展名,文件扩展名既可支持 properties,又可以支持 yaml/yml。 此时 spring.cloud.nacos.config.file-extension 的配置对自定义扩展配置的 Data Id 文件扩展名没有影响。***\n为了更加清晰的在多个应用间配置共享的 Data Id ,你可以通过以下的方式来配置:\nspring.cloud.nacos.config.shared-dataids=bootstrap-common.properties,all-common.properties spring.cloud.nacos.config.refreshable-dataids=bootstrap-common.properties 可以看到:\n 通过 spring.cloud.nacos.config.shared-dataids 来支持多个共享 Data Id 的配置,多个之间用逗号隔开。 通过 spring.cloud.nacos.config.refreshable-dataids 来支持哪些共享配置的 Data Id 在配置变化时,应用中是否可动态刷新, 感知到最新的配置值,多个 Data Id 之间用逗号隔开。如果没有明确配置,默认情况下所有共享配置的 Data Id 都不支持动态刷新。 Note:*** 通过 spring.cloud.nacos.config.shared-dataids 来支持多个共享配置的 Data Id 时, 多个共享配置间的一个优先级的关系我们约定:按照配置出现的先后顺序,即后面的优先级要高于前面。 ***\nNote:*** 通过 1spring.cloud.nacos.config.shared-dataids1 来配置时,Data Id 必须带文件扩展名,文件扩展名既可支持 properties,也可以支持 yaml/yml。 此时 spring.cloud.nacos.config.file-extension1 的配置对自定义扩展配置的 Data Id 文件扩展名没有影响。 ***\nNote:spring.cloud.nacos.config.refreshable-dataids 给出哪些需要支持动态刷新时,Data Id 的值也必须明确给出文件扩展名。\n配置的优先级 Spring Cloud Alibaba Nacos Config 目前提供了三种配置能力从 Nacos 拉取相关的配置\n A: 通过 spring.cloud.nacos.config.shared-dataids 支持多个共享 Data Id 的配置 B: 通过 spring.cloud.nacos.config.ext-config[n].data-id 的方式支持多个扩展 Data Id 的配置 C: 通过内部相关规则(应用名、应用名+ Profile )自动生成相关的 Data Id 配置 当三种方式共同使用时,他们的一个优先级关系是:A \u0026lt; B \u0026lt; C\n遇到的问题 登陆错误:`io.jsonwebtoken.security.SignatureException: Unable to obtain JCA MAC algorithm \u0026lsquo;HmacSHA256\u0026rsquo;: Algorithm HmacSHA256 not available\n解决方法:\n1.由于是我yay -S 安装的jdk,所以没有JAVA_HOME,解决方案,控制台先执行export JAVA_HOME=/usr/lib/jvm/openjdk1.8.1 然后执行 sh startup.sh -m standalone\n2.设置JAVA_HOME环境变量\n参考连接:\nhttps://github.com/alibaba/nacos/issues/711\nhttps://github.com/jwtk/jjwt/issues/429\n 查看服务列表跳会登陆页,浏览器控制台提示401错误\n解决方法:\n清除浏览器缓存\n 集群模式,cluster.conf中的ip写127.0.0.1:8848报错\n 解决方法:\n写ip地址,例如:`192.168.0.118` ","id":20,"section":"posts","summary":"服务启动 本地单机启动 下载源码 从github上下载源码方式: git clone https://github.com/alibaba/nacos.git cd nacos/ mvn -Prelease-nacos clean install -U ls -al distribution/target/ // change the $version to your actual path cd distribution/target/nacos-server-$version/nacos/bin 下载编译后压缩包方式 从 最新稳定版本 下","tags":["java","nacos","spring cloud"],"title":"nacos使用指南","uri":"https://fhxisdog.github.io/2019/05/nacos_guide/","year":"2019"},{"content":"运行 单机运行 下载最新release包 解压,sh ${skywalkingpath}/bin/startup.sh 在springboot启动命令前加入启动参数 -javaagent:${path}/skywalking-agent.jar #path为skywalking解压的agent目录 访问localhost:8080查看结果 docker 运行 前置条件 下载 下载最新release包并解压\n构建docker bridge dcoker network create skywalking 使用数据库 ElasticSearch 启动es docker run -p 9200:9200 -p 9300:9300 -e \u0026quot;discovery.type=single-node\u0026quot; docker.elastic.co/elasticsearch/elasticsearch:6.4.3 #skywalking目前只支持6.x(6.1.0版本) 初始化数据库 编辑 ${path}/apache-skywalking-bin/config/appliccation.yml\nstorage: #elasticsearch: nameSpace: ${SW_NAMESPACE:\u0026quot;\u0026quot;} clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:dev.iotechina.com:9201} #user: ${SW_ES_USER:\u0026quot;\u0026quot;} #password: ${SW_ES_PASSWORD:\u0026quot;\u0026quot;} indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:2} indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:0} #Batch process setting, refer to https://www.elastic.co/guide/en/elasticsearch/client/java-api/5.5/java-docs-bulk-processor.html bulkActions: ${SW_STORAGE_ES_BULK_ACTIONS:2000} # Execute the bulk every 2000 requests bulkSize: ${SW_STORAGE_ES_BULK_SIZE:20} # flush the bulk every 20mb flushInterval: ${SW_STORAGE_ES_FLUSH_INTERVAL:10} # flush the bulk every 10 seconds whatever the number of requests concurrentRequests: ${SW_STORAGE_ES_CONCURRENT_REQUESTS:2} # the number of concurrent requests metadataQueryMaxSize: ${SW_STORAGE_ES_QUERY_MAX_SIZE:5000} segmentQueryMaxSize: ${SW_STORAGE_ES_QUERY_SEGMENT_SIZE:200} 执行\nsh ${path}/apache-skywalking-bin/bin/oapServiceInit.sh 执行\n tail -f ${path}/apache-skywalking-bin/config/skywalking-oap-server.log 等待执行完成\nmysql 复制mysql驱动到${path}/apapch-skywalking-bin/oap-libs 编辑 ${path}/apache-skywalking-bin/config/appliccation.yml storage: mysql: metadataQueryMaxSize: ${SW_STORAGE_H2_QUERY_MAX_SIZE:5000} 编辑${path}/apache-skywalking-bin/config/datasource-setting.properties jdbcUrl=jdbc:mysql://dev.iotechina.com:3316/sky_walking dataSource.user= you know dataSource.password=you know dataSource.cachePrepStmts=true dataSource.prepStmtCacheSize=250 dataSource.prepStmtCacheSqlLimit=2048 dataSource.useServerPrepStmts=true dataSource.useLocalSessionState=true dataSource.rewriteBatchedStatements=true dataSource.cacheResultSetMetadata=true dataSource.cacheServerConfiguration=true dataSource.elideSetAutoCommits=true dataSource.maintainTimeStats=false sh ${path}/apache-skywalking-bin/bin/oapServiceInit.sh 执行\ntail -f ${path}/apache-skywalking-bin/config/skywalking-oap-server.log 等待执行完成\n构建OAP镜像 准备条件 复制oap-libs文件夹到/root/skywalking/oap目录\n复制config目录到/root/skywalking/oap目录\n编辑config目录下log4j2.xml:\n\u0026lt;?xml version=\u0026quot;1.0\u0026quot; encoding=\u0026quot;UTF-8\u0026quot;?\u0026gt; \u0026lt;Configuration status=\u0026quot;INFO\u0026quot;\u0026gt; \u0026lt;Appenders\u0026gt; \u0026lt;Console name=\u0026quot;Console\u0026quot; target=\u0026quot;SYSTEM_OUT\u0026quot;\u0026gt; \u0026lt;PatternLayout charset=\u0026quot;UTF-8\u0026quot; pattern=\u0026quot;%d - %c -%-4r [%t] %-5p %x - %m%n\u0026quot;/\u0026gt; \u0026lt;/Console\u0026gt; \u0026lt;/Appenders\u0026gt; \u0026lt;Loggers\u0026gt; \u0026lt;logger name=\u0026quot;org.eclipse.jetty\u0026quot; level=\u0026quot;INFO\u0026quot;/\u0026gt; \u0026lt;logger name=\u0026quot;org.apache.zookeeper\u0026quot; level=\u0026quot;INFO\u0026quot;/\u0026gt; \u0026lt;logger name=\u0026quot;org.elasticsearch.common.network.IfConfig\u0026quot; level=\u0026quot;INFO\u0026quot;/\u0026gt; \u0026lt;logger name=\u0026quot;io.grpc.netty\u0026quot; level=\u0026quot;INFO\u0026quot;/\u0026gt; \u0026lt;logger name=\u0026quot;org.apache.skywalking.oap.server.receiver.istio.telemetry\u0026quot; level=\u0026quot;DEBUG\u0026quot;/\u0026gt; \u0026lt;Root level=\u0026quot;INFO\u0026quot;\u0026gt; \u0026lt;AppenderRef ref=\u0026quot;Console\u0026quot;/\u0026gt; \u0026lt;/Root\u0026gt; \u0026lt;/Loggers\u0026gt; \u0026lt;/Configuration\u0026gt; 编写启动脚本 新建docker-entrypoint.sh:\n#!/bin/bash set -ex CLASSPATH=\u0026quot;config:$CLASSPATH\u0026quot; for i in oap-libs/*.jar do CLASSPATH=\u0026quot;$i:$CLASSPATH\u0026quot; done exec java -Xms256M -Xmx512M -classpath $CLASSPATH org.apache.skywalking.oap.server.starter.OAPServerStartUp \u0026quot;$@\u0026quot; 编写Dockerfile 新建Dcokerfile:\nFROM openjdk:8-jre-alpine RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime RUN echo 'Asia/Shanghai' \u0026gt;/etc/timezone #设置时区,不然ui会查不到数据 WORKDIR /skywalking RUN apk add --no-cache \\ bash COPY docker-entrypoint.sh /skywalking COPY oap-libs/ /skywalking/oap-libs EXPOSE 12800 11800 ENTRYPOINT [\u0026quot;sh\u0026quot;,\u0026quot;docker-entrypoint.sh\u0026quot;] 构建镜像 dcoker build -t {your tag} . 启动容器 docker run --name skywalking-oap -d -v /root/skywalking/oap/config:/skywalking/config -p 12800:12800 -p 11800:11800 --network skywalking {your tag} 构建ui镜像 准备条件 复制webapp文件夹到/root/skywalking/ui目录\n在/root/skywalking/ui/webapp目录下新建文件logback.xml:\n\u0026lt;?xml version=\u0026quot;1.0\u0026quot; encoding=\u0026quot;UTF-8\u0026quot;?\u0026gt; \u0026lt;configuration\u0026gt; \u0026lt;include resource=\u0026quot;org/springframework/boot/logging/logback/base.xml\u0026quot; /\u0026gt; \u0026lt;/configuration\u0026gt; 编写启动脚本: 新建docker-entrypoint.sh:\n#!/bin/bash exec java -XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -jar /skywalking/webapp/skywalking-webapp.jar --logging.config=/skywalking/webapp/logback.xml --spring.config.location=/skywalking/webapp/webapp.yml \u0026quot;$@\u0026quot; 编写Dockerfile: 新建Dockerfile:\nFROM openjdk:8-jre-alpine RUN apk add --no-cache \\ bash RUN echo 'Asia/Shanghai' \u0026gt;/etc/timezone WORKDIR /skywalking COPY docker-entrypoint.sh . EXPOSE 8080 ENTRYPOINT [\u0026quot;bash\u0026quot;, \u0026quot;docker-entrypoint.sh\u0026quot;] 构建镜像 dcoker build -t {your tag} . 修改配置 编辑/root/skywalking/webapp/webapp.yml\nserver: port: 8080 collector: path: /graphql ribbon: ReadTimeout: 10000 # Point to all backend's restHost:restPort, split by , listOfServers: skywalking-oap:12800 #服务端地址 security: user: admin: password: you know 启动容器 docker run --name skywalking-ui -d -v /root/skywalking/ui/webapp:/skywalking/webapp -p 8080:8080 --network skywalking {your tag} 修改agent配置 修改 path/agent/config/agent.config:\ncollector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES:你的服务器地址:11800} 启动应用并访问服务器8080端口查看信息 连接elasticsearch(基于上一步docker运行skywalking) 启动esasticsearch docker run -d --name es-skywalking -p 9201:9200 -p 9301:9300 -e \u0026quot;discovery.type=single-node\u0026quot; elasticsearch:6.4.3 #skywalking 6.1.0 貌似不支持 es 7.x的版本 初始化elasticsearch 修改本地 path/config/application.yml:\nstorage: elasticsearch: #nameSpace: ${SW_NAMESPACE:\u0026quot;\u0026quot;} clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:你的服务器地址:9201} #user: ${SW_ES_USER:\u0026quot;\u0026quot;} #password: ${SW_ES_PASSWORD:\u0026quot;\u0026quot;} indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:2} indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:0} # Batch process setting, refer to https://www.elastic.co/guide/en/elasticsearch/client/java-api/5.5/java-docs-bulk-processor.html bulkActions: ${SW_STORAGE_ES_BULK_ACTIONS:2000} # Execute the bulk every 2000 requests bulkSize: ${SW_STORAGE_ES_BULK_SIZE:20} # flush the bulk every 20mb flushInterval: ${SW_STORAGE_ES_FLUSH_INTERVAL:10} # flush the bulk every 10 seconds whatever the number of requests concurrentRequests: ${SW_STORAGE_ES_CONCURRENT_REQUESTS:2} # the number of concurrent requests metadataQueryMaxSize: ${SW_STORAGE_ES_QUERY_MAX_SIZE:5000} segmentQueryMaxSize: ${SW_STORAGE_ES_QUERY_SEGMENT_SIZE:200} 执行\nsh {path}/bin/oapServiceInit.sh 将es接入 skywalking bridge docker network connect skywalking es-skywalking 修改skywalking配置文件 修改/root/skywalking/oap/config/application.yml:\nstorage: elasticsearch: #nameSpace: ${SW_NAMESPACE:\u0026quot;\u0026quot;} clusterNodes: ${SW_STORAGE_ES_CLUSTER_NODES:es-skywalking:9200} #user: ${SW_ES_USER:\u0026quot;\u0026quot;} #password: ${SW_ES_PASSWORD:\u0026quot;\u0026quot;} indexShardsNumber: ${SW_STORAGE_ES_INDEX_SHARDS_NUMBER:2} indexReplicasNumber: ${SW_STORAGE_ES_INDEX_REPLICAS_NUMBER:0} # Batch process setting, refer to https://www.elastic.co/guide/en/elasticsearch/client/java-api/5.5/java-docs-bulk-processor.html bulkActions: ${SW_STORAGE_ES_BULK_ACTIONS:2000} # Execute the bulk every 2000 requests bulkSize: ${SW_STORAGE_ES_BULK_SIZE:20} # flush the bulk every 20mb flushInterval: ${SW_STORAGE_ES_FLUSH_INTERVAL:10} # flush the bulk every 10 seconds whatever the number of requests concurrentRequests: ${SW_STORAGE_ES_CONCURRENT_REQUESTS:2} # the number of concurrent requests metadataQueryMaxSize: ${SW_STORAGE_ES_QUERY_MAX_SIZE:5000} segmentQueryMaxSize: ${SW_STORAGE_ES_QUERY_SEGMENT_SIZE:200} 重启skywalking-oap docker restart skywalking-oap plugin使用 apm-toolkit-logback-1.x 项目pom文件引入 \u0026lt;dependency\u0026gt; \u0026lt;groupId\u0026gt;org.apache.skywalking\u0026lt;/groupId\u0026gt; \u0026lt;artifactId\u0026gt;apm-toolkit-logback-1.x\u0026lt;/artifactId\u0026gt; \u0026lt;version\u0026gt;6.1.0\u0026lt;/version\u0026gt; \u0026lt;/dependency\u0026gt; 编辑logback.yml \u0026lt;appender name=\u0026quot;console\u0026quot; class=\u0026quot;ch.qos.logback.core.ConsoleAppender\u0026quot;\u0026gt; \u0026lt;encoder class=\u0026quot;ch.qos.logback.core.encoder.LayoutWrappingEncoder\u0026quot;\u0026gt; \u0026lt;layout class=\u0026quot;org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\u0026quot;\u0026gt; \u0026lt;Pattern\u0026gt;%d{yyyy-MM-dd HH:mm:ss.SSS} [%tid] [%thread] %-5level %logger{36} -%msg%n\u0026lt;/Pattern\u0026gt; \u0026lt;/layout\u0026gt; \u0026lt;/encoder\u0026gt; \u0026lt;/appender\u0026gt; ","id":21,"section":"posts","summary":"运行 单机运行 下载最新release包 解压,sh ${skywalkingpath}/bin/startup.sh 在springboot启动命令前加入启动参数 -javaagent:${path}/skywalking-agent.jar #path为skywalking解压的agen","tags":["java","skywalking","spring cloud"],"title":"skywalking使用指南","uri":"https://fhxisdog.github.io/2019/05/skywalking_guide/","year":"2019"},{"content":"装了manjaro的i3wm桌面环境后,发现外界显示器无法显示。搜索资料后发现解决方法如下。\n查看设备输入 终端输入:\nxrandr 可以看到结果如下,其中 eDP1是我笔记本自带的屏幕,HDMI1就是我外接的显示器。\nScreen 0: minimum 8 x 8, current 1920 x 1080, maximum 32767 x 32767 eDP1 connected primary 1920x1080+0+0 (normal left inverted right x axis y axis) 310mm x 170mm 1920x1080 60.05*+ 59.93 1680x1050 59.88 1400x1050 59.98 1600x900 60.00 59.95 59.82 1280x1024 60.02 1400x900 59.96 59.88 1280x960 60.00 1368x768 60.00 59.88 59.85 1280x800 59.81 59.91 1280x720 59.86 60.00 59.74 1024x768 60.00 1024x576 60.00 59.90 59.82 960x540 60.00 59.63 59.82 800x600 60.32 56.25 864x486 60.00 59.92 59.57 640x480 59.94 720x405 59.51 60.00 58.99 640x360 59.84 59.32 60.00 DP1 disconnected (normal left inverted right x axis y axis) HDMI1 connected (normal left inverted right x axis y axis) 1920x1080 60.00 + 50.00 59.94 1920x1080i 60.00 50.00 59.94 1600x900 60.00 1280x1024 75.02 60.02 1152x864 75.00 1280x720 60.00 50.00 59.94 1024x768 75.03 60.00 800x600 75.00 60.32 720x576 50.00 720x576i 50.00 720x480 60.00 59.94 720x480i 60.00 59.94 640x480 75.00 60.00 59.94 720x400 70.08 HDMI2 disconnected (normal left inverted right x axis y axis) VIRTUAL1 disconnected (normal left inverted right x axis y axis) 双屏设置 设置主屏幕 xrandr --auto --output eDP1 --primary ---auto:可以自动启用关闭的屏幕。 ---primary:设置主屏。 复制模式 xrandr --auto --output eDP1 --pos 0x0 --mode 1920x1080 --output HDMI1 --same-as eDP1 ---pos:起始位置,x。 ---same-as:与eDP1输出保持一致。 扩展模式 xrandr --auto --output eDP1 --pos 0x0 --mode 1920x1080 --primary --output HDMI1 --mode 1024x768 --right-of eDP1 --right-of:HDMI1的起始位置在eDP1的右边。 命令的结果就是HDM1 会在 eDP1 的右边,eDP1 为主屏,另外位置的参数还有 \u0026ndash;left-of, \u0026ndash;above, \u0026ndash;below 等。 如果需要自定义两个屏幕的位置, 可以通过计算每个屏幕的分辨率, 用 \u0026ndash;pos 参数来指定每个屏幕显示的位置。\n 单屏模式 xrandr --output eDP1 --pos 0x0 --mode 1920x1080 --primary --output VGA1 --off --off:关闭某个屏幕. 自定义模式 另外屏幕的旋转, 镜像和缩放可以分别使用 --rotate, --reflect 和 --scale 参数来实现. 永久保存 如果需要永久保存配置,可以通过更改/etc/X11/xorg.conf或者/etc/X11/xorg.conf.d/****进行保存。 常用命令 外接显示器(\u0026ndash;auto:最高分辨率),与笔记本液晶屏幕显示同样内容(克隆) xrandr --output HDMI1 --same-as eDP1 auto 打开外接显示器(分辨率为1920x1080),与笔记本液晶屏幕显示同样内容(克隆) xrandr --output HDMI1 --same-as eDMP1 --mode 1920x1020 打开外接显示器(\u0026ndash;auto:最高分辨率),设置为右侧扩展屏幕 xrandr --output HDMI1 --right-of eDP1 --auto 关闭外接显示器 xrandr --output HDMI1 --off 打开外接显示器,同时关闭笔记本液晶屏幕(只用外接显示器工作) xrandr --output HDMI1 --auto --output eDP1 --off 关闭外接显示器,同时打开笔记本液晶屏幕 (只用笔记本液晶屏) xrandr --output HDMI1 --off --output eDP1 --auto 参考连接 Arch Wiki\n使用Xrandr设置单屏和双屏\n","id":22,"section":"posts","summary":"装了manjaro的i3wm桌面环境后,发现外界显示器无法显示。搜索资料后发现解决方法如下。 查看设备输入 终端输入: xrandr 可以看到结果如下,其中 e","tags":["manjaro","linux","i3wm"],"title":"Manjaro I3wm接入外接显示器不显示","uri":"https://fhxisdog.github.io/2019/05/manjaro-i3wm_external_mirror/","year":"2019"},{"content":"manjaro的i3wm版本默认的mod键是super(就是windows键),为了拯救我的小拇指,所以使用xmodmap将super键和CapsLock互换位置。\n 创建自己的映射表 xmodmap -pke \u0026gt; ~/.Xmodmap 创建映射 编辑~/.Xmodmap,在底部加入:\nremove Lock = Caps_Lock remove Mod4 = Super_L keysym Super_L = Caps_Lock keysym Caps_Lock = Super_L add Lock = Caps_Lock add Mod4 = Super_L 测试: xmodmap ~/.Xmodmap 设置开机启动: 默认系统读取的就是$HOME/.Xmodmap\n参考连接 arch-wiki\nhttps://www.linuxidc.com/Linux/2013-05/84542.htm\n","id":23,"section":"posts","summary":"manjaro的i3wm版本默认的mod键是super(就是windows键),为了拯救我的小拇指,所以使用xmodmap将super键和C","tags":["modmap","linux","i3wm"],"title":"Linux使用modmap替换super和CapsLock位置","uri":"https://fhxisdog.github.io/2019/05/linux_use_modmap_switch_super_capslock/","year":"2019"},{"content":"问题描述 使用pacmanfm在地址栏输入:smb://fhxisdog-win/temp访问windows共享文件夹,提示NO FILE OR DIRECTIONARY\n在命令行使用命令:smbclient -L fhxisdog-win -U% 提示:NT_STATUS_ACCESS_DENIED\n使用:smbclient -L fhxisdog-win -m SMB2可以访问\n解决方法 编辑 /etc/samba/smb.conf:在[gloabl]下加入:\nmin protocOl = SMB2 max protocol = SMB2 client min protocol = SMB2 client max protocol = SMB2 smb简介 服务器消息块(Server Message Block,缩写为SMB),又称网络文件共享系统(Common Internet File System,缩写为CIFS, /ˈsɪfs/),一种应用层网络传输协议,由微软开发,主要功能是使网络上的机器能够共享计算机文件、打印机、串行端口和通讯等资源。它也提供经认证的行程间通信机能。它主要用在装有Microsoft Windows的机器上,在这样的机器上被称为Microsoft Windows Network。\n 经过Unix服务器厂商重新开发后,它可以用于连接Unix服务器和Windows客户机,执行打印和文件共享等任务。\n 与功能类似的网络文件系统(NFS)相比,NFS的消息格式是固定长度,而CIFS的消息格式大多数是可变长度,这增加了协议的复杂性。CIFS消息一般使用NetBIOS或TCP协议发送,分别使用不同的端口139或445,当前倾向于使用445端口。CIFS的消息包括一个信头(32字节)和消息体(1个或多个,可变长)。\n 参考连接:wiki\n","id":24,"section":"posts","summary":"问题描述 使用pacmanfm在地址栏输入:smb://fhxisdog-win/temp访问windows共享文件夹,提示NO FILE OR DIRECTIONARY 在命令行","tags":["smb","linux"],"title":"Smb无法连接windows共享文件夹","uri":"https://fhxisdog.github.io/2019/05/smb_connect_windows/","year":"2019"},{"content":"本文所用到的工具 shadowsocks-libev privoxy gfwlist2privoxy shadowsocks-libev安装配置 下载安装:sudo pacman -S shadowsocks-libev\n配置:\n 在/etc/shadowsocks/下新建config.json文件 编辑config.json: { \u0026#34;server\u0026#34;:\u0026#34;xxxxxx\u0026#34;, #服务器IP \u0026#34;server_port\u0026#34;:xxx, #服务器端口 \u0026#34;local_address\u0026#34;:\u0026#34;127.0.0.1\u0026#34;, #本地监听IP \u0026#34;local_port\u0026#34;:1080, #本地监听端口 \u0026#34;password\u0026#34;:\u0026#34;xxx\u0026#34;, #密码 \u0026#34;method\u0026#34;:\u0026#34;xxx\u0026#34;, #加密方式 \u0026#34;fast_open\u0026#34;:false, # tcp_fastopen \u0026#34;timeout\u0026#34;:1000, #超时时间 \u0026#34;workers\u0026#34;:1 #workder进程数 } 启动ss-local: ss-local -c /etc/shadowsocks/config.json 测试:curl -x socks5://127.0.0.1:1080 http://www.google.com 设置守护进程启动:systemctl start shadowsocks-libev@config ** 注意:这里@config是指 /etc/shadowsocks/下面的配置文件 ** 设置开机自启动: systemctl enable shadows-libev@config privoxy+gfwlist2privoxy安装配置 下载privoxy:sudo pacman -S privoxy\n获取 gfwlist2privoxy 脚本:curl -skL https://raw.github.com/zfl9/gfwlist2privoxy/master/gfwlist2privoxy -o gfwlist2privoxy\n生成 gfwlist.action 文件: bash gfwlist2privoxy '127.0.0.1:1080' \n拷贝至 privoxy 配置目录:cp -af gfwlist.action /etc/privoxy/\n加载 gfwlist.action 文件 echo 'actionsfile gfwlist.action' \u0026gt;\u0026gt; /etc/privoxy/config\n启动privoxy:sysemctl start privoxy\n设置开机自启动: systemctl enable privoxy\n测试:\n 设置环境变量: # 8118 为privoxy默认监听端口 export http_proxy = http:127.0.0.1:8118 export https_proxy = http:127.0.0.1:8118 curl测试: curl www.google.com 设置环境变量 编辑 /etc/prifile,加入以下两行:\nexport http_proxy = http:127.0.0.1:8118 export https_proxy = http:127.0.0.1:8118 重启,打开浏览器验证\n","id":25,"section":"posts","summary":"本文所用到的工具 shadowsocks-libev privoxy gfwlist2privoxy shadowsocks-libev安装配置 下载安装:sudo pacman -S shadowsocks-libev 配置: 在/etc/shadowsocks/下新建co","tags":["linux","shadowsocks","vpn"],"title":"Shadowsocks-libev+privoxy+gfwlist2privoxy","uri":"https://fhxisdog.github.io/2019/05/shadowsocks-libev-privoxy/","year":"2019"},{"content":"转自:https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/\nJIT 简介 JIT 是 just in time 的缩写, 也就是即时编译编译器。使用即时编译器技术,能够加速 Java 程序的执行速度。下面,就对该编译器技术做个简单的讲解。\n首先,我们大家都知道,通常通过 javac 将程序源代码编译,转换成 java 字节码,JVM 通过解释字节码将其翻译成对应的机器指令,逐条读入,逐条解释翻译。很显然,经过解释执行,其执行速度必然会比可执行的二进制字节码程序慢很多。为了提高执行速度,引入了 JIT 技术。\n在运行时 JIT 会把翻译过的机器码保存起来,以备下次使用,因此从理论上来说,采用该 JIT 技术可以接近以前纯编译技术。下面我们看看,JIT 的工作过程。\nJIT 编译过程 当 JIT 编译启用时(默认是启用的),JVM 读入.class 文件解释后,将其发给 JIT 编译器。JIT 编译器将字节码编译成本机机器代码,下图展示了该过程。\n图 1. JIT 工作原理图\nHot Spot 编译 当 JVM 执行代码时,它并不立即开始编译代码。这主要有两个原因:\n首先,如果这段代码本身在将来只会被执行一次,那么从本质上看,编译就是在浪费精力。因为将代码翻译成 java 字节码相对于编译这段代码并执行代码来说,要快很多。\n当然,如果一段代码频繁的调用方法,或是一个循环,也就是这段代码被多次执行,那么编译就非常值得了。因此,编译器具有的这种权衡能力会首先执行解释后的代码,然后再去分辨哪些方法会被频繁调用来保证其本身的编译。其实说简单点,就是 JIT 在起作用,我们知道,对于 Java 代码,刚开始都是被编译器编译成字节码文件,然后字节码文件会被交由 JVM 解释执行,所以可以说 Java 本身是一种半编译半解释执行的语言。Hot Spot VM 采用了 JIT compile 技术,将运行频率很高的字节码直接编译为机器指令执行以提高性能,所以当字节码被 JIT 编译为机器码的时候,要说它是编译执行的也可以。也就是说,运行时,部分代码可能由 JIT 翻译为目标机器指令(以 method 为翻译单位,还会保存起来,第二次执行就不用翻译了)直接执行。\n第二个原因是最优化,当 JVM 执行某一方法或遍历循环的次数越多,就会更加了解代码结构,那么 JVM 在编译代码的时候就做出相应的优化。\n我们将在后面讲解这些优化策略,这里,先举一个简单的例子:我们知道 equals() 这个方法存在于每一个 Java Object 中(因为是从 Object class 继承而来)而且经常被覆写。当解释器遇到 b = obj1.equals(obj2) 这样一句代码,它则会查询 obj1 的类型从而得知到底运行哪一个 equals() 方法。而这个动态查询的过程从某种程度上说是很耗时的。\n寄存器和主存 其中一个最重要的优化策略是编译器可以决定何时从主存取值,何时向寄存器存值。考虑下面这段代码:\n清单 1. 主存 or 寄存器测试代码\npublic class RegisterTest { private int sum; public void calculateSum(int n) { for (int i = 0; i \u0026lt; n; ++i) { sum += i; } } } 在某些时刻,sum 变量居于主存之中,但是从主存中检索值是开销很大的操作,需要多次循环才可以完成操作。正如上面的例子,如果循环的每一次都是从主存取值,性能是非常低的。相反,编译器加载一个寄存器给 sum 并赋予其初始值,利用寄存器里的值来执行循环,并将最终的结果从寄存器返回给主存。这样的优化策略则是非常高效的。但是线程的同步对于这种操作来说是至关重要的,因为一个线程无法得知另一个线程所使用的寄存器里变量的值,线程同步可以很好的解决这一问题,有关于线程同步的知识,我们将在后续文章中进行讲解。\n寄存器的使用是编译器的一个非常普遍的优化。\n回到之前的例子,JVM 注意到每次运行代码时,obj1 都是 java.lang.String 这种类型,那么 JVM 生成的被编译后的代码则是直接调用 String.equals() 方法。这样代码的执行将变得非常快,因为不仅它是被编译过的,而且它会跳过查找该调用哪个方法的步骤。\n当然过程并不是上面所述这样简单,如果下次执行代码时,obj1 不再是 String 类型了,JVM 将不得不再生成新的字节码。尽管如此,之后执行的过程中,还是会变的更快,因为同样会跳过查找该调用哪个方法的步骤。这种优化只会在代码被运行和观察一段时间之后发生。这也就是为什么 JIT 编译器不会理解编译代码而是选择等待然后再去编译某些代码片段的第二个原因。\n初级调优:客户模式或服务器模式 JIT 编译器在运行程序时有两种编译模式可以选择,并且其会在运行时决定使用哪一种以达到最优性能。这两种编译模式的命名源自于命令行参数(eg: -client 或者 -server)。JVM Server 模式与 client 模式启动,最主要的差别在于:-server 模式启动时,速度较慢,但是一旦运行起来后,性能将会有很大的提升。原因是:当虚拟机运行在-client 模式的时候,使用的是一个代号为 C1 的轻量级编译器,而-server 模式启动的虚拟机采用相对重量级代号为 C2 的编译器。C2 比 C1 编译器编译的相对彻底,服务起来之后,性能更高。\n通过 java -version 命令行可以直接查看当前系统使用的是 client 还是 server 模式。例如:\n图 2. 查看编译模式\n中级编译器调优 大多数情况下,优化编译器其实只是选择合适的 JVM 以及为目标主机选择合适的编译器(-cient,-server 或是-xx:+TieredCompilation)。多层编译经常是长时运行应用程序的最佳选择,短暂应用程序则选择毫秒级性能的 client 编译器。\n优化代码缓存 当 JVM 编译代码时,它会将汇编指令集保存在代码缓存。代码缓存具有固定的大小,并且一旦它被填满,JVM 则不能再编译更多的代码。\n我们可以很容易地看到如果代码缓存很小所具有的潜在问题。有些热点代码将会被编译,而其他的则不会被编译,这个应用程序将会以运行大量的解释代码来结束。\n这是当使用 client 编译器模式或分层编译时很频繁的一个问题。当使用普通 server 编译器模式时,编译合格的类的数量将被填入代码缓存,通常只有少量的类会被编译。但是当使用 client 编译器模式时,编译合格的类的数量将会高很多。\n在 Java 7 版本,分层编译默认的代码缓存大小经常是不够的,需要经常提高代码缓存大小。大型项目若使用 client 编译器模式,则也需要提高代码缓存大小。\n现在并没有一个好的机制可以确定一个特定的应用到底需要多大的代码缓存。因此,当需要提高代码缓存时,这将是一种凑巧的操作,一个通常的做法是将代码缓存变成默认大小的两倍或四倍。\n可以通过 –XX:ReservedCodeCacheSize=Nflag(N 就是之前提到的默认大小)来最大化代码缓存大小。代码缓存的管理类似于 JVM 中的内存管理:有一个初始大小(用-XX:InitialCodeCacheSize=N 来声明)。代码缓存的大小从初始大小开始,随着缓存被填满而逐渐扩大。代码缓存的初始大小是基于芯片架构(例如 Intel 系列机器,client 编译器模式下代码缓存大小起始于 160KB,server 编译器模式下代码缓存大小则起始于 2496KB)以及使用的编译器的。重定义代码缓存的大小并不会真正影响性能,所以设置 ReservedCodeCacheSize 的大小一般是必要的。\n再者,如果 JVM 是 32 位的,那么运行过程大小不能超过 4GB。这包括了 Java 堆,JVM 自身所有的代码空间(包括其本身的库和线程栈),应用程序分配的任何的本地内存,当然还有代码缓存。\n所以说代码缓存并不是无限的,很多时候需要为大型应用程序来调优(或者甚至是使用分层编译的中型应用程序)。比如 64 位机器,为代码缓存设置一个很大的值并不会对应用程序本身造成影响,应用程序并不会内存溢出,这些额外的内存预定一般都是被操作系统所接受的。\n编译阈值 在 JVM 中,编译是基于两个计数器的:一个是方法被调用的次数,另一个是方法中循环被回弹执行的次数。回弹可以有效的被认为是循环被执行完成的次数,不仅因为它是循环的结尾,也可能是因为它执行到了一个分支语句,例如 continue。\n当 JVM 执行一个 Java 方法,它会检查这两个计数器的总和以决定这个方法是否有资格被编译。如果有,则这个方法将排队等待编译。这种编译形式并没有一个官方的名字,但是一般被叫做标准编译。\n但是如果方法里有一个很长的循环或者是一个永远都不会退出并提供了所有逻辑的程序会怎么样呢?这种情况下,JVM 需要编译循环而并不等待方法被调用。所以每执行完一次循环,分支计数器都会自增和自检。如果分支计数器计数超出其自身阈值,那么这个循环(并不是整个方法)将具有被编译资格。\n这种编译叫做栈上替换(OSR),因为即使循环被编译了,这也是不够的:JVM 必须有能力当循环正在运行时,开始执行此循环已被编译的版本。换句话说,当循环的代码被编译完成,若 JVM 替换了代码(前栈),那么循环的下个迭代执行最新的被编译版本则会更加快。\n标准编译是被-XX:CompileThreshold=Nflag 的值所触发。Client 编译器模式下,N 默认的值 1500,而 Server 编译器模式下,N 默认的值则是 10000。改变 CompileThreshold 标志的值将会使编译器相对正常情况下提前(或推迟)编译代码。在性能领域,改变 CompileThreshold 标志是很被推荐且流行的方法。事实上,您可能知道 Java 基准经常使用此标志(比如:对于很多 server 编译器来说,经常在经过 8000 次迭代后改变次标志)。\n我们已经知道 client 编译器和 server 编译器在最终的性能上有很大的差别,很大程度上是因为编译器在编译一个特定的方法时,对于两种编译器可用的信息并不一样。降低编译阈值,尤其是对于 server 编译器,承担着不能使应用程序运行达到最佳性能的风险,但是经过测试应用程序我们也发现,将阈值从 8000 变成 10000,其实有着非常小的区别和影响。\n检查编译过程 中级优化的最后一点其实并不是优化本身,而是它们并不能提高应用程序的性能。它们是 JVM(以及其他工具)的各个标志,并可以给出编译工作的可见性。它们中最重要的就是\u0026ndash;XX:+PrintCompilation(默认状态下是 false)。\n如果 PrintCompilation 被启用,每次一个方法(或循环)被编译,JVM 都会打印出刚刚编译过的相关信息。不同的 Java 版本输出形式不一样,我们这里所说的是基于 Java 7 版本的。\n编译日志中大部分的行信息都是下面的形式:\n清单 2. 日志形式\ntimestamp compilation_id attributes (tiered_level) method_name size depot 这里 timestamp 是编译完成时的时间戳,compilation_id 是一个内部的任务 ID,且通常情况下这个数字是单调递增的,但有时候对于 server 编译器(或任何增加编译阈值的时候),您可能会看到失序的编译 ID。这表明编译线程之间有些快有些慢,但请不要随意推断认为是某个编译器任务莫名其妙的非常慢。\n用 jstat 命令检查编译 要想看到编译日志,则需要程序以-XX:+PrintCompilation flag 启动。如果程序启动时没有 flag,您可以通过 jstat 命令得到有限的可见性信息。\nJstat 有两个选项可以提供编译器信息。其中,-compile 选项提供总共有多少方法被编译的总结信息(下面 6006 是要被检查的程序的进程 ID):\n清单 3 进程详情\n% jstat -compiler 6006 CompiledFailedInvalid TimeFailedTypeFailedMethod 206 0 0 1.97 0 注意,这里也列出了编译失败的方法的个数信息,以及编译失败的最后一个方法的名称。\n另一种选择,您可以使用-printcompilation 选项得到最后一个被编译的方法的编译信息。因为 jstat 命令有一个参数选项用来重复其操作,您可以观察每一次方法被编译的情况。举个例子:\nJstat 对 6006 号 ID 进程每 1000 毫秒执行一次: %jstat –printcompilation 6006 1000,具体的输出信息在此不再描述。\n高级编译器调优 这一节我们将介绍编译工作剩下的细节,并且过程中我们会探讨一些额外的调优策略。调优的存在很大程度上帮助了 JVM 工程师诊断 JVM 自身的行为。如果您对编译器的工作原理很感兴趣,这一节您一定会喜欢。\n编译线程 从前文中我们知道,当一个方法(或循环)拥有编译资格时,它就会排队并等待编译。这个队列是由一个或很多个后台线程组成。这也就是说编译是一个异步的过程。它允许程序在代码正在编译时被继续执行。如果一个方法被标准编译方式所编译,那么下一个方法调用则会执行已编译的方法。如果一个循环被栈上替换方式所编译,那么下一次循环迭代则会执行新编译的代码。\n这些队列并不会严格的遵守先进先出原则:哪一个方法的调用计数器计数更高,哪一个就拥有优先权。所以即使当一个程序开始执行,并且有大量的代码需要编译,这个优先权顺序将帮助并保证最重要的代码被优先编译(这也是为什么编译 ID 在 PrintComilation 的输出结果中有时会失序的另一个原因)。\n当使用 client 编译器时,JVM 启动一个编译线程,而 server 编译器有两个这样的线程。当分层编译生效时,JVM 会基于某些复杂方程式默认启动多个 client 和 server 线程,涉及双日志在目标平台上的 CPU 数量。如下图所示:\n分层编译下 C1 和 C2 编译器线程默认数量:\n图 3. C1 和 C2 编译器默认数量\n编译器线程的数量可以通过-XX:CICompilerCount=N flag 进行调节设置。这个数量是 JVM 将要执行队列所用的线程总数。对于分层编译,三分之一的(至少一个)线程被用于执行 client 编译器队列,剩下的(也是至少一个)被用来执行 server 编译器队列。\n在何时我们应该考虑调整这个值呢?如果一个程序被运行在单 CPU 机器上,那么只有一个编译线程会更好一些:因为对于某个线程来说,其对 CPU 的使用是有限的,并且在很多情况下越少的线程竞争资源会使其运行性能更高。然而,这个优势仅仅局限于初始预热阶段,之后,这些具有编译资格的方法并不会真的引起 CPU 争用。当一个股票批处理应用程序运行在单 CPU 机器上并且编译器线程被限制成只有一个,那么最初的计算过程将比一般情况下快 10%(因为它没有被其他线程进行 CPU 争用)。迭代运行的次数越多,最初的性能收益就相对越少,直到所有的热点方法被编译完性能收益也随之终止。\n结束语 本文详细介绍了 JIT 编译器的工作原理。从优化的角度讲,最简单的选择就是使用 server 编译器的分层编译技术,这将解决大约 90%左右的与编译器直接相关的性能问题。最后,请保证代码缓存的大小设置的足够大,这样编译器将会提供最高的编译性能。\n","id":26,"section":"posts","summary":"转自:https://www.ibm.com/developerworks/cn/java/j-lo-just-in-time/ JIT 简介 JIT 是 just","tags":["jvm","java"],"title":"深入浅出 JIT 编译器","uri":"https://fhxisdog.github.io/2019/04/%E6%B7%B1%E5%85%A5%E6%B5%85%E5%87%BA-jit-%E7%BC%96%E8%AF%91%E5%99%A8/","year":"2019"}],"tags":[{"title":"android","uri":"https://fhxisdog.github.io/tags/android/"},{"title":"Arch","uri":"https://fhxisdog.github.io/tags/arch/"},{"title":"docker","uri":"https://fhxisdog.github.io/tags/docker/"},{"title":"emacs","uri":"https://fhxisdog.github.io/tags/emacs/"},{"title":"flutter","uri":"https://fhxisdog.github.io/tags/flutter/"},{"title":"Git","uri":"https://fhxisdog.github.io/tags/git/"},{"title":"i3wm","uri":"https://fhxisdog.github.io/tags/i3wm/"},{"title":"java","uri":"https://fhxisdog.github.io/tags/java/"},{"title":"jvm","uri":"https://fhxisdog.github.io/tags/jvm/"},{"title":"linux","uri":"https://fhxisdog.github.io/tags/linux/"},{"title":"manjaro","uri":"https://fhxisdog.github.io/tags/manjaro/"},{"title":"maven","uri":"https://fhxisdog.github.io/tags/maven/"},{"title":"modmap","uri":"https://fhxisdog.github.io/tags/modmap/"},{"title":"nacos","uri":"https://fhxisdog.github.io/tags/nacos/"},{"title":"orther","uri":"https://fhxisdog.github.io/tags/orther/"},{"title":"postman","uri":"https://fhxisdog.github.io/tags/postman/"},{"title":"shadowsocks","uri":"https://fhxisdog.github.io/tags/shadowsocks/"},{"title":"skill","uri":"https://fhxisdog.github.io/tags/skill/"},{"title":"skywalking","uri":"https://fhxisdog.github.io/tags/skywalking/"},{"title":"smb","uri":"https://fhxisdog.github.io/tags/smb/"},{"title":"smicroservices","uri":"https://fhxisdog.github.io/tags/smicroservices/"},{"title":"spring boot","uri":"https://fhxisdog.github.io/tags/spring-boot/"},{"title":"spring cloud","uri":"https://fhxisdog.github.io/tags/spring-cloud/"},{"title":"tips","uri":"https://fhxisdog.github.io/tags/tips/"},{"title":"vpn","uri":"https://fhxisdog.github.io/tags/vpn/"},{"title":"vue","uri":"https://fhxisdog.github.io/tags/vue/"}]}