<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Coding Life</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://codinglife.tech/"/>
  <updated>2020-12-28T09:38:16.844Z</updated>
  <id>https://codinglife.tech/</id>
  
  <author>
    <name>Max Peng</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Git Merge Commits</title>
    <link href="https://codinglife.tech/2020/12/git-merge-commits/"/>
    <id>https://codinglife.tech/2020/12/git-merge-commits/</id>
    <published>2020-12-09T06:25:30.000Z</published>
    <updated>2020-12-28T09:38:16.844Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/2020-12-09/git-merge-commits.png" alt="Git Branch &amp; Commits"></p><p>平时在 Git 的使用过程中，遇到了好些场景需要把多个 Commit 合并成一个 Commit。经过在网上一通搜索，大家也是仁者见仁，智者见智。在本文中总结了几种常见场景，如果大家还遇到过其他的场景，也欢迎留言讨论。</p><a id="more"></a><h2 id="场景一：把最新的改动合并本地还未提交到远端的-commit-上"><a href="#场景一：把最新的改动合并本地还未提交到远端的-commit-上" class="headerlink" title="场景一：把最新的改动合并本地还未提交到远端的 commit 上"></a>场景一：把最新的改动合并本地还未提交到远端的 commit 上</h2><p>先来热一下身，看一个最简单的场景。C1、C2 已经被提交到远端仓库，C3 已提交到本地仓库，现在想要把还未提交到本地的 C4 和 C3 合并。</p><p><img src="/images/2020-12-09/%E5%9C%BA%E6%99%AF%E4%B8%80.png" alt="场景一"></p><p>步骤：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">git add .</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 按照提示修改 Commit message，如果不用修改，可以直接加上 --no-edit</span><br><span class="line">git commit --amend</span><br></pre></td></tr></table></figure><h2 id="场景二：合并本地还未提交到远端的-commit"><a href="#场景二：合并本地还未提交到远端的-commit" class="headerlink" title="场景二：合并本地还未提交到远端的 commit"></a>场景二：合并本地还未提交到远端的 commit</h2><p>C1、C2 已经被提交到远端仓库，C3、C4 已提交到本地仓库，现在想要把 C3 和 C4 合并。</p><p><img src="/images/2020-12-09/%E5%9C%BA%E6%99%AF%E4%BA%8C.png" alt="场景二"></p><h3 id="方法一：使用-rebase-i，基于-SHA-值"><a href="#方法一：使用-rebase-i，基于-SHA-值" class="headerlink" title="方法一：使用 rebase -i，基于 SHA 值"></a>方法一：使用 <code>rebase -i</code>，基于 SHA 值</h3><p>步骤：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> f944f36e 为 C2 的 SHA 值</span><br><span class="line">git rebase -i f944f36e</span><br></pre></td></tr></table></figure><p>执行 <code>rebase -i</code> 操作后会看到类似如下的界面（<strong>注意这里 Commit 的顺序</strong>）：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">  1 pick 7d1cc37 C3</span><br><span class="line">  2 pick 34f88df C4</span><br><span class="line">  3 </span><br><span class="line">  4 # Rebase f944f36..34f88df onto f944f36 (2 commands)</span><br><span class="line">  5 #</span><br><span class="line">  6 # Commands:</span><br><span class="line">  7 # p, pick &lt;commit&gt; = use commit</span><br><span class="line">  8 # r, reword &lt;commit&gt; = use commit, but edit the commit message</span><br><span class="line">  9 # e, edit &lt;commit&gt; = use commit, but stop for amending</span><br><span class="line"> 10 # s, squash &lt;commit&gt; = use commit, but meld into previous commit</span><br><span class="line"> 11 # f, fixup &lt;commit&gt; = like "squash", but discard this commit's log message</span><br><span class="line"> 12 # x, exec &lt;command&gt; = run command (the rest of the line) using shell</span><br><span class="line"> 13 # b, break = stop here (continue rebase later with 'git rebase --continue')</span><br><span class="line"> 14 # d, drop &lt;commit&gt; = remove commit</span><br><span class="line"> 15 # l, label &lt;label&gt; = label current HEAD with a name</span><br><span class="line"> 16 # t, reset &lt;label&gt; = reset HEAD to a label</span><br><span class="line"> 17 # m, merge [-C &lt;commit&gt; | -c &lt;commit&gt;] &lt;label&gt; [# &lt;oneline&gt;]</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>然后根据自己的需求对每个 Commit 选择对应的操作，这里我们对 C3 选择 <code>pick</code>，对 C4 以及后面的 Commit（如果有的话）选择 <code>squash</code> 或者 <code>fixup</code> 都可以，然后保存退出 vi 模式即可，C3 后面的 Commit 都会被合并到 C3 中。这里通过灵活运用不同的操作，可以改变提交顺序，提交信息，合并提交，放弃提交等操作。</p><h3 id="方法二：使用-rebase-i，基于-HEAD-的相对位置"><a href="#方法二：使用-rebase-i，基于-HEAD-的相对位置" class="headerlink" title="方法二：使用 rebase -i，基于 HEAD 的相对位置"></a>方法二：使用 <code>rebase -i</code>，基于 HEAD 的相对位置</h3><p>该方法是基于 HEAD 的相对位置，适合合并少量的 Commit。如果想要看到2个 Commit，就写 <code>HEAD~2</code>。</p><p>步骤：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git rebase -i HEAD~2</span><br></pre></td></tr></table></figure><p>其他的步骤和方法一相同。方法一和方法二的详细解释可以参考这个<a href="https://stackoverflow.com/a/5189600/3049524" target="_blank" rel="noopener">回答</a>。</p><h3 id="方法三：使用-Soft-Reset-和-Commit"><a href="#方法三：使用-Soft-Reset-和-Commit" class="headerlink" title="方法三：使用 Soft Reset 和 Commit"></a>方法三：使用 Soft Reset 和 Commit</h3><p>步骤：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git reset --soft HEAD~2</span><br><span class="line">git commit -m 'your message'</span><br></pre></td></tr></table></figure><p>如果想要复用之前的提交信息，可以用这个命令 <code>git commit --edit -m&quot;$(git log --format=%B --reverse HEAD..HEAD@{1})&quot;</code>。更详细的解释，可以参考这个<a href="https://stackoverflow.com/a/5201642/3049524" target="_blank" rel="noopener">回答</a>。</p><h3 id="方法四：使用-Hard-Reset-和-Merge-Squash"><a href="#方法四：使用-Hard-Reset-和-Merge-Squash" class="headerlink" title="方法四：使用 Hard Reset 和 Merge Squash"></a>方法四：使用 Hard Reset 和 Merge Squash</h3><blockquote><p><strong>注意：</strong> 在采用该方式之前，先确保所有的改动都已经提交，因为 Hard Reset 会直接丢掉 Staged 和 Unstaged 的改动。</p></blockquote><p>步骤：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> Reset the current branch to the commit just before the last 2:</span><br><span class="line">git reset --hard HEAD~2</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> HEAD@&#123;1&#125; is where the branch was just before the previous command.</span><br><span class="line"><span class="meta">#</span> This command sets the state of the index to be as it would just</span><br><span class="line"><span class="meta">#</span> after a merge from that commit:</span><br><span class="line">git merge --squash HEAD@&#123;1&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> Commit those squashed changes.  The commit message will be helpfully</span><br><span class="line"><span class="meta">#</span> prepopulated with the commit messages of all the squashed commits:</span><br><span class="line">git commit</span><br></pre></td></tr></table></figure><p>在执行 Merge 的时候可能会遇到一个错误：<code>fatal: refusing to merge unrelated histories</code>。这是因为默认情况下，Git 是不允许在不同的项目之间执行 Merge 动作的。但是默认不允许不代表不能做，可以通过加上参数 <code>--allow-unrelated-histories</code> 强制执行。具体的解释可以参考这个<a href="https://stackoverflow.com/a/37938036/3049524" target="_blank" rel="noopener">回答</a>。</p><p>这种方式的好处是，默认会自动带出之前的详细提交信息。如果最后需要重写提交信息，也可以直接 <code>git commit -m &#39;your message&#39;</code>。更详细的解释，可以参考这个<a href="https://stackoverflow.com/a/5190323/3049524" target="_blank" rel="noopener">回答</a>。</p><h2 id="场景三：合并本地提交和已经-Push-到远端的提交"><a href="#场景三：合并本地提交和已经-Push-到远端的提交" class="headerlink" title="场景三：合并本地提交和已经 Push 到远端的提交"></a>场景三：合并本地提交和已经 Push 到远端的提交</h2><p>其实该场景和场景二并没有太大的区别，唯一需要特别注意的是 Force Push（<code>git push -f</code>），如果 Force Push 不成功，可以检查一下远端仓库是否设置的有分支保护策略。有可能覆盖掉其他人的提交的潜在风险，在 Push 之前需要再三确认远端没有比本地更新的提交记录。操作步骤和场景二中的类似，这里就不再赘述。</p><h2 id="场景四：将所有的提交记录合并为一个-Commit"><a href="#场景四：将所有的提交记录合并为一个-Commit" class="headerlink" title="场景四：将所有的提交记录合并为一个 Commit"></a>场景四：将所有的提交记录合并为一个 Commit</h2><p><img src="/images/2020-12-09/%E5%9C%BA%E6%99%AF%E5%9B%9B.png" alt="场景四"></p><p>先介绍一下这个场景产生的背景。有一个已经开发了一段时间的项目，从某个时间点开始，需要不定期把之前一段时间的所有提交合并成一个提交并且规整提交信息为特定的格式，然后 Push 到远端的另一个仓库中，而且这个操作在将来还需要增量进行。比如：1月1号需要把 Repo A 的 C1 和 C2 合并成 CC1 并 Push 到 Repo B 中，1月10号需要把 Repo A 的 C3 和 C4 合并成 CC2 并 Push 到 Repo B 中，依次类推。</p><ul><li>第一次合并操作，即把所有的提交合并成一个提交。</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git rebase -i &lt;第一个提交的 SHA 值&gt;</span><br></pre></td></tr></table></figure><p>因为这里必须要提供一个 SHA 值，所以最终结果一定会大于或等于2个提交记录，并<strong>不能达到我们的目的</strong>。</p><p>那么，聪明的程序员马上想到基于相对位置的方式，假设本地总共4个提交记录，我是不是可以通过<code>HEAD~4</code>来定位呢？</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git rebase -i HEAD~4</span><br></pre></td></tr></table></figure><p>这是你发现事与愿违，并不能成功执行，而是得到一个错误：<code>fatal: invalid upstream &#39;HEAD~4&#39;</code>。但是功夫不负有心人，经过一番摸索，找到一个参数 <code>--root</code> 可以帮我们完美解决问题。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git rebase -i --root</span><br></pre></td></tr></table></figure><ul><li>第一次合并操作之后，后续的操作可以通过分支的方式操作更加容易，就如 Pull Request 中的 Squash and Merge 一般丝滑。</li></ul><p><img src="/images/2020-12-09/%E5%9C%BA%E6%99%AF%E5%9B%9B-2.png" alt="场景四-2"></p><p>具体操作步骤如下：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span> 假设第一次将 C1 和 C2 合并成了 CC1，这时我们从合并完成的这个点创建出一个分支（以分支名 `repo-b` 为例）</span><br><span class="line">git checkout -b repo-b &lt;CC1 的 SHA 值&gt;</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 假设 Repo A 的 C3 和 C4 在 master 分支上，通过 Merge Squash 的方式合并 C3 和 C4 成为 CC2</span><br><span class="line"><span class="meta">#</span> 如果此时 master 上除了 C3 和 C4 还有其他的提交记录，那么我们可以从 C4 的地方创建出新分支（以 `repo-a` 为例），然后在下面的命令中将 `master` 替换为 `repo-a` 即可</span><br><span class="line">git merge --squash master --allow-unrelated-histories</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span> 此时如果有冲突就解决冲突，解决完冲突后，执行 `git commit`</span><br><span class="line"><span class="meta">#</span> 最后 Push 到 Repo B 的远端仓库即可。</span><br></pre></td></tr></table></figure><p>至此，我遇到过的几种场景就列举完了，欢迎大家留言补充，或者探讨更好的方式。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/2020-12-09/git-merge-commits.png&quot; alt=&quot;Git Branch &amp;amp; Commits&quot;&gt;&lt;/p&gt;
&lt;p&gt;平时在 Git 的使用过程中，遇到了好些场景需要把多个 Commit 合并成一个 Commit。经过在网上一通搜索，大家也是仁者见仁，智者见智。在本文中总结了几种常见场景，如果大家还遇到过其他的场景，也欢迎留言讨论。&lt;/p&gt;
    
    </summary>
    
      <category term="Productivity" scheme="https://codinglife.tech/categories/Productivity/"/>
    
    
      <category term="Git" scheme="https://codinglife.tech/tags/Git/"/>
    
  </entry>
  
  <entry>
    <title>开发人员常用的国内镜像仓库</title>
    <link href="https://codinglife.tech/2020/09/awesome-mirrors/"/>
    <id>https://codinglife.tech/2020/09/awesome-mirrors/</id>
    <published>2020-09-01T14:10:54.000Z</published>
    <updated>2020-12-02T03:08:06.045Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/awesome-mirrors.jpg" alt="Awesome mirrors"></p><blockquote><p>本文如果对你有帮助请帮忙Star一下，以帮助更多的人。<iframe src="https://ghbtns.com/github-btn.html?user=pengisgood&repo=awesome-mirrors&type=star&count=true" frameborder="0" scrolling="0" width="100px" height="32" style="vertical-align:text-top"></iframe><iframe src="https://ghbtns.com/github-btn.html?user=pengisgood&repo=awesome-mirrors&type=follow&count=true" frameborder="0" scrolling="0" width="200px" height="32" style="vertical-align:text-top"></iframe></p></blockquote><p>由于众所周知的原因，中国的开发者在下载各种依赖的时候速度都比较慢。因此部分有实力的高校、公司在国内搭建了方便开发者的各种镜像仓库，并且几乎都是和国外的源定时同步的。本仓库主要收集方便中国开发者提速的源，以及配置的方式。</p><p>下面的操作方式在不同的系统可能会有细微的差别，欢迎大家补充和纠正。</p><a id="more"></a><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><ul><li><a href="#Docker">Docker</a></li><li><a href="#Nodejs">Nodejs</a></li><li><a href="#Python">Python</a></li><li><a href="#Java">Java</a></li><li><a href="#Goproxy">Goproxy</a></li><li><a href="#Ruby">Ruby</a></li><li><a href="#Alpine-apk">Alpine apk</a></li><li><a href="#Centos-yum">Centos yum</a></li><li><a href="#Debian-apt">Debian apt</a></li><li><a href="#Ubuntu-apt">Ubuntu apt</a></li><li><a href="#Homebrew">Homebrew</a></li><li><a href="#iOS">iOS</a></li><li><a href="#Git">Git</a></li></ul><h2 id="Docker"><a href="#Docker" class="headerlink" title="Docker"></a>Docker</h2><p>首先从网上搜索国内的docker registry源，然后修改docker的配置并<strong>重启</strong>docker。在这里我比较推荐使用免费的阿里云镜像加速器（至少截至2020年09月03日还是免费的），目前使用一直比较平稳。</p><ul><li>获取镜像加速url</li></ul><p>注册一个阿里云账号并登录，在<code>产品与服务</code>中搜索<code>容器镜像服务</code>，跟随引导完成必要的一些步骤，然后来到这个<a href="https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors" target="_blank" rel="noopener">页面</a>，就可以看到自己专有的加速器地址了。</p><ul><li>给docker客户端配置镜像加速器</li></ul><p>这里以MacOS为例，其他系统类似。如果没有<code>/etc/docker/daemon.json</code>文件，则可以直接通过下面的命令完成配置；如果该文件已经存在了，则选择自己熟悉的文本编辑器编辑该文件，添加加速器地址的配置。如果你使用的带界面的客户端，也可以在<code>Preferences... -&gt; Docker Engine</code>显示的编辑器中添加相应的配置。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">sudo mkdir -p /etc/docker</span><br><span class="line">sudo tee /etc/docker/daemon.json &lt;&lt;-'EOF'</span><br><span class="line">&#123;</span><br><span class="line">  "registry-mirrors": ["https://xxx.mirror.aliyuncs.com"]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p><strong>注意</strong>：将<code>https://xxx.mirror.aliyuncs.com</code>替换成你自己的镜像加速器地址。再次提醒，配置完成后，<strong>重启</strong>docker之后才会生效。</p><p>除了阿里云的源，还有一些其他的源可以参考：</p><ol><li><a href="https://registry.docker-cn.com" target="_blank" rel="noopener">https://registry.docker-cn.com</a></li><li><a href="http://hub-mirror.c.163.com" target="_blank" rel="noopener">http://hub-mirror.c.163.com</a></li><li><a href="http://docker.mirrors.ustc.edu.cn" target="_blank" rel="noopener">http://docker.mirrors.ustc.edu.cn</a></li><li><a href="http://mirror.azure.cn/help/docker-registry-proxy-cache.html" target="_blank" rel="noopener">http://mirror.azure.cn/help/docker-registry-proxy-cache.html</a></li></ol><h2 id="Nodejs"><a href="#Nodejs" class="headerlink" title="Nodejs"></a>Nodejs</h2><p>下面是NPM的配置方式，以淘宝的源为例。</p><ul><li>临时使用</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 为单用户安装</span></span><br><span class="line">npm install --registry https://registry.npm.taobao.org &lt;package-name&gt;</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash">为所有用户安装</span></span><br><span class="line">npm install --global --registry https://registry.npm.taobao.org &lt;package-name&gt;</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> yarn</span></span><br><span class="line">yarn save &lt;package-name&gt; --registry https://registry.npm.taobao.org</span><br></pre></td></tr></table></figure><ul><li>默认使用</li></ul><figure class="highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># npm</span></span><br><span class="line">npm <span class="built_in">set</span> registry https://registry.npm.taobao.org</span><br><span class="line"></span><br><span class="line"><span class="comment"># yarn</span></span><br><span class="line">yarn config <span class="built_in">set</span> registry https://registry.npm.taobao.org</span><br></pre></td></tr></table></figure><p>除了淘宝的源，还有一些其他的源可以参考：</p><ol><li><a href="http://crproxy.trafficmanager.net:4873" target="_blank" rel="noopener">http://crproxy.trafficmanager.net:4873</a></li></ol><p>另外，可以通过<a href="https://github.com/Pana/nrm" target="_blank" rel="noopener"><code>nrm</code></a>管理多个NPM的源，相应的<code>yarn</code>也有<a href="https://github.com/i5ting/yrm" target="_blank" rel="noopener"><code>yrm</code></a>。</p><h2 id="Python"><a href="#Python" class="headerlink" title="Python"></a>Python</h2><p>PyPI (Python Package Index) 是 Python 编程语言的软件存储库。开发者可以通过 PyPI 查找和安装由 Python 社区开发和共享的软件，也可以将自己开发的库上传至 PyPI 。这里以阿里云的源为例。</p><ul><li>临时使用</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install -i https://mirrors.aliyun.com/pypi/simple &lt;package-name&gt;</span><br></pre></td></tr></table></figure><p>注意，<code>simple</code> 不能少, 是 <code>https</code> 而不是 <code>http</code></p><ul><li>默认使用</li></ul><p>升级 pip 到最新的版本 (&gt;=10.0.0) 后进行配置：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">pip install pip -U</span><br><span class="line">pip config set global.index-url https://mirrors.aliyun.com/pypi/simple</span><br></pre></td></tr></table></figure><p>除了阿里云的源，还有一些其他的源可以参考：</p><ol><li><a href="https://pypi.tuna.tsinghua.edu.cn/simple" target="_blank" rel="noopener">https://pypi.tuna.tsinghua.edu.cn/simple</a></li><li><a href="https://pypi.mirrors.ustc.edu.cn/simple" target="_blank" rel="noopener">https://pypi.mirrors.ustc.edu.cn/simple</a></li><li><a href="https://pypi.douban.com/simple" target="_blank" rel="noopener">https://pypi.douban.com/simple</a></li><li><a href="http://mirror.azure.cn/pypi/simple" target="_blank" rel="noopener">http://mirror.azure.cn/pypi/simple</a></li></ol><p><code>Anaconda</code>的配置方式可以参考清华大学的<a href="https://mirrors.tuna.tsinghua.edu.cn/help/anaconda" target="_blank" rel="noopener">Anaconda镜像使用帮助</a></p><h2 id="Java"><a href="#Java" class="headerlink" title="Java"></a>Java</h2><ul><li>Maven 配置</li></ul><p>打开 Maven 的配置文件(windows机器一般在maven安装目录的conf/settings.xml)，在<code>&lt;mirrors&gt;&lt;/mirrors&gt;</code>标签中添加 mirror 子节点:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">mirror</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span>&gt;</span>aliyunmaven<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">mirrorOf</span>&gt;</span>*<span class="tag">&lt;/<span class="name">mirrorOf</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">name</span>&gt;</span>阿里云公共仓库<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://maven.aliyun.com/repository/public<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">mirror</span>&gt;</span></span><br></pre></td></tr></table></figure><p>如果想使用其它代理仓库,可在<code>&lt;repositories&gt;&lt;/repositories&gt;</code>节点中加入对应的仓库使用地址。以使用spring代理仓为例：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">repository</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">id</span>&gt;</span>spring<span class="tag">&lt;/<span class="name">id</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">url</span>&gt;</span>https://maven.aliyun.com/repository/spring<span class="tag">&lt;/<span class="name">url</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">releases</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">releases</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">snapshots</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">enabled</span>&gt;</span>true<span class="tag">&lt;/<span class="name">enabled</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">snapshots</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">repository</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>gradle 配置</li></ul><p>在 build.gradle 文件中加入以下代码:</p><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">allprojects &#123;</span><br><span class="line">    repositories &#123;</span><br><span class="line">        maven &#123; url <span class="string">'https://maven.aliyun.com/repository/public/'</span> &#125;</span><br><span class="line">        mavenLocal()</span><br><span class="line">        mavenCentral()</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果想使用 maven.aliyun.com 提供的其它代理仓，以使用 spring 仓为例，代码如下:</p><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">allprojects &#123;</span><br><span class="line">    repositories &#123;</span><br><span class="line">        maven &#123; url <span class="string">'https://maven.aliyun.com/repository/public/'</span> &#125;</span><br><span class="line">        maven &#123; url <span class="string">'https://maven.aliyun.com/repository/spring/'</span>&#125;</span><br><span class="line">        mavenLocal()</span><br><span class="line">        mavenCentral()</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>除了Spring的仓库之外，阿里云还提供一些其他的源：</p><ol><li>central <a href="https://maven.aliyun.com/repository/central" target="_blank" rel="noopener">https://maven.aliyun.com/repository/central</a></li><li>jcenter <a href="https://maven.aliyun.com/repository/public" target="_blank" rel="noopener">https://maven.aliyun.com/repository/public</a></li><li>public <a href="https://maven.aliyun.com/repository/public" target="_blank" rel="noopener">https://maven.aliyun.com/repository/public</a></li><li>google <a href="https://maven.aliyun.com/repository/google" target="_blank" rel="noopener">https://maven.aliyun.com/repository/google</a></li><li>gradle-plugin <a href="https://maven.aliyun.com/repository/gradle-plugin" target="_blank" rel="noopener">https://maven.aliyun.com/repository/gradle-plugin</a></li><li>spring <a href="https://maven.aliyun.com/repository/spring" target="_blank" rel="noopener">https://maven.aliyun.com/repository/spring</a></li><li>spring-plugin <a href="https://maven.aliyun.com/repository/spring-plugin" target="_blank" rel="noopener">https://maven.aliyun.com/repository/spring-plugin</a></li><li>grails-core <a href="https://maven.aliyun.com/repository/grails-core" target="_blank" rel="noopener">https://maven.aliyun.com/repository/grails-core</a></li><li>apache snapshots <a href="https://maven.aliyun.com/repository/apache-snapshots" target="_blank" rel="noopener">https://maven.aliyun.com/repository/apache-snapshots</a></li></ol><h2 id="Goproxy"><a href="#Goproxy" class="headerlink" title="Goproxy"></a>Goproxy</h2><p>使用go1.11以上版本并开启go module机制，下面以阿里云的源为例：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">export GO111MODULE=on</span><br><span class="line">export GOPROXY=https://mirrors.aliyun.com/goproxy</span><br></pre></td></tr></table></figure><p>除了阿里云的源，还有一些其他的源可以参考：</p><ol><li><a href="https://goproxy.cn" target="_blank" rel="noopener">https://goproxy.cn</a></li><li><a href="https://goproxy.io" target="_blank" rel="noopener">https://goproxy.io</a></li></ol><h2 id="Ruby"><a href="#Ruby" class="headerlink" title="Ruby"></a>Ruby</h2><p>这里以配置<code>Ruby China</code>的源为例：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/</span><br></pre></td></tr></table></figure><p><code>Bundler</code>用户参考如下配置：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bundle config mirror.https://rubygems.org https://gems.ruby-china.com</span><br></pre></td></tr></table></figure><p>这样你<strong>不用</strong>改你的<code>Gemfile</code>的<code>source</code>。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">source &apos;https://rubygems.org/&apos;</span><br><span class="line">gem &apos;rails&apos;, &apos;4.2.5&apos;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>除了<a href="https://gems.ruby-china.com" target="_blank" rel="noopener">Ruby China</a>的源，还有一些其他的源可以参考：</p><ol><li><a href="https://mirrors.aliyun.com/rubygems" target="_blank" rel="noopener">https://mirrors.aliyun.com/rubygems</a></li><li><a href="https://mirrors.tuna.tsinghua.edu.cn/rubygems" target="_blank" rel="noopener">https://mirrors.tuna.tsinghua.edu.cn/rubygems</a></li><li><a href="https://mirrors.ustc.edu.cn/rubygems" target="_blank" rel="noopener">https://mirrors.ustc.edu.cn/rubygems</a></li><li><a href="http://mirror.azure.cn/rubygems" target="_blank" rel="noopener">http://mirror.azure.cn/rubygems</a></li></ol><h2 id="Alpine-apk"><a href="#Alpine-apk" class="headerlink" title="Alpine apk"></a>Alpine apk</h2><p>Alpine Linux 是一个面向安全，轻量级的基于musl libc与busybox项目的Linux发行版。</p><p>下面以配置阿里云的源作为例子：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories</span><br></pre></td></tr></table></figure><p>除了阿里云的源，还有一些其他的源可以参考：</p><ol><li>dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn</li></ol><h2 id="Centos-yum"><a href="#Centos-yum" class="headerlink" title="Centos yum"></a>Centos yum</h2><p>CentOS，是基于 Red Hat Linux 提供的可自由使用源代码的企业级 Linux 发行版本；是一个稳定，可预测，可管理和可复制的免费企业级计算平台。</p><p>更换源之前，建议先备份一下，下面以阿里云的源为例：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup</span><br></pre></td></tr></table></figure><p>下载阿里云的源：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> Centos 6</span></span><br><span class="line">curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-6.repo</span><br><span class="line"><span class="meta">#</span><span class="bash"> Centos 7</span></span><br><span class="line">curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo</span><br><span class="line"><span class="meta">#</span><span class="bash"> Centos 8</span></span><br><span class="line">curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo</span><br></pre></td></tr></table></figure><p>生成缓存：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum makecache</span><br></pre></td></tr></table></figure><p><strong>注意：</strong> 非阿里云ECS用户会出现 <code>Couldn&#39;t resolve host &#39;mirrors.cloud.aliyuncs.com&#39;</code>信息，不影响使用。用户也可自行修改相关配置：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sed -i -e '/mirrors.cloud.aliyuncs.com/d' -e '/mirrors.aliyuncs.com/d' /etc/yum.repos.d/CentOS-Base.repo</span><br></pre></td></tr></table></figure><p>除了阿里云的源，还有一些其他的源可以参考：</p><ol><li><a href="https://mirrors.tuna.tsinghua.edu.cn/help/centos" target="_blank" rel="noopener">https://mirrors.tuna.tsinghua.edu.cn/help/centos</a></li><li><a href="http://mirrors.ustc.edu.cn/help/centos.html" target="_blank" rel="noopener">http://mirrors.ustc.edu.cn/help/centos.html</a></li><li><a href="http://mirror.azure.cn/help/centos.html" target="_blank" rel="noopener">http://mirror.azure.cn/help/centos.html</a></li></ol><h2 id="Debian-apt"><a href="#Debian-apt" class="headerlink" title="Debian apt"></a>Debian apt</h2><p>Debian GNU/Linux ，是一个操作系统及自由软件的发行版，由一群自愿付出时间和精力的用户来维护并更新。它附带了超过 59000 个软件包，这些预先编译好的软件被打包成一种良好的格式以便于用户安装和使用。</p><p>下面以阿里云的源为例：</p><p><strong>debian 7.x (wheezy)</strong></p><p>编辑/etc/apt/sources.list文件(需要使用sudo), 在文件最前面添加以下条目(操作前请做好相应备份)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">deb http://mirrors.aliyun.com/debian/ wheezy main non-free contrib</span><br><span class="line">deb http://mirrors.aliyun.com/debian/ wheezy-proposed-updates main non-free contrib</span><br><span class="line">deb-src http://mirrors.aliyun.com/debian/ wheezy main non-free contrib</span><br><span class="line">deb-src http://mirrors.aliyun.com/debian/ wheezy-proposed-updates main non-free contrib</span><br></pre></td></tr></table></figure><p><strong>debian 8.x (jessie)</strong></p><p>编辑/etc/apt/sources.list文件(需要使用sudo), 在文件最前面添加以下条目(操作前请做好相应备份)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">deb http://mirrors.aliyun.com/debian/ jessie main non-free contrib</span><br><span class="line">deb http://mirrors.aliyun.com/debian/ jessie-proposed-updates main non-free contrib</span><br><span class="line">deb-src http://mirrors.aliyun.com/debian/ jessie main non-free contrib</span><br><span class="line">deb-src http://mirrors.aliyun.com/debian/ jessie-proposed-updates main non-free contrib</span><br></pre></td></tr></table></figure><p><strong>debian 9.x (stretch)</strong></p><p>编辑/etc/apt/sources.list文件(需要使用sudo), 在文件最前面添加以下条目(操作前请做好相应备份)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">deb http://mirrors.aliyun.com/debian/ stretch main non-free contrib</span><br><span class="line">deb-src http://mirrors.aliyun.com/debian/ stretch main non-free contrib</span><br><span class="line">deb http://mirrors.aliyun.com/debian-security stretch/updates main</span><br><span class="line">deb-src http://mirrors.aliyun.com/debian-security stretch/updates main</span><br><span class="line">deb http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib</span><br><span class="line">deb-src http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib</span><br><span class="line">deb http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib</span><br><span class="line">deb-src http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib</span><br></pre></td></tr></table></figure><p>除了阿里云的源，还有一些其他的源可以参考：</p><ol><li><a href="https://mirrors.tuna.tsinghua.edu.cn/help/debian" target="_blank" rel="noopener">https://mirrors.tuna.tsinghua.edu.cn/help/debian</a></li><li><a href="http://mirrors.ustc.edu.cn/help/debian.html" target="_blank" rel="noopener">http://mirrors.ustc.edu.cn/help/debian.html</a></li></ol><h2 id="Ubuntu-apt"><a href="#Ubuntu-apt" class="headerlink" title="Ubuntu apt"></a>Ubuntu apt</h2><p>Ubuntu，是一款基于 Debian Linux 的以桌面应用为主的操作系统，内容涵盖文字处理、电子邮件、软件开发工具和 Web 服务等，可供用户免费下载、使用和分享。</p><p>下面以阿里云的源为例：</p><p>用你熟悉的编辑器打开<code>/etc/apt/sources.list</code>，替换默认的<code>http://archive.ubuntu.com/</code>为<code>mirrors.aliyun.com</code>。</p><h3 id="ubuntu-16-04-配置如下"><a href="#ubuntu-16-04-配置如下" class="headerlink" title="ubuntu 16.04 配置如下"></a>ubuntu 16.04 配置如下</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">deb http://mirrors.aliyun.com/ubuntu/ xenial main</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ xenial main</span><br><span class="line"></span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ xenial-updates main</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates main</span><br><span class="line"></span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ xenial universe</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ xenial universe</span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ xenial-updates universe</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ xenial-updates universe</span><br><span class="line"></span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ xenial-security main</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security main</span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ xenial-security universe</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ xenial-security universe</span><br></pre></td></tr></table></figure><h3 id="ubuntu-18-04-bionic-配置如下"><a href="#ubuntu-18-04-bionic-配置如下" class="headerlink" title="ubuntu 18.04(bionic) 配置如下"></a>ubuntu 18.04(bionic) 配置如下</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse</span><br><span class="line"></span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse</span><br><span class="line"></span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse</span><br><span class="line"></span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse</span><br><span class="line"></span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse</span><br></pre></td></tr></table></figure><h3 id="ubuntu-20-04-focal-配置如下"><a href="#ubuntu-20-04-focal-配置如下" class="headerlink" title="ubuntu 20.04(focal) 配置如下"></a>ubuntu 20.04(focal) 配置如下</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse</span><br><span class="line"></span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse</span><br><span class="line"></span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse</span><br><span class="line"></span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse</span><br><span class="line"></span><br><span class="line">deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse</span><br><span class="line">deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse</span><br></pre></td></tr></table></figure><p>除了阿里云的源，还有一些其他的源可以参考：</p><ol><li><a href="https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu" target="_blank" rel="noopener">https://mirrors.tuna.tsinghua.edu.cn/help/ubuntu</a></li><li><a href="http://mirrors.ustc.edu.cn/help/ubuntu.html" target="_blank" rel="noopener">http://mirrors.ustc.edu.cn/help/ubuntu.html</a></li><li><a href="http://mirror.azure.cn/help/ubuntu.html" target="_blank" rel="noopener">http://mirror.azure.cn/help/ubuntu.html</a></li></ol><h2 id="Homebrew"><a href="#Homebrew" class="headerlink" title="Homebrew"></a>Homebrew</h2><p>Homebrew 是一款自由及开放源代码的软件包管理系统，用以简化 macOS 系统上的软件安装过程。它拥有安装、卸载、更新、查看、搜索等很多实用的功能，通过简单的一条指令，就可以实现包管理，十分方便快捷。</p><p>下面以阿里云的源为例：</p><table><thead><tr><th align="left">名称</th><th align="left">说明</th></tr></thead><tbody><tr><td align="left">brew</td><td align="left">Homebrew 源代码仓库</td></tr><tr><td align="left">homebrew-core</td><td align="left">Homebrew 核心源</td></tr><tr><td align="left">homebrew-cask</td><td align="left">提供 macOS 应用和大型二进制文件的安装</td></tr><tr><td align="left">homebrew-bottles</td><td align="left">预编译二进制软件包</td></tr></tbody></table><ul><li>Bash 终端配置</li></ul><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 替换brew.git:</span></span><br><span class="line">cd "$(brew --repo)"</span><br><span class="line">git remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git</span><br><span class="line"><span class="meta">#</span><span class="bash"> 替换homebrew-core.git:</span></span><br><span class="line">cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"</span><br><span class="line">git remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-core.git</span><br><span class="line"><span class="meta">#</span><span class="bash"> 应用生效</span></span><br><span class="line">brew update</span><br><span class="line"><span class="meta">#</span><span class="bash"> 替换homebrew-bottles:</span></span><br><span class="line">echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles' &gt;&gt; ~/.bash_profile</span><br><span class="line">source ~/.bash_profile</span><br></pre></td></tr></table></figure><ul><li>恢复默认配置</li></ul><p>出于某些场景, 可能需要回退到默认配置, 你可以通过下述方式回退到默认配置。</p><p>首先执行下述命令:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 重置brew.git:</span></span><br><span class="line">cd "$(brew --repo)"</span><br><span class="line">git remote set-url origin https://github.com/Homebrew/brew.git</span><br><span class="line"><span class="meta">#</span><span class="bash"> 重置homebrew-core.git:</span></span><br><span class="line">cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core"</span><br><span class="line">git remote set-url origin https://github.com/Homebrew/homebrew-core.git</span><br></pre></td></tr></table></figure><p>然后删掉 <code>HOMEBREW_BOTTLE_DOMAIN</code> 环境变量,将你终端文件<code>~/.bash_profile</code>中<code>HOMEBREW_BOTTLE_DOMAIN</code>行删掉, 并执行<code>source ~/.bash_profile</code>。</p><p>除了阿里云的源，还有一些其他的源可以参考：</p><ol><li><a href="https://mirrors.tuna.tsinghua.edu.cn/help/homebrew" target="_blank" rel="noopener">https://mirrors.tuna.tsinghua.edu.cn/help/homebrew</a></li><li><a href="http://mirrors.ustc.edu.cn/help/brew.git.html" target="_blank" rel="noopener">http://mirrors.ustc.edu.cn/help/brew.git.html</a></li></ol><h2 id="iOS"><a href="#iOS" class="headerlink" title="iOS"></a>iOS</h2><p><code>CocoaPods</code> 是一个 Cocoa 和 Cocoa Touch 框架的依赖管理器，具体原理和 <a href="#homebrew"><code>Homebrew</code> </a>有点类似，都是从 GitHub 下载索引，然后根据索引下载依赖的源代码。</p><p>下面以清华大学的源为例：</p><p>对于旧版的 CocoaPods 可以使用如下方法使用 tuna 的镜像：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ pod repo remove master</span><br><span class="line">$ pod repo add master https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git</span><br><span class="line">$ pod repo update</span><br></pre></td></tr></table></figure><p>新版的 CocoaPods 不允许用<code>pod repo add</code>直接添加master库了，但是依然可以：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ cd ~/.cocoapods/repos </span><br><span class="line">$ pod repo remove master</span><br><span class="line">$ git clone https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git master</span><br></pre></td></tr></table></figure><p>最后进入自己的工程，在自己工程的<code>podFile</code>第一行加上：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source &apos;https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git&apos;</span><br></pre></td></tr></table></figure><p><strong>注意：</strong> 从<a href="http://blog.cocoapods.org/CocoaPods-1.7.2/" target="_blank" rel="noopener"><code>1.7.2</code></a>开始，已经完全切到<code>CDN</code>上了。<a href="http://blog.cocoapods.org/CocoaPods-1.8.0-beta/" target="_blank" rel="noopener"><code>1.8</code></a>以上甚至把<code>CDN</code>作为默认源使用，在<code>Podfile</code>最上面添加即可。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">source &apos;https://cdn.cocoapods.org/&apos;</span><br></pre></td></tr></table></figure><p>除了清华大学的源，还有一些其他的源可以参考：</p><ol><li><a href="https://gitclub.cn/CocoaPods/Specs.git" target="_blank" rel="noopener">https://gitclub.cn/CocoaPods/Specs.git</a></li><li><a href="http://git.oschina.net/akuandev/Specs.git" target="_blank" rel="noopener">http://git.oschina.net/akuandev/Specs.git</a></li></ol><h2 id="Git"><a href="#Git" class="headerlink" title="Git"></a>Git</h2><blockquote><p>以下内容摘自原仓库的说明文档，推荐优先参考原<a href="https://github.com/fastgh/fgit/blob/master/README.md" target="_blank" rel="noopener">文档</a>。</p></blockquote><p><a href="https://github.com/fastgh/fgit" target="_blank" rel="noopener">fgit</a>是一个可以无缝替换git命令行的工具，使用优化线路为使用github.com加速。</p><p>官方介绍的特点：</p><ul><li>目前实测git clone速度超过15MB/S</li><li>支持github.com私有库，也支持push</li><li>两种工作模式：镜像（反向代理）模式和HTTP代理模式，都是实时连接github.com，不是缓存</li><li>支持包括clone/push/pull/fetch在内的各种git命令，兼容git命令行参数，可以用来无缝替换git命令行</li><li>只针对github.com加速，不干扰对非github.com库的使用</li><li>使用go语言开发，不是shell脚本或.bat，跨平台。Windows 10、Linux (Unbuntu)、Mac (x86)都实测通过</li></ul><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><ul><li>Mac</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo curl -L https://github.com/fastgh/fgit/releases/download/v1.0.0/fgit.darwin -o /usr/local/bin/fgit</span><br><span class="line">sudo chmod +x /usr/local/bin/fgit</span><br></pre></td></tr></table></figure><ul><li>Linux</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo curl -L https://github.com/fastgh/fgit/releases/download/v1.0.0/fgit.linux -o /usr/local/bin/fgit</span><br><span class="line">sudo chmod +x /usr/local/bin/fgit</span><br></pre></td></tr></table></figure><h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><ul><li><p>和常规的git命令行几乎相同，支持各种命令行参数</p></li><li><p>对于公共库，clone/pull/fetch时默认使用镜像模式，基于安全考虑，镜像模式不支持push以及私有库</p><p>  镜像模式例如：<code>fgit clone https://github.com/spring-projects/spring-boot.git --depth=1</code></p></li><li><p>两种情况会判定为需要push或者私有库，此时会使用HTTP代理模式，代理模式比镜像模式安全</p><ol><li><p>push</p></li><li><p>URL中包含用户名，那么会被判定为需要push或者私有库。<br> 对于clone命令，URL是从clone的URL中解析得到，对于其它git命令，则使用<code>git remote -v</code>得到</p><p>代理模式例如：</p></li><li><p><code>fgit push origin master</code></p></li><li><p><code>fgit clone https://fastgh@github.com/fastgh/fgit.git</code></p><p>也可以通过<code>--use-proxy</code>选项强制走HTTP代理模式，例如:</p></li><li><p><code>fgit --use-proxy clone https://github.com/fastgh/fgit.git</code></p><p>代理服务器的优化线路成本高，所以大家尽量使用镜像模式，以节省服务器带宽资源</p></li></ol></li><li><p>其它功能：</p><ol><li><p>可以打开调试开关，看一看fgit的工作过程：下载服务器列表 –&gt; 设置镜像或代理 –&gt; 执行git –&gt; 回复镜像或代理</p><p><code>fgit --debug clone https://github.com/fastgh/fgit.git</code></p></li><li><p>fgit首次运行时，会在用户主目录下生成一个配置文件.fgit.json，包含服务器地址等信息，必要时可以通过设置这个文件选择接入其它服务方，或指定镜像服务器或代理服务器</p></li></ol></li></ul>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/awesome-mirrors.jpg&quot; alt=&quot;Awesome mirrors&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;本文如果对你有帮助请帮忙Star一下，以帮助更多的人。&lt;iframe src=&quot;https://ghbtns.com/github-btn.html?user=pengisgood&amp;repo=awesome-mirrors&amp;type=star&amp;count=true&quot; frameborder=&quot;0&quot; scrolling=&quot;0&quot; width=&quot;100px&quot; height=&quot;32&quot; style=&quot;vertical-align:text-top&quot;&gt;&lt;/iframe&gt;&lt;iframe src=&quot;https://ghbtns.com/github-btn.html?user=pengisgood&amp;repo=awesome-mirrors&amp;type=follow&amp;count=true&quot; frameborder=&quot;0&quot; scrolling=&quot;0&quot; width=&quot;200px&quot; height=&quot;32&quot; style=&quot;vertical-align:text-top&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由于众所周知的原因，中国的开发者在下载各种依赖的时候速度都比较慢。因此部分有实力的高校、公司在国内搭建了方便开发者的各种镜像仓库，并且几乎都是和国外的源定时同步的。本仓库主要收集方便中国开发者提速的源，以及配置的方式。&lt;/p&gt;
&lt;p&gt;下面的操作方式在不同的系统可能会有细微的差别，欢迎大家补充和纠正。&lt;/p&gt;
    
    </summary>
    
      <category term="Productivity" scheme="https://codinglife.tech/categories/Productivity/"/>
    
    
      <category term="Untagged" scheme="https://codinglife.tech/tags/Untagged/"/>
    
  </entry>
  
  <entry>
    <title>加速 Hyperledger Fabric 的 Docker 镜像构建过程</title>
    <link href="https://codinglife.tech/2020/08/speed-up-hyperledger-fabric-build-process/"/>
    <id>https://codinglife.tech/2020/08/speed-up-hyperledger-fabric-build-process/</id>
    <published>2020-08-18T08:26:40.000Z</published>
    <updated>2020-12-02T03:09:17.590Z</updated>
    
    <content type="html"><![CDATA[<p>Hyperledger Fabric 从<code>v2.0</code>开始，全面将docker基础镜像替换成了体积更小、潜在安全风险更少、更加轻量的<code>Alpine Linux</code>，从而使得<code>make docker</code>出来的各种镜像的体积几乎都缩小为原来的一半，确实能够节省更多的硬盘空间。但是，由于众所周知的原因，对于生在红旗下，长在新中国的程序员们，第一次在Fabric项目下构建docker镜像时，依然是奇慢无比，屡次超时。</p><p>那么这个问题怎么解决呢？</p><h2 id="分析速度的瓶颈"><a href="#分析速度的瓶颈" class="headerlink" title="分析速度的瓶颈"></a>分析速度的瓶颈</h2><p>首先通过分析速度慢的原因，找出可以优化的点。通过分析<code>make docker</code>命令，大概过程是这样的：首先是docker会从docker registry pull Alpine作为基础镜像，然后使用<code>apk add --no-cache xxx</code>安装一些软件，最后通过make命令build出Peer、Order以及其他的tools的二进制包。到这里相信国内的各种奇人义士已经磨刀霍霍，迫不及待的开始替换各种国内的mirror了。</p><a id="more"></a><p>下面的各种资源都来源于网上各位好心人的分享。</p><h2 id="加速docker-pull过程"><a href="#加速docker-pull过程" class="headerlink" title="加速docker pull过程"></a>加速docker pull过程</h2><p>首先从网上搜索国内的docker registry源，然后修改docker的配置并<strong>重启</strong>docker。在这里我比较推荐使用自己专有的免费的阿里云镜像加速器，目前使用一直比较平稳。</p><ul><li>获取镜像加速url</li></ul><p>注册一个阿里云账号并登录，在<code>产品与服务</code>中搜索<code>容器镜像服务</code>，跟随引导完成必要的一些步骤，然后来到这个页面：<a href="https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors，就可以看到自己专有的加速器地址了。" target="_blank" rel="noopener">https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors，就可以看到自己专有的加速器地址了。</a></p><ul><li>给docker客户端配置镜像加速器</li></ul><p>如果没有<code>/etc/docker/daemon.json</code>文件，则可以直接通过下面的命令完成配置；如果该文件已经存在了，则选择自己熟悉的文本编辑器编辑该文件，添加加速器地址的配置。如果你使用的带界面的客户端，也可以在<code>Preferences... -&gt; Docker Engine</code>显示的编辑器中添加相应的配置。</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">sudo mkdir -p /etc/docker</span><br><span class="line">sudo tee /etc/docker/daemon.json &lt;&lt;-'EOF'</span><br><span class="line">&#123;</span><br><span class="line">  "registry-mirrors": ["https://xxx.mirror.aliyuncs.com"]</span><br><span class="line">&#125;</span><br><span class="line">EOF</span><br><span class="line">sudo systemctl daemon-reload</span><br><span class="line">sudo systemctl restart docker</span><br></pre></td></tr></table></figure><p><strong>注意</strong>：将<code>https://xxx.mirror.aliyuncs.com</code>替换成你自己的镜像加速器地址。再次提醒，配置完成后，<strong>重启</strong>docker之后才会生效。</p><p>至此我们完成了docker pull镜像阶段的加速，接下来我们加速在Alpine中安装软件的过程。</p><h2 id="加速Alpine安装软件的过程"><a href="#加速Alpine安装软件的过程" class="headerlink" title="加速Alpine安装软件的过程"></a>加速Alpine安装软件的过程</h2><p>在docker pull下来的Alpine镜像中，使用apk安装软件时默认使用的是国外的源，速度比较慢。这里我们把源替换为国内的源，可以大大节省时间。下面以Hyperledger fabric peer的<a href="https://github.com/hyperledger/fabric/blob/master/images/peer/Dockerfile" target="_blank" rel="noopener">Dockerfile</a>为例，替换为阿里的源：</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="keyword">FROM</span> alpine:$&#123;ALPINE_VER&#125; as peer-base</span><br><span class="line"><span class="comment"># 使用下面这行命令完成源的替换</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> sed -i <span class="string">'s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g'</span> /etc/apk/repositories</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apk add --no-cache tzdata</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">FROM</span> golang:$&#123;GO_VER&#125;-alpine$&#123;ALPINE_VER&#125; as golang</span><br><span class="line"><span class="comment"># 使用下面这行命令完成源的替换</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> sed -i <span class="string">'s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g'</span> /etc/apk/repositories</span></span><br><span class="line"><span class="keyword">RUN</span><span class="bash"> apk add --no-cache \</span></span><br><span class="line"><span class="bash">bash \</span></span><br><span class="line"><span class="bash">gcc \</span></span><br><span class="line"><span class="bash">git \</span></span><br><span class="line"><span class="bash">make \</span></span><br><span class="line"><span class="bash">musl-dev</span></span><br><span class="line"><span class="keyword">ADD</span><span class="bash"> . <span class="variable">$GOPATH</span>/src/github.com/hyperledger/fabric</span></span><br><span class="line"></span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>另外也可以替换为中科大的源：dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn，或者清华的源：dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn，或者其他的源。</p><p><strong>注意</strong>：这里对于使用multi-stage的Dockerfile，如果不同的stage使用的是不同的基础镜像，则都需要替换源。</p><p>到这里，当docker使用Alpine作为基础镜像时，安装依赖软件的过程就会快很多。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Hyperledger Fabric 从&lt;code&gt;v2.0&lt;/code&gt;开始，全面将docker基础镜像替换成了体积更小、潜在安全风险更少、更加轻量的&lt;code&gt;Alpine Linux&lt;/code&gt;，从而使得&lt;code&gt;make docker&lt;/code&gt;出来的各种镜像的体积几乎都缩小为原来的一半，确实能够节省更多的硬盘空间。但是，由于众所周知的原因，对于生在红旗下，长在新中国的程序员们，第一次在Fabric项目下构建docker镜像时，依然是奇慢无比，屡次超时。&lt;/p&gt;
&lt;p&gt;那么这个问题怎么解决呢？&lt;/p&gt;
&lt;h2 id=&quot;分析速度的瓶颈&quot;&gt;&lt;a href=&quot;#分析速度的瓶颈&quot; class=&quot;headerlink&quot; title=&quot;分析速度的瓶颈&quot;&gt;&lt;/a&gt;分析速度的瓶颈&lt;/h2&gt;&lt;p&gt;首先通过分析速度慢的原因，找出可以优化的点。通过分析&lt;code&gt;make docker&lt;/code&gt;命令，大概过程是这样的：首先是docker会从docker registry pull Alpine作为基础镜像，然后使用&lt;code&gt;apk add --no-cache xxx&lt;/code&gt;安装一些软件，最后通过make命令build出Peer、Order以及其他的tools的二进制包。到这里相信国内的各种奇人义士已经磨刀霍霍，迫不及待的开始替换各种国内的mirror了。&lt;/p&gt;
    
    </summary>
    
      <category term="Productivity" scheme="https://codinglife.tech/categories/Productivity/"/>
    
    
      <category term="Blockchain" scheme="https://codinglife.tech/tags/Blockchain/"/>
    
      <category term="Docker" scheme="https://codinglife.tech/tags/Docker/"/>
    
      <category term="Hyperledger-Fabric" scheme="https://codinglife.tech/tags/Hyperledger-Fabric/"/>
    
  </entry>
  
  <entry>
    <title>Mac上的JDK多版本管理</title>
    <link href="https://codinglife.tech/2019/09/manage-multiple-java-versions-on-mac/"/>
    <id>https://codinglife.tech/2019/09/manage-multiple-java-versions-on-mac/</id>
    <published>2019-09-24T07:27:36.000Z</published>
    <updated>2020-12-02T03:05:20.338Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/jenv.png" alt="jEnv"><br>我的Mac上已经有一个JDK8的版本了，这不<a href="http://openjdk.java.net/projects/jdk/13/" target="_blank" rel="noopener">JDK13</a>刚发布（2019-09-17），想快速的尝一尝鲜，就得安装多个版本的JDK了。这个对Node、Ruby、Python的使用者来说，已经不是个什么新鲜话题了，但是对于Java的使用者来说，似乎没有那么多的人受到过多版本的折磨（我是通过GitHub上<a href="https://github.com/nvm-sh/nvm" target="_blank" rel="noopener"><code>nvm</code></a>、<a href="https://github.com/rbenv/rbenv" target="_blank" rel="noopener"><code>rbenv</code></a>、<a href="https://github.com/pyenv/pyenv" target="_blank" rel="noopener"><code>pyenv</code></a>、<a href="https://github.com/jenv/jenv" target="_blank" rel="noopener"><code>jenv</code></a>的Star数量臆测出这个结论的 :P）。</p><a id="more"></a><ul><li>nvm<iframe src="https://ghbtns.com/github-btn.html?user=nvm-sh&repo=nvm&type=star&count=true" frameborder="0" scrolling="0" width="200px" height="20px" style="vertical-align:middle"></iframe></li><li>rbenv<iframe src="https://ghbtns.com/github-btn.html?user=rbenv&repo=rbenv&type=star&count=true" frameborder="0" scrolling="0" width="200px" height="20px" style="vertical-align:middle"></iframe></li><li>pyenv<iframe src="https://ghbtns.com/github-btn.html?user=pyenv&repo=pyenv&type=star&count=true" frameborder="0" scrolling="0" width="200px" height="20px" style="vertical-align:middle"></iframe></li><li>jenv<iframe src="https://ghbtns.com/github-btn.html?user=jenv&repo=jenv&type=star&count=true" frameborder="0" scrolling="0" width="200px" height="20px" style="vertical-align:middle"></iframe></li></ul><h2 id="安装JDK-13"><a href="#安装JDK-13" class="headerlink" title="安装JDK 13"></a>安装JDK 13</h2><p>通过Homebrew 安装JDK 13，可以先通过<code>brew cask info java</code>查看目前Java的版本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">java: 13,33:5b8a42f3905b406298b72d750b6919f6</span><br><span class="line">https://openjdk.java.net/</span><br><span class="line">Not installed</span><br><span class="line">From: https://github.com/Homebrew/homebrew-cask/blob/master/Casks/java.rb</span><br><span class="line">==&gt; Name</span><br><span class="line">OpenJDK Java Development Kit</span><br><span class="line">==&gt; Artifacts</span><br><span class="line">jdk-13.jdk -&gt; /Library/Java/JavaVirtualMachines/openjdk-13.jdk (Generic Artifact)</span><br></pre></td></tr></table></figure><p>这里显示的是JDK13，正好是我想要安装的JDK版本，如果不是你想要的版本可以自己搜索相应的 Homebrew Tap。接下来直接安装:</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">$ brew cask install java</span><br><span class="line">$ java -version</span><br><span class="line"></span><br><span class="line">openjdk version <span class="string">"13"</span> 2019-09-17</span><br><span class="line">OpenJDK Runtime Environment (build 13+33)</span><br><span class="line">OpenJDK 64-Bit Server VM (build 13+33, mixed mode, sharing)</span><br></pre></td></tr></table></figure><p>这就说明JDK13已经安装好了。</p><p>但是另一个问题来了，我电脑上原来安装的JDK8去哪呢？我如何在不同的版本中随意切换呢？比如像Node的<code>nvm</code>，Ruby的<code>rvm</code>，Python的<code>pyenv</code>等。答案是我们可以通过<code>jenv</code>来实现相同的效果。</p><h2 id="安装-jEnv"><a href="#安装-jEnv" class="headerlink" title="安装 jEnv"></a>安装 jEnv</h2><ol><li>安装 jEnv</li></ol><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ brew install jenv</span><br><span class="line">$ <span class="built_in">exec</span> <span class="variable">$SHELL</span> -l</span><br></pre></td></tr></table></figure><p>安装完成之后，然后检查是否安装成功。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">$ jenv doctor</span><br><span class="line">[OK]No JAVA_HOME <span class="built_in">set</span></span><br><span class="line">[ERROR]Java binary <span class="keyword">in</span> path is not <span class="keyword">in</span> the jenv shims.</span><br><span class="line">[ERROR]Please check your path, or try using /path/to/java/home is not a valid path to java installation.</span><br><span class="line">PATH : /usr/<span class="built_in">local</span>/Cellar/jenv/0.5.2/libexec/libexec:/Users/xxx/.cargo/bin:/Users/xxx/.pyenv/shims:/Users/username/.pyenv:/Users/xxx/.nvm/versions/node/v8.11.4/bin:/Users/xxx/bin:/usr/<span class="built_in">local</span>/bin:/Users/xxx/.cargo/bin:/usr/<span class="built_in">local</span>/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/<span class="built_in">local</span>/go/bin:/Users/xxx/Documents/Projects/golang/bin</span><br><span class="line">[ERROR]Jenv is not loaded <span class="keyword">in</span> your zsh</span><br><span class="line">[ERROR]To fix : cat <span class="built_in">eval</span> <span class="string">"<span class="variable">$(jenv init -)</span>"</span> &gt;&gt; /Users/xxx/.zshrc</span><br></pre></td></tr></table></figure><p>在这里如果按照提示执行<code>cat eval &quot;$(jenv init -)&quot; &gt;&gt; /Users/xxx/.zshrc</code>：可能会得到如下错误：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">cat <span class="built_in">eval</span> <span class="string">"<span class="variable">$(jenv init -)</span>"</span> &gt;&gt; /Users/xxx/.zshrc</span><br><span class="line">cat: <span class="built_in">eval</span>: No such file or directory</span><br><span class="line">cat: <span class="built_in">export</span> PATH=<span class="string">"/Users/xxx/.jenv/shims:<span class="variable">$&#123;PATH&#125;</span>"</span></span><br><span class="line"><span class="built_in">export</span> JENV_SHELL=zsh</span><br><span class="line"><span class="built_in">export</span> JENV_LOADED=1</span><br><span class="line"><span class="built_in">unset</span> JAVA_HOME</span><br><span class="line"><span class="built_in">source</span> <span class="string">'/usr/local/Cellar/jenv/0.5.2/libexec/libexec/../completions/jenv.zsh'</span></span><br><span class="line">jenv <span class="built_in">rehash</span> 2&gt;/dev/null</span><br><span class="line"><span class="function"><span class="title">jenv</span></span>() &#123;</span><br><span class="line">  <span class="built_in">typeset</span> <span class="built_in">command</span></span><br><span class="line">  <span class="built_in">command</span>=<span class="string">"<span class="variable">$1</span>"</span></span><br><span class="line">  <span class="keyword">if</span> [ <span class="string">"<span class="variable">$#</span>"</span> -gt 0 ]; <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">shift</span></span><br><span class="line">  <span class="keyword">fi</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">case</span> <span class="string">"<span class="variable">$command</span>"</span> <span class="keyword">in</span></span><br><span class="line">  <span class="built_in">enable</span>-plugin|<span class="built_in">rehash</span>|shell|shell-options)</span><br><span class="line">    <span class="built_in">eval</span> `jenv <span class="string">"sh-<span class="variable">$command</span>"</span> <span class="string">"<span class="variable">$@</span>"</span>`;;</span><br><span class="line">  *)</span><br><span class="line">    <span class="built_in">command</span> jenv <span class="string">"<span class="variable">$command</span>"</span> <span class="string">"<span class="variable">$@</span>"</span>;;</span><br><span class="line">  <span class="keyword">esac</span></span><br><span class="line">&#125;: No such file or directory</span><br></pre></td></tr></table></figure><p>经过一番搜索，得到如下的解决办法，主要就是将<code>cat</code>替换为<code>echo</code>，这里我已经给jEnv提了个<a href="https://github.com/jenv/jenv/pull/265" target="_blank" rel="noopener">PR</a>，以消除这个干扰。</p><ul><li><p>Bash用户</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">echo</span> <span class="string">'export PATH="$HOME/.jenv/bin:$PATH"'</span> &gt;&gt; ~/.bash_profile</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">'eval "$(jenv init -)"'</span> &gt;&gt; ~/.bash_profile</span><br><span class="line">$ <span class="built_in">exec</span> <span class="variable">$SHELL</span> -l</span><br></pre></td></tr></table></figure></li><li><p>Zsh用户</p><figure class="highlight zsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">echo</span> <span class="string">'export PATH="$HOME/.jenv/bin:$PATH"'</span> &gt;&gt; ~/.zshrc</span><br><span class="line">$ <span class="built_in">echo</span> <span class="string">'eval "$(jenv init -)"'</span> &gt;&gt; ~/.zshrc</span><br><span class="line">$ <span class="built_in">exec</span> <span class="variable">$SHELL</span> -l</span><br></pre></td></tr></table></figure></li></ul><p>然后再次执行<code>jenv doctor</code>，得到如下信息：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[OK]No JAVA_HOME <span class="built_in">set</span></span><br><span class="line">[ERROR]Java binary <span class="keyword">in</span> path is not <span class="keyword">in</span> the jenv shims.</span><br><span class="line">[ERROR]Please check your path, or try using /path/to/java/home is not a valid path to java installation.</span><br><span class="line">PATH : /usr/<span class="built_in">local</span>/Cellar/jenv/0.5.2/libexec/libexec:/Users/xxx/.jenv/shims:/Users/xxx/.cargo/bin:/Users/xxx/.pyenv/shims:/Users/username/.pyenv:/Users/xxx/.cargo/bin:/Users/xxx/.pyenv/shims:/Users/username/.pyenv:/Users/xxx/.nvm/versions/node/v8.11.4/bin:/Users/xxx/bin:/usr/<span class="built_in">local</span>/bin:/Users/xxx/.cargo/bin:/usr/<span class="built_in">local</span>/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/<span class="built_in">local</span>/go/bin:/Users/xxx/Documents/Projects/golang/bin:/Users/xxx/Documents/Projects/golang/bin</span><br><span class="line">[OK]Jenv is correctly loaded</span><br></pre></td></tr></table></figure><p>为了能够正确的设置<code>JAVA_HOME</code>，最好开启<code>export</code>插件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ jenv <span class="built_in">enable</span>-plugin <span class="built_in">export</span></span><br><span class="line">$ <span class="built_in">exec</span> <span class="variable">$SHELL</span> -l</span><br></pre></td></tr></table></figure><p>如果你是Maven用户，建议开启Maven插件，使得Maven能够使用正确的JDK版本：</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">$</span><span class="bash"> jenv <span class="built_in">enable</span>-plugin maven</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> <span class="built_in">exec</span> <span class="variable">$SHELL</span> -l</span></span><br></pre></td></tr></table></figure><h2 id="管理不同版本的JDK"><a href="#管理不同版本的JDK" class="headerlink" title="管理不同版本的JDK"></a>管理不同版本的JDK</h2><h3 id="添加JDK"><a href="#添加JDK" class="headerlink" title="添加JDK"></a>添加JDK</h3><p>添加最新安装的JDK：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ jenv add $(/usr/libexec/java_home)</span><br></pre></td></tr></table></figure><p>如果<code>/usr/libexec/java_home</code>所指的位置不是你想要的，也可以手动指定目录：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ jenv add /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/</span><br></pre></td></tr></table></figure><h3 id="查看JDK版本"><a href="#查看JDK版本" class="headerlink" title="查看JDK版本"></a>查看JDK版本</h3><p>执行<code>jenv versions</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">  system</span><br><span class="line">* 1.8 (<span class="built_in">set</span> by JENV_VERSION environment variable)</span><br><span class="line">  1.8.0.191</span><br><span class="line">  13</span><br><span class="line">  openjdk64-13</span><br><span class="line">  oracle64-1.8.0.191</span><br></pre></td></tr></table></figure><p>默认情况下，system指的是系统中安装的最新版本的JDK。</p><h3 id="切换JDK版本"><a href="#切换JDK版本" class="headerlink" title="切换JDK版本"></a>切换JDK版本</h3><ul><li><p>Global<br>设置全局模式下的JDK版本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ jenv global 13</span><br><span class="line">$ <span class="built_in">exec</span> <span class="variable">$SHELL</span> -l </span><br><span class="line">$ java -version</span><br></pre></td></tr></table></figure></li><li><p>Local<br>在某个工作目录下设置JDK版本，会在当前目录下创建一个<code>.java-version</code>的文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ jenv <span class="built_in">local</span> 1.8</span><br><span class="line">$ <span class="built_in">exec</span> <span class="variable">$SHELL</span> -l </span><br><span class="line">$ java -version</span><br></pre></td></tr></table></figure></li><li><p>Shell<br>设置当前Shell session中的JDK版本：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ jenv shell 1.8</span><br><span class="line">$ java -version</span><br></pre></td></tr></table></figure></li></ul><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><blockquote><ul><li><a href="http://www.jenv.be/" target="_blank" rel="noopener">http://www.jenv.be/</a></li><li><a href="https://github.com/jenv/jenv" target="_blank" rel="noopener">https://github.com/jenv/jenv</a></li><li><a href="https://emcorrales.com/blog/install-oracle-jdk-macos-homebrew" target="_blank" rel="noopener">https://emcorrales.com/blog/install-oracle-jdk-macos-homebrew</a></li></ul></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/jenv.png&quot; alt=&quot;jEnv&quot;&gt;&lt;br&gt;我的Mac上已经有一个JDK8的版本了，这不&lt;a href=&quot;http://openjdk.java.net/projects/jdk/13/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;JDK13&lt;/a&gt;刚发布（2019-09-17），想快速的尝一尝鲜，就得安装多个版本的JDK了。这个对Node、Ruby、Python的使用者来说，已经不是个什么新鲜话题了，但是对于Java的使用者来说，似乎没有那么多的人受到过多版本的折磨（我是通过GitHub上&lt;a href=&quot;https://github.com/nvm-sh/nvm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;nvm&lt;/code&gt;&lt;/a&gt;、&lt;a href=&quot;https://github.com/rbenv/rbenv&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;rbenv&lt;/code&gt;&lt;/a&gt;、&lt;a href=&quot;https://github.com/pyenv/pyenv&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;pyenv&lt;/code&gt;&lt;/a&gt;、&lt;a href=&quot;https://github.com/jenv/jenv&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;jenv&lt;/code&gt;&lt;/a&gt;的Star数量臆测出这个结论的 :P）。&lt;/p&gt;
    
    </summary>
    
      <category term="Java" scheme="https://codinglife.tech/categories/Java/"/>
    
    
      <category term="JDK" scheme="https://codinglife.tech/tags/JDK/"/>
    
      <category term="jEnv" scheme="https://codinglife.tech/tags/jEnv/"/>
    
  </entry>
  
  <entry>
    <title>JVM 执行 Java 程序时的内存区域划分</title>
    <link href="https://codinglife.tech/2019/09/java-virtual-machine-runtime-memory-layout/"/>
    <id>https://codinglife.tech/2019/09/java-virtual-machine-runtime-memory-layout/</id>
    <published>2019-09-17T02:28:03.000Z</published>
    <updated>2020-12-02T03:05:15.727Z</updated>
    
    <content type="html"><![CDATA[<p><img src="/images/jvm-memory-layout.png" alt="JVM Memory Layout"></p><p>在学习 Java 虚拟机（后面简称：<code>JVM</code>）中的垃圾回收机制（GC）之前，先需要了解 在 JVM 中的 Java 程序（class 文件）加载到内存之后到底是怎么存的。在阅读了 <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5" target="_blank" rel="noopener"><strong>JVM规范</strong></a>和周志明的<a href="https://book.douban.com/subject/24722612/" target="_blank" rel="noopener"><strong>《深入理解Java虚拟机（第2版）》</strong></a>之后，总结一下JVM中的内存划分以及各个区域的作用。</p><a id="more"></a><p>在JVM规范中定义了5种运行时的数据区域：程序计数器（Program Counter Register）、Java虚拟机栈（JVM Stacks）、堆（Heap）、方法区（Method Area）、运行时常量池（Runtime Constant Pool）、本地方法栈（Native Method Stack）。在周志明的书中还提到了直接内存（Direct Memory），它并不是JVM运行时数据区域的一部分，在JVM的规范中也没有相关的定义。下面分别来说明各自的用途。</p><h2 id="程序计数器"><a href="#程序计数器" class="headerlink" title="程序计数器"></a>程序计数器</h2><p>程序计数器，也叫PC Register。它的用途很单一，但是却是很多功能的基础。如果线程当前执行的是Native方法，那么寄存器里的值就是Undefined；如果线程当前执行的是非Native方法，那么寄存器里的值就是当前执行的JVM字节码指令的地址。像我们常用的分支、循环、跳转、异常处理、线程恢复等都依赖于它。</p><p>由于JVM支持多个线程同时执行，所以每个线程都有一个独立的程序计数器，各个线程互不影响，这类内存区域也称之为<strong>线程私有</strong>的。</p><h2 id="Java虚拟机栈"><a href="#Java虚拟机栈" class="headerlink" title="Java虚拟机栈"></a>Java虚拟机栈</h2><p>虚拟机栈也是<strong>线程私有</strong>的，随着一个线程的创建而创建，主要用来存储栈帧（Stack Frame）。什么是栈帧呢？在Java中，每个方法在执行时就会先创建一个栈帧并放入虚拟机栈中，在方法执行完毕时再从虚拟机栈中移除该栈帧。它主要用来存储局部变量表、操作数栈、动态链接、方法出口等信息。我们常说的堆（Heap）和栈（Stack）中的栈，指的就是虚拟机栈。</p><p>在JVM规范中并没有对虚拟机栈空间的大小做限制，可以设置为固定大小的，也可以设置为可扩展的。但是在规范中定义了两种异常情况：</p><ul><li>如果计算时请求的栈空间大于虚拟机栈的最大值，则会抛出<code>StackOverflowError</code>异常；</li><li>如果虚拟机栈设置为可扩展的并且无法再获取更多内存时，则会抛出<code>OutOfMemoryError</code>异常。</li></ul><h2 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h2><p>相比而言，堆在JVM管理的内存区域中属于最大的一块，随着虚拟机的启动而创建，用来存储所有的class实例和数组，所有<strong>线程共享</strong>这一区域，该区域也是垃圾回收的主要区域。虽然JVM规范中说所有的对象实例都在该区域分配空间，但是随着JIT技术的逐步发展，这一说法也不严谨了。</p><p>堆空间的大小也可以设置为固定大小，或者可扩展的。但不管是何种方式，规范中还是定义了一种异常场景：</p><ul><li>如果计算需要更多的堆空间而无法满足时，则会抛出<code>OutOfMemoryError</code>异常。</li></ul><h2 id="方法区"><a href="#方法区" class="headerlink" title="方法区"></a>方法区</h2><p>方法区和堆一样，也是随着虚拟机启动而创建，所有<strong>线程共享</strong>，主要用来存储被JVM加载的类信息、常量、静态变量等信息。</p><p>JVM规范中并未严格要求要对该区域进行垃圾回收，但是HotSpot虚拟机在垃圾回收的时候还是会考虑该区域，在分代垃圾回收中所说的“<strong>永久代</strong>”指的就是方法区。方法区的大小也可以设置为固定大小，或者可扩展的。但不管是何种方式，规范中还是定义了一种异常场景：</p><ul><li>如果计算需要更多的方法区空间而无法满足时，则会抛出<code>OutOfMemoryError</code>异常。</li></ul><h2 id="运行时常量池"><a href="#运行时常量池" class="headerlink" title="运行时常量池"></a>运行时常量池</h2><p>运行时常量池是方法区的一部分，用于存储编译期生成的各种字面量和符号引用。在Java中并不要求常量一定只有编译期才能产生，运行期间也可能将新的常量放入池中，例如<code>String</code>类的<code>intern()</code>方法。</p><p>每个运行时常量池都是随着一个类或者接口的创建而创建的。在规范中定义了一种异常场景：</p><ul><li>在创建一个类或者接口时，如果运行时的常量池无法分配到足够的空间时，则会抛出<code>OutOfMemoryError</code>异常。</li></ul><h2 id="本地方法栈"><a href="#本地方法栈" class="headerlink" title="本地方法栈"></a>本地方法栈</h2><p>本地方法栈和虚拟机栈类似，也是<strong>线程私有</strong>的，随着一个线程的创建而创建，只不过虚拟机栈是用来服务Java方法调用，而本地方法栈是用来服务本地方法调用的。</p><p>在JVM规范中并没有对本地方法栈空间的大小做限制，可以设置为固定大小的，也可以设置为可扩展的。在规范中也定义了两种异常情况：</p><ul><li>如果计算时请求的栈空间大于本地方法栈的最大值，则会抛出<code>StackOverflowError</code>异常；</li><li>如果本地方法栈设置为可扩展的并且无法再获取更多内存时，则会抛出<code>OutOfMemoryError</code>异常。</li></ul><h2 id="直接内存"><a href="#直接内存" class="headerlink" title="直接内存*"></a>直接内存<sup>*</sup></h2><p>直接内存不受虚拟机参数的控制，在NIO中有一种基于通道（Channel）与缓冲区（Buffer)的I/O方式，它可以通过Native方法在堆外分配内存，然后通过DirectByteBuffer对象来引用这块内存。因为避免了在Java堆和Native堆之间来回复制数据，从而在某些场景中能够得到性能的提升。一旦使用的直接内存超过了物理内存的总和，则会抛出<code>OutOfMemoryError</code>异常。</p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><blockquote><ul><li><a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5" target="_blank" rel="noopener">https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5</a></li><li><a href="https://book.douban.com/subject/24722612/" target="_blank" rel="noopener">https://book.douban.com/subject/24722612/</a></li></ul></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;/images/jvm-memory-layout.png&quot; alt=&quot;JVM Memory Layout&quot;&gt;&lt;/p&gt;
&lt;p&gt;在学习 Java 虚拟机（后面简称：&lt;code&gt;JVM&lt;/code&gt;）中的垃圾回收机制（GC）之前，先需要了解 在 JVM 中的 Java 程序（class 文件）加载到内存之后到底是怎么存的。在阅读了 &lt;a href=&quot;https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;JVM规范&lt;/strong&gt;&lt;/a&gt;和周志明的&lt;a href=&quot;https://book.douban.com/subject/24722612/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;《深入理解Java虚拟机（第2版）》&lt;/strong&gt;&lt;/a&gt;之后，总结一下JVM中的内存划分以及各个区域的作用。&lt;/p&gt;
    
    </summary>
    
      <category term="Java" scheme="https://codinglife.tech/categories/Java/"/>
    
    
      <category term="JVM" scheme="https://codinglife.tech/tags/JVM/"/>
    
  </entry>
  
  <entry>
    <title>在MySQL中使用LAST_INSERT_ID获取唯一自增序列</title>
    <link href="https://codinglife.tech/2019/07/implement-sequence-with-mysql-last-insert-id/"/>
    <id>https://codinglife.tech/2019/07/implement-sequence-with-mysql-last-insert-id/</id>
    <published>2019-07-15T12:03:02.000Z</published>
    <updated>2020-12-02T03:08:12.122Z</updated>
    
    <content type="html"><![CDATA[<p>一般如果遇到生成全局唯一的自增ID的需求时，往往第一反应都是直接利用数据的Sequence对象，简单，直接了当。但是MySQL偏偏不支持Sequence对象，那我们该如何是好呢？</p><h2 id="什么是Sequence"><a href="#什么是Sequence" class="headerlink" title="什么是Sequence"></a>什么是Sequence</h2><p>Sequence也叫做序列，一般用做表的主键，或者一些项目的编号等。一般具有以下几个特点：</p><ul><li>全表唯一</li><li>自增</li><li>不一定严格连续（中间由于事务的回滚可能会出现<code>洞</code>，比如1，2，3，5，6）</li></ul><a id="more"></a><p>在常见的几种数据库中，Oracle、SQL Server都内置有Sequence对象，具体用法就不在此赘述了。在本文中我们来讨论一下如何在原生不支持Sequence的MySQL（目前最新的大版本为8.0）中模拟出Sequence的效果。</p><h2 id="如何在MySQL中模拟Sequence"><a href="#如何在MySQL中模拟Sequence" class="headerlink" title="如何在MySQL中模拟Sequence"></a>如何在MySQL中模拟Sequence</h2><p>MySQL中的<code>auto_increment</code>一般是用来生成表的主键，本身能够生成自增的唯一ID，但是一张表只能有一个列带有<code>auto_increment</code>属性。在实际项目中，我们可能需要不止一种序列号，比如项目编号（PROJ-001，PROJ-002…）、发票编号（INV-0001，INV-0002…），订单编号（ORD-0001，ORD-0002…）等等，下面将通过<code>auto_increment</code>和<code>LAST_INSERT_ID</code>相结合实现该功能。</p><h3 id="LAST-INSERT-ID函数"><a href="#LAST-INSERT-ID函数" class="headerlink" title="LAST_INSERT_ID函数"></a>LAST_INSERT_ID函数</h3><p>该函数有两种形式：<a href="https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_last-insert-id" target="_blank" rel="noopener"><code>LAST_INSERT_ID()</code></a>, <a href="https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_last-insert-id" target="_blank" rel="noopener"><code>LAST_INSERT_ID(expr)</code></a>。无参的形式会返回最近一次执行<code>INSERT</code>语句时<code>auto_increment</code>的值；带<code>expr</code>的形式会返回表达式的值，并且该值会被记住，在下一次调用<code>LAST_INSERT_ID()</code>时也返回该值。下面我们来看一个例子。</p><p>首先创建一张表：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE user (</span><br><span class="line">    id INT AUTO_INCREMENT PRIMARY KEY,</span><br><span class="line">    name VARCHAR(50) NOT NULL</span><br><span class="line">);</span><br></pre></td></tr></table></figure><p>然后插入两条数据：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">INSERT INTO user(name) VALUES(&apos;张三&apos;);</span><br><span class="line">INSERT INTO user(name) VALUES(&apos;李四&apos;);</span><br><span class="line"></span><br><span class="line">SELECT LAST_INSERT_ID();</span><br></pre></td></tr></table></figure><p>此时得到的结果是2。</p><p><strong>注意：</strong>如果是一条语句插入多条值，则返回的是插入第一条时自动生成的ID，而不是最后一条的。</p><p>比如我们再插入三条数据，不过换个写法：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">INSERT INTO user(name) </span><br><span class="line">VALUES(&apos;王五&apos;),</span><br><span class="line">      (&apos;赵六&apos;),</span><br><span class="line">      (&apos;郑七&apos;);</span><br><span class="line"></span><br><span class="line">SELECT LAST_INSERT_ID();</span><br></pre></td></tr></table></figure><p>此时得到的结果是3，而<strong>不是</strong>5。</p><h3 id="获取自增序列"><a href="#获取自增序列" class="headerlink" title="获取自增序列"></a>获取自增序列</h3><p>在实际的项目中我们完全可以换个方式，避免上面👆的情况，作为一个程序员，何必没有困难，制造困难为难自己呢？接着看下一个更加通用的例子。</p><p>创建另一张表并初始化数据：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">CREATE TABLE sequence (</span><br><span class="line">    id INT AUTO_INCREMENT PRIMARY KEY,</span><br><span class="line">    seq_type VARCHAR(50) NOT NULL,</span><br><span class="line">    year INT NOT NULL,</span><br><span class="line">    current_val BIGINT NOT NULL</span><br><span class="line">);</span><br><span class="line"></span><br><span class="line">INSERT INTO sequence(seq_type, year, current_val) VALUES(&apos;INVOICE&apos;, 2019, 0);</span><br></pre></td></tr></table></figure><p>每次在获取<code>current_val</code>之前，先通过<code>LAST_INSERT_ID(current_val + 1)</code>更新：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">UPDATE sequence </span><br><span class="line">SET current_val = LAST_INSERT_ID(current_val + 1)</span><br><span class="line">WHERE seq_type = &apos;INVOICE&apos; AND year = 2019;</span><br><span class="line"></span><br><span class="line">SELECT LAST_INSERT_ID();</span><br></pre></td></tr></table></figure><p>这样每次都能获取自增之后的值了，但是也有例外的情况。比如两个人同时在获取新的值，A先做了update操作，然后B也做了update操作，然后A的操作由于某种原因回滚了，B的操作成功了，此时序列中间就会出现一个<code>洞</code>。虽然不是严格连续的，但是在大多数业务场景中，已经满足要求了。</p><p><strong>还需要注意的是</strong>，如果<code>seq_type</code>或者<code>year</code>条件不满足，那么这里的<code>SELECT LAST_INSERT_ID();</code>就会始终返回上一次的值，可能会导致意想不到的的错误。</p><h2 id="LAST-INSERT-ID-vs-MAX"><a href="#LAST-INSERT-ID-vs-MAX" class="headerlink" title="LAST_INSERT_ID() vs. MAX()"></a>LAST_INSERT_ID() vs. MAX()</h2><p><code>LAST_INSERT_ID()</code>是以数据库连接为基础的，即使有多个人同时通过多个连接获取Sequence也不会有问题，每个客户端会获取到属于他自己的序列号，不用担心会受到其他客户端的影响，或者影响其他客户端。在这种情况下，<code>MAX()</code>恐怕就不能正常工作了。</p><h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><blockquote><ul><li><a href="https://www.percona.com/community-blog/2018/10/12/generating-identifiers-auto_increment-sequence/" target="_blank" rel="noopener">https://www.percona.com/community-blog/2018/10/12/generating-identifiers-auto_increment-sequence/</a></li><li><a href="https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_last-insert-id" target="_blank" rel="noopener">https://dev.mysql.com/doc/refman/8.0/en/information-functions.html#function_last-insert-id</a></li><li><a href="http://www.mysqltutorial.org/mysql-last_insert_id.aspx" target="_blank" rel="noopener">http://www.mysqltutorial.org/mysql-last_insert_id.aspx</a></li></ul></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;一般如果遇到生成全局唯一的自增ID的需求时，往往第一反应都是直接利用数据的Sequence对象，简单，直接了当。但是MySQL偏偏不支持Sequence对象，那我们该如何是好呢？&lt;/p&gt;
&lt;h2 id=&quot;什么是Sequence&quot;&gt;&lt;a href=&quot;#什么是Sequence&quot; class=&quot;headerlink&quot; title=&quot;什么是Sequence&quot;&gt;&lt;/a&gt;什么是Sequence&lt;/h2&gt;&lt;p&gt;Sequence也叫做序列，一般用做表的主键，或者一些项目的编号等。一般具有以下几个特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;全表唯一&lt;/li&gt;
&lt;li&gt;自增&lt;/li&gt;
&lt;li&gt;不一定严格连续（中间由于事务的回滚可能会出现&lt;code&gt;洞&lt;/code&gt;，比如1，2，3，5，6）&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
      <category term="Database" scheme="https://codinglife.tech/categories/Database/"/>
    
    
      <category term="MySQL" scheme="https://codinglife.tech/tags/MySQL/"/>
    
  </entry>
  
</feed>
