<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>真没什么逻辑</title>
    <link>https://wechat2rss.xlab.app/feed/347c1a20a1a8ff2b789e454e938addadc85b2c4b.xml</link>
    <description>系统设计、微服务架构和云原生技术&#xA;(wechat feed made by @ttttmr https://wechat2rss.xlab.app)</description>
    <managingEditor> (真没什么逻辑)</managingEditor>
    <image>
      <url>https://wx.qlogo.cn/mmhead/Q3auHgzwzM5VXQ4T0MTD4VOPicXI8lnt0ELqZ6oDj8IJBecbpdqDXTg/0</url>
      <title>真没什么逻辑</title>
      <link>https://wechat2rss.xlab.app/feed/347c1a20a1a8ff2b789e454e938addadc85b2c4b.xml</link>
    </image>
    <item>
      <title>Kubernetes、集群联邦和资源分发</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247485054&amp;idx=1&amp;sn=2dbe9e4734b2eb2c1c74041d9ff3edc8</link>
      <description>谈谈 Kubernetes 集群的联邦</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2022-03-21 09:00</span> <span style="display: inline-block;"></span>
</p>

<p>谈谈 Kubernetes 集群的联邦</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=0d48ddf4&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7boBctGd3xE9XAk1PmlPvJ5fVj0oicrCcJkGZ0CcELzRekZY3gpugcjlP0Xgl4gpbsPRubHrU5NNic0Q%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 从比较早的版本就声称单机群可以支持 5,000 节点，而且也没有计划在短期内提高单个 Kubernetes 集群支撑的节点数，如果需要在 Kubernetes 中支持 5,000 以上的节点，更推荐使用集群联邦（Federation）的方式。</p><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">People frequently ask how far we are going to go in improving Kubernetes scalability. Currently we do not have plans to increase scalability beyond 5000-node clusters (within our SLOs) in the next few releases. If you need clusters larger than 5000 nodes, we recommend to use federation to aggregate multiple Kubernetes clusters.[^1]</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: Scalability updates in Kubernetes 1.6: 5,000 node and 150,000 pod clusters <a href="https://kubernetes.io/blog/2017/03/scalability-updates-in-kubernetes-1-6/" target="_blank">https://kubernetes.io/blog/2017/03/scalability-updates-in-kubernetes-1-6/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">对云服务稍微熟悉一点的读者朋友应该都知道可用区（Available Zone、AZ）的概念，我们在使用 AWS、谷歌云等服务提供的实例时，需要选择实例所在的区域以及可用区。区域（Region）是地理上的概念，以 AWS 为例，它在北京和宁夏都有数据中心，每个数据中心都有 3 个可用区。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="1.5593984962406016" data-type="png" data-w="1330" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7d9a4c16&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boBctGd3xE9XAk1PmlPvJ5fibeso4ORFOg7EzjQKvCkbXGkpvicicj53ECywADOHAlibqDF0yATiaXqV7A%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">aws-asia-pacific-region-maps</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 1 - AWS 亚太地区</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作为云服务厂商提供的 AZ，每个 AZ 可能会包含几万、甚至十几万节点，想使用单个集群管理这种规模的节点数量是非常困难的，所以管理多个集群成为了该规模下必须面对的问题。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">集群联邦听起来是一种非常高端的技术，但是实际上我们可以将它理解成更灵活、易用的多集群。当我们仅仅提到多个集群时，这些集群更像是一些独立的孤岛，彼此之间没有太多的联系，但是联邦集群将这些独立的集群『打包』成了一个整体，上层的用户不需要关心集群这一层级。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.46361746361746364" data-type="png" data-w="962" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=2786123c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boBctGd3xE9XAk1PmlPvJ5fwO0cic4oujrexcgvF2B9ABLib39cpo0d9X3y9u9KD0ZTWVHPBVTe405A%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">multi-cluster-and-federation</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 2 - 多集群和集群联邦</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">联邦集群引入的新的控制面板需要两个比较重要的功能：跨集群的服务发现和跨集群的调度。其中，跨集群服务发现打通了多个集群的网络，让请求可以跨越不同集群的边界；而跨集群调度可以保证服务的稳定性以及可用性。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在这篇文章中，我们将以 kubefed 和 karmada 两个集群联邦项目为例介绍集群联邦可能遇到的问题，多个项目的对比也能让我们清晰地意识到不同设计选择带来的影响。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>kubefed</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">kubefed 是个非常老牌的 Kubernetes 集群联邦项目，这个项目目前就挂在官方的仓库下，由官方的多集群兴趣小组开发，到今天已经将近四年了。从这个项目的年头来看，Kubernetes 集群联邦已经是一个非常老的话题了，但是到今天也没有比较完美的解决方案。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.75" data-type="png" data-w="960" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=d40305b8&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boBctGd3xE9XAk1PmlPvJ5f4UTVLibBZ9FUWtSUibyZbzgYySfTV23khyZabLgY9ibVtMTgsASGDSCHg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">kubefed-architecture</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 3 - kubefed 架构</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">所有集群联邦的方案都需要我们把资源从管理集群同步资源到联邦集群，传播（Propagation）是该项目引入的一个术语，它会将宿主集群中的资源分配到所有的联邦集群中。这个机制会需要引入以下三个概念：Templates、Placement 和 Overrides：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.3924050632911392" data-type="png" data-w="1106" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=44015697&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boBctGd3xE9XAk1PmlPvJ5fJZ7sZUaB3ajHe1ibFkZMMqaK03JEFTiauSzAzdtzFQbKGJxuSLgQCGbg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">kubefed-concepts</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 4 - Kubefed 概念</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">其中 Template 中定义了该资源的一些基本信息，以 Deployment 为例，可能包含部署的容器镜像、环境变量以及实例数等信息；Placement 决定该资源需要部署在哪些集群中，如上图所示，该资源会部署在 Cluster1、Cluster2 两个集群中；最后的 Override 是会覆写原有 Template 中的资源，以满足当前集群的一些特定需求，例如实例数、拉镜像使用的秘钥等与集群有关的属性。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在具体的实现上，kubefed 选择为集群中的所有资源生成对应的联邦资源，例如 Deployment 和对应的 FederatedDeployment。联邦资源中的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">spec</code> 字段存储了 Deployment 资源的模板，而 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">overrides</code> 中定义了资源同步到不同集群时需要做的变更。</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_svg/qE9MKluetOmuevzHpToZ4fl41CVggkdoJY37korpCFal1ogkLyZF4X5BskMHq1ETJKZrtmrnegPLwjewg4vibkUicqVgfPpLfP/640?wx_fmt=svg&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">FederatedDeployment</span><br/><span style="color: #d14;line-height: 26px;">...</span><br/><span style="line-height: 26px;">spec:</span><br/>  <span style="color: #d14;line-height: 26px;">...</span><br/>  <span style="line-height: 26px;">overrides:</span><br/>  <span style="color: #998;font-style: italic;line-height: 26px;"># Apply overrides to cluster1</span><br/>    <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">clusterName:</span> <span style="color: #d14;line-height: 26px;">cluster1</span><br/>      <span style="line-height: 26px;">clusterOverrides:</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># Set the replicas field to 5</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">path:</span> <span style="color: #d14;line-height: 26px;">&#34;/spec/replicas&#34;</span><br/>          <span style="line-height: 26px;">value:</span> <span style="color: #008080;line-height: 26px;">5</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># Set the image of the first container</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">path:</span> <span style="color: #d14;line-height: 26px;">&#34;/spec/template/spec/containers/0/image&#34;</span><br/>          <span style="line-height: 26px;">value:</span> <span style="color: #d14;line-height: 26px;">&#34;nginx:1.17.0-alpine&#34;</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># Ensure the annotation &#34;foo: bar&#34; exists</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">path:</span> <span style="color: #d14;line-height: 26px;">&#34;/metadata/annotations&#34;</span><br/>          <span style="line-height: 26px;">op:</span> <span style="color: #d14;line-height: 26px;">&#34;add&#34;</span><br/>          <span style="line-height: 26px;">value:</span><br/>            <span style="line-height: 26px;">foo:</span> <span style="color: #d14;line-height: 26px;">bar</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># Ensure an annotation with key &#34;foo&#34; does not exist</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">path:</span> <span style="color: #d14;line-height: 26px;">&#34;/metadata/annotations/foo&#34;</span><br/>          <span style="line-height: 26px;">op:</span> <span style="color: #d14;line-height: 26px;">&#34;remove&#34;</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># Adds an argument `-q` at index 0 of the args list</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># this will obviously shift the existing arguments, if any</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">path:</span> <span style="color: #d14;line-height: 26px;">&#34;/spec/template/spec/containers/0/args/0&#34;</span><br/>          <span style="line-height: 26px;">op:</span> <span style="color: #d14;line-height: 26px;">&#34;add&#34;</span><br/>          <span style="line-height: 26px;">value:</span> <span style="color: #d14;line-height: 26px;">&#34;-q&#34;</span><br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">联邦集群的控制平面会根据上述 FederatedDeployment 为不同的集群分别生成对应的 Deployment 并推送到下层管理的联邦集群中，这也是集群联邦 kubefed 解决的主要问题。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">从理论上讲，所有的集群联邦组件只要实现了<strong>定义模板</strong>和<strong>覆写字段</strong>的能力就满足了全部需求，然而在实际应用上，不同的方案也会提供一些类似语法糖的特性帮助我们更好的在不同集群之间实现更复杂的资源分发能力。kubefed 提供了 ReplicaSchedulingPreference^2 在不同集群中实现更加智能的分发策略：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_svg/qE9MKluetOmuevzHpToZ4fl41CVggkdoJY37korpCFal1ogkLyZF4X5BskMHq1ETJKZrtmrnegPLwjewg4vibkUicqVgfPpLfP/640?wx_fmt=svg&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="line-height: 26px;">apiVersion:</span> <span style="color: #d14;line-height: 26px;">scheduling.kubefed.io/v1alpha1</span><br/><span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">ReplicaSchedulingPreference</span><br/><span style="line-height: 26px;">metadata:</span><br/>  <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">test-deployment</span><br/>  <span style="line-height: 26px;">namespace:</span> <span style="color: #d14;line-height: 26px;">test-ns</span><br/><span style="line-height: 26px;">spec:</span><br/>  <span style="line-height: 26px;">targetKind:</span> <span style="color: #d14;line-height: 26px;">FederatedDeployment</span><br/>  <span style="line-height: 26px;">totalReplicas:</span> <span style="color: #008080;line-height: 26px;">9</span><br/>  <span style="line-height: 26px;">clusters:</span><br/>    <span style="line-height: 26px;">A:</span><br/>      <span style="line-height: 26px;">minReplicas:</span> <span style="color: #008080;line-height: 26px;">4</span><br/>      <span style="line-height: 26px;">maxReplicas:</span> <span style="color: #008080;line-height: 26px;">6</span><br/>      <span style="line-height: 26px;">weight:</span> <span style="color: #008080;line-height: 26px;">1</span><br/>    <span style="line-height: 26px;">B:</span><br/>      <span style="line-height: 26px;">minReplicas:</span> <span style="color: #008080;line-height: 26px;">4</span><br/>      <span style="line-height: 26px;">maxReplicas:</span> <span style="color: #008080;line-height: 26px;">8</span><br/>      <span style="line-height: 26px;">weight:</span> <span style="color: #008080;line-height: 26px;">2</span><br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上述调度的策略可以实现工作负载在不同集群之间的权重，在集群资源不足甚至出现问题时将实例迁移到其他集群，这样既能够提高服务部署的灵活性和可用性，基础架构工程师也可以更好地平衡多个集群的负载。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">kubefed 在较早的版本中还是包含跨集群的服务发现功能的，但是在最新的分支中已经将与服务发现的相关功能都移除了，这可能也是因为跨集群的服务发现功能非常复杂，社区中目前已经有很多第三方的工具可以提供基于 DNS 的联邦 Ingress 资源，不需要 kubefed 的支持[^3]。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^3]: kubefed: remove FederatedIngress feature #1284 <a href="https://github.com/kubernetes-sigs/kubefed/issues/1284" target="_blank">https://github.com/kubernetes-sigs/kubefed/issues/1284</a></p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>karmada</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubefed 虽然是 Kubernetes 社区早期的集群联邦项目，虽然已经经历了比较长的时间，但是一直都处于实验阶段，到今天项目基本也陷入停止维护的状态。Karmada 是对 Kubefed 项目的延续，它继承了来自 kubefed 中的一些概念，目前也处于积极开发和维护中，这也是目前社区中比较活跃和成熟的集群联邦项目。</p><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">Notice: this project is developed in continuation of Kubernetes Federation v1 and v2. Some basic concepts are inherited from these two versions.^4</p></blockquote><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.8838475499092558" data-type="png" data-w="551" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=4a889db6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boBctGd3xE9XAk1PmlPvJ5fmu4N9vLLo6IJibCTQHXabhZWnzeFqKXUXibibEn9DhLYJm65SPu7szyDw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">karmada-architecture</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 5 - Karmada 架构</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Karmada 这种多级的集群管理都是天然的树形结构，如上图所示，根节点上的 Karmada 控制面板主要包含三个主要组件，API Server、Controller Manager 和 Scheduler，相信了解 Kubernetes 控制面板的读者应该都可以想象到这三个不同组件的作用。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">需要注意的是 Karmada 的 Controller Manager 中不包含 Kubernetes Controller Manager 中控制器，如果我们在 Karmada 集群中创建了 Deployment 资源，Karmada 的控制面也不会根据 Deployment 创建 Pod，它只负责 Karmada 原生 CRD 的同步和管理工作，包括 Cluster、PropagationPolicy、ResourceBinding 和 Work 资源。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.9763113367174281" data-type="png" data-w="591" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=05172689&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boBctGd3xE9XAk1PmlPvJ5fHwMibWqwYJgZsefzIicFFt6T7wXJJxZZCib9OGIv9WHkHnAArSiaDAXicfQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">karmada-resource-relation</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 5 - Karmada 概念</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">正如我们在上面提到的，Karmada 中的概念也几乎全盘继承自 Kubefed，我们可以根据上图总结出 Karmada 将资源模板转换成成员集群的资源需要经过以下几个步骤：</p><ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Deployment、Service、ConfigMap 等资源模板经过 PropagationPolicy 生成一组 ResourceBinding，每个 ResourceBinding 都对应特定的成员集群；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">ResourceBinding 根据 OverridePolicy 改变一些资源以适应的不同成员集群，例如：集群名等参数，这些资源定义会存储在 Work 对象中；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Work 对象中存储的资源定义会被提交到成员集群中，成员集群中的 Controller Manager 等控制面板组件会负责这些资源的处理，例如：根据 Deployment 创建 Pod 等。</section></li></ol><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_svg/qE9MKluetOmuevzHpToZ4fl41CVggkdoJY37korpCFal1ogkLyZF4X5BskMHq1ETJKZrtmrnegPLwjewg4vibkUicqVgfPpLfP/640?wx_fmt=svg&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="color: #998;font-style: italic;line-height: 26px;"># propagationpolicy.yaml</span><br/><span style="line-height: 26px;">apiVersion:</span> <span style="color: #d14;line-height: 26px;">policy.karmada.io/v1alpha1</span><br/><span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">PropagationPolicy</span><br/><span style="line-height: 26px;">metadata:</span><br/>  <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">example-policy</span><br/><span style="line-height: 26px;">spec:</span><br/>  <span style="line-height: 26px;">resourceSelectors:</span><br/>    <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">apiVersion:</span> <span style="color: #d14;line-height: 26px;">apps/v1</span><br/>      <span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">Deployment</span><br/>      <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">nginx</span><br/>  <span style="line-height: 26px;">placement:</span><br/>    <span style="line-height: 26px;">clusterAffinity:</span><br/>      <span style="line-height: 26px;">clusterNames:</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="color: #d14;line-height: 26px;">member1</span><br/><span style="color: #998;font-style: italic;line-height: 26px;"># overridepolicy.yaml</span><br/><span style="line-height: 26px;">apiVersion:</span> <span style="color: #d14;line-height: 26px;">policy.karmada.io/v1alpha1</span><br/><span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">OverridePolicy</span><br/><span style="line-height: 26px;">metadata:</span><br/>  <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">example-override</span><br/>  <span style="line-height: 26px;">namespace:</span> <span style="color: #d14;line-height: 26px;">default</span><br/><span style="line-height: 26px;">spec:</span><br/>  <span style="line-height: 26px;">resourceSelectors:</span><br/>    <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">apiVersion:</span> <span style="color: #d14;line-height: 26px;">apps/v1</span><br/>      <span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">Deployment</span><br/>      <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">nginx</span><br/>  <span style="line-height: 26px;">overrideRules:</span><br/>    <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">targetCluster:</span><br/>        <span style="line-height: 26px;">clusterNames:</span><br/>          <span style="color: #990073;line-height: 26px;">-</span> <span style="color: #d14;line-height: 26px;">member1</span><br/>      <span style="line-height: 26px;">overriders:</span><br/>        <span style="line-height: 26px;">plaintext:</span><br/>          <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">path:</span> <span style="color: #d14;line-height: 26px;">&#34;/metadata/annotations&#34;</span><br/>            <span style="line-height: 26px;">operator:</span> <span style="color: #d14;line-height: 26px;">add</span><br/>            <span style="line-height: 26px;">value:</span><br/>              <span style="line-height: 26px;">foo:</span> <span style="color: #d14;line-height: 26px;">bar</span><br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Karmada 与 Kubefed 的概念是非常相似的，它们都需要在 API 中解决两个问题：资源模板应该部署到哪个集群中、资源模板在该集群中需要实现做哪些特定变更。上述两个问题是『模板实例化』的过程中一定会面对的，而 Kubefed 和 Karmada 两个组件也选择了不同的接口：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.8341384863123994" data-type="png" data-w="621" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=48d6de0e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boBctGd3xE9XAk1PmlPvJ5fh5Xxvp0vz3c6rgqqkusaddtl47hxiaYCicib4CVypEvvELbuu6utjYpPA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">kubefed-karmada-api</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 6 - Kubefed 和 Karmada API 对比</strong></p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Kubefed 为所有的 Kubernetes 原生资源创建了对应的联邦资源，例如 Deployment 和 FederatedDeployment，Placement 和 Override 的定义都包含在新的 FederatedDeployment 中；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Karmada 保留了 Kubernetes 的全部原生资源，同时引入新的 PropagationPolicy 和 OverridePolicy；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上述两种方案各有优缺点，创建对应资源可以将全部的定义整合在一起，但是如果在集群中引入了新的自定义资源（Custom Resource）需要引入额外的工作；创建独立的 PropagationPolicy 和 OverridePolicy 可以简化引入新资源需要的步骤，但是可能需要额外的看板才能看到资源模板最终生成的资源。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在该场景下，作者更倾向于 Karmada 的做法，因为随着 Kubernetes 中资源种类的增加，使用 PropagationPolicy 和 OverridePolicy 能够降低集群的维护成本，我们不需要为每一个新的资源创建联邦类型的映射，而引入额外的看板展示最终生成的资源也不是不能接受的成本。</p><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">需要注意的是，我们在 Karmada 控制面只会分发 Deployment 等『高级』资源，管理集群中的 Controller Manager 不会根据 Deployment 创建 Pod，这样才能减轻控制面的压力，实现多集群联邦的管理。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了资源的分发之外，Karmada 还在 PropagationPolicy 加入 replicaScheduling 字段管理工作负载实例的分发，能够提供故障转移和按照权重分发的功能：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_svg/qE9MKluetOmuevzHpToZ4fl41CVggkdoJY37korpCFal1ogkLyZF4X5BskMHq1ETJKZrtmrnegPLwjewg4vibkUicqVgfPpLfP/640?wx_fmt=svg&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="color: #998;font-style: italic;line-height: 26px;"># duplicated.yaml</span><br/><span style="line-height: 26px;">replicaScheduling:</span><br/>  <span style="line-height: 26px;">replicaSchedulingType:</span> <span style="color: #d14;line-height: 26px;">Duplicated</span><br/><span style="color: #998;font-style: italic;line-height: 26px;"># divided.yaml</span><br/><span style="line-height: 26px;">replicaScheduling:</span><br/>  <span style="line-height: 26px;">replicaDivisionPreference:</span> <span style="color: #d14;line-height: 26px;">Weighted</span><br/>  <span style="line-height: 26px;">replicaSchedulingType:</span> <span style="color: #d14;line-height: 26px;">Divided</span><br/>  <span style="line-height: 26px;">weightPreference:</span><br/>    <span style="line-height: 26px;">staticWeightList:</span><br/>      <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">targetCluster:</span><br/>          <span style="line-height: 26px;">clusterNames:</span><br/>            <span style="color: #990073;line-height: 26px;">-</span> <span style="color: #d14;line-height: 26px;">member1</span><br/>        <span style="line-height: 26px;">weight:</span> <span style="color: #008080;line-height: 26px;">1</span><br/>      <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">targetCluster:</span><br/>          <span style="line-height: 26px;">clusterNames:</span><br/>            <span style="color: #990073;line-height: 26px;">-</span> <span style="color: #d14;line-height: 26px;">member2</span><br/>        <span style="line-height: 26px;">weight:</span> <span style="color: #008080;line-height: 26px;">2</span><br/></code></pre><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.8754325259515571" data-type="png" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" data-w="" src="https://wechat2rss.xlab.app/img-proxy/?k=ff675027&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boBctGd3xE9XAk1PmlPvJ5fS1q2MYYIALh8cIwJNdShu3unZ090Q2XMN2pGnsjRlPonJGaUj7xVicQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">karmada-deployment-failover</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 7 - Karmada 故障转移</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Karmada 的调度器需要解决工作负载<strong>调度到哪里</strong>和<strong>调度多少</strong>的问题，与更细粒度的 Kubernetes 的调度器相比，这是一种粒度较粗的调度，因为上下文的不足，它不需要也没有办法保证调度的全局最优解，而提供跨集群的部署和故障转移就已经可以满足常见的需求了。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">集群的联邦主要解决的还是两个问题，单集群的扩展性、跨可用区（地域、云）的集群管理。在一个单个集群可以做到 100k 节点的系统中，我们几乎听不到联邦这一概念，如果 Kubernetes 集群的控制面足够强大、能够承担足够多的压力，那么多集群和集群联邦的概念在社区中也不会特别热门。随着 Kubernetes 项目逐渐走入成熟，Pipeline、联邦和集群管理这些细分领域也都会逐渐完善，相信我们会逐渐在社区中看到成熟的联邦集群解决方案。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>推荐阅读</h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-1"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484931&amp;idx=1&amp;sn=7418d82eaee37c9904669f77f40b57f2&amp;chksm=fe795908c90ed01e0f9e2a1ac9c3714e06ca1d2bb223de8bcf5c7b4f7bbcc5ac82eb765af480&amp;scene=21#wechat_redirect" textvalue="谈谈 Kubernetes 的问题和局限性 " linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2">谈谈 Kubernetes 的问题和局限性 </a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">KEP-1645: Multi-Cluster Services API <a href="https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api" target="_blank">https://github.com/kubernetes/enhancements/tree/master/keps/sig-multicluster/1645-multi-cluster-services-api</a></section></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>书籍推荐</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">各位读者朋友，很高兴大家通过本公众号学习 Go 语言，感谢一路相伴！《Go语言设计与实现》 的纸质版图书到今天已经四印，总印数超过 10,000 册，有需要的朋友请点击链接购买，可以先在 draven.co/golang 阅读电子版的内容。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img class="rich_pages wxw-img" data-ratio="1" data-type="png" data-w="1080" style="height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7582f426&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFSDPh64PYmIrzuWdoYEHynWWXopL3ocicDLscbYl6rdicMsvwoXicgBYUWA%2F640%3Fwx_fmt%3Dpng"/></p><section><mpcps frameborder="0" class="js_editor_cps" data-datakey="1647788517501_0.012316455063675402" style="border: 0px;width: 100% !important;height: auto !important;" data-uid="1647788517463" data-type="1" data-product="" data-templateid="list" data-pid="13521160" data-color="#fa7834" data-categoryid="3" data-appuin="3208869061"></mpcps></section><ul data-tool="mdnice编辑器" class="list-paddingleft-1" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;width: 557.438px;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;text-align: left;white-space: normal;"><li><p><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247485005&amp;idx=1&amp;sn=d3455f81e06024dd5981ca6ef88ea7cc&amp;chksm=fe795946c90ed050b2e12e2df618dd4ca1484a4b8e530fcb92276c868ff3f0f0af14404c3395&amp;scene=21#wechat_redirect" textvalue="《Go 语言设计与实现》纸质书正式发售：文末福利" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2">《Go 语言设计与实现》纸质书正式发售：文末福利</a><br/></p></li></ul><section class="mp_profile_iframe_wrp"><mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzU5NTAzNjc3Mg==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7brHJjiaESpLopIrUcNJzZudFZe4rSczjIBiakgpbibJ2pjnq7WVUrqBbNeU0eqIId01t3NiaicFGKLtWqQ/0?wx_fmt=png" data-nickname="真没什么逻辑" data-alias="draveness" data-signature="系统设计、微服务架构和云原生技术" data-from="0"></mpprofile></section></section>



<p><a href="https://draveness.me/kuberentes-federation/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=edb2bf3c&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247485054%26idx%3D1%26sn%3D2dbe9e4734b2eb2c1c74041d9ff3edc8%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Mon, 21 Mar 2022 09:00:00 +0800</pubDate>
    </item>
    <item>
      <title>《Go 语言设计与实现》纸质书正式发售：​文末福利</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247485005&amp;idx=1&amp;sn=d3455f81e06024dd5981ca6ef88ea7cc</link>
      <description>纸质书正式发售，赠书 10 本</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-12-11 11:56</span> <span style="display: inline-block;"></span>
</p>

<p>纸质书正式发售，赠书 10 本</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=809244ee&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bovnhlC9VDmSRkjEhjgc2sG4bricHQfcPibfkTibiaSk771aGPUiaibyyIoPmrYGtF5hCIbEKHnHB9kIaLg%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section style="font-size: 16px;"><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今年下半年要处理的事情一直都很多，博客和公众号确实太久已经没有更新过了，前面两次更新也基本都跟《Go 语言设计与实现》这本书有关，发了几年所谓『硬核』的技术文章，没想到最近一直都在推荐自己写的这本书，心里其实还有点过意不去的。不过作为一个特殊的时间点，这次书籍的正式发售可能还是要打扰各位读者。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="1" data-type="png" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=2cea914d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brefUqsWVibmfNtnrnA0mCJAOtmnmtqMJbaA4MQ2icicdyWsbh3SiaayBOF3xicQ7YERxLAjpzHf5Mo1eQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - 书籍介绍</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在这本书发售之前，作者是没有想到会有这么多读者的支持，也没有想到这本书的签名版在正式预售的前两天基本就卖光了，只有少部分地区有货，当时看着后台说哪个地区缺货的留言，编辑不停协调网店在不同的地区的库存，人肉去协调不同区域库存的调度工作，在这里也再次感谢博客和公众号的每一位读者。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">前几天发微信文章的时候，说过给大家申请了比较给力的折扣，7 折预售签名版，这个确实也协商了好久。没想到，后来上普通版刚好赶上京东和当当的双十二活动，这本书目前也参与了 5 折活动。对于先前 7 折购买签名版的朋友，感谢大家的支持，也建议大家试试<strong>申请京东保价</strong>。对于网店的一些活动机制，作者和编辑都没办法干涉太多，还请大家多多见谅。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>写作历程</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">《Go 语言设计与实现》这本书是作者在 2019 年初开始写的，初衷也只是想要能够系统地、深入地了解 Go 语言的实现原理，当时的 Go 语言虽然已经很热门，但是跟今天相比还是差了很多。这本书的内容并不是严格按照顺序完成的，写的第一节介绍了 Go 语言的 函数调用 规范，作者仍然记得在完成这一节时，还把样章的 PDF 发给了朋友并问了一些建议，当时其实也没有想到最终这本书会有出版的那一天。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.6324675324675325" data-type="png" data-w="1540" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=51a126e3&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brefUqsWVibmfNtnrnA0mCJAT31COyEymicYliawXJclXCD46icYtia7A04jLTLXticia8xdXUVLicVnbvzvg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - 第一节完成的内容</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">相信很多书的作者都会提到，完成一本书真的是一个很大的工程，从最开始凭借着一腔热血和激情是可以写出很多内容的，但是随着战线拉的越来越长，一旦这口气断掉就很难接上来。作者在这本书的写作过程中也想到过放弃，因为对 Go 语言了解足够多之后，就失去了最开始的那种兴奋，我们享受的是这种探索的过程，然而写作真的是痛苦的，尤其需要重复劳动，在一个主题中写出几十篇能够达到出版质量的内容。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在很长的一段时间内，都有各个出版社的编辑老师联系我要不要考虑出版，但是当时的作者实在是不愿意再花更多的时间来完成这个主题，大量的时间都花在了《为什么这么设计》系列文章上了，所以我一直都没有考虑过出版的事情，直到 20 年的 2 月份才跟图灵社区的英子老师达成了出版的意向。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.31316489361702127" data-type="png" data-w="3008" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=418d8976&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brefUqsWVibmfNtnrnA0mCJAbiaIwtTEWUMAFZl2EXfZCYEdeFD407icJy8kQNz4GFttAfOhTLCD0XEg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - 图灵社区官网</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在整个出版的过程中，最让我感到惊喜的是这本书选择了全彩的印刷方式，很多人可能都觉得技术书籍没有必要做全彩的印刷，双色的书籍完全可以满足图片解释功能的需要。作者很认同这一点，但是内容和形式其实都很重要，我们都有审美上的追求，在阅读一篇排版很差的文章时心里总会觉得不够愉快，作者最初写博客的一个原因是希望能够提供『更好的内容』，也就是无论是内容本身、还是呈现形式，都能带来最好的阅读体验。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="1.3338557993730407" data-type="jpeg" data-w="1276" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=5ca7abf5&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brefUqsWVibmfNtnrnA0mCJATbFEYRQzSWD1ibN5qHIia8udTau5CDjCC5mBEDVxEWX8vIWRPAXoVxGg%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - 全彩印刷的书籍</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">需要跟各位读者坦白的是，<strong>签名版实际是 1000 册</strong>，第一天就卖出了 800 多册。之所以最初决定上架 500 册签名版，也是我们不太确定大家对签名版的喜好情况，报了个相对保守的数据。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.3654390934844193" data-type="png" data-w="1412" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=6ba85b1d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brefUqsWVibmfNtnrnA0mCJAcmpA7UFicic2X2zMicq5GanzLXS8hLw2rtp1csZOdgqB9YWk6pnUILl8w%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 5 - 豆瓣界面</strong></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这本书上线以来，作者从微信、公众号后台和 Twitter 等各个渠道上都收获了来自读者的返图和热情评论，在豆瓣上也能看到读者对这本书的评价，这也算是完成了一项人生的成就吧。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>图书发售</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">全彩印刷的书籍真的很好地保留了阅读上的体验，如果你认为全彩印刷全无必要，而且这本书的定价太高，其实可以在 draven.co/golang 的开源电子版上阅读这些内容。</p><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">关于图书与博客的区别，可以在前面的文章 <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484981&amp;idx=1&amp;sn=df70633c8a2c5f96a09e47b20fd04bb8&amp;chksm=fe79593ec90ed02817cae1473d80e9dda66ea5b56c469616df527a1f3547d7f6443cf90f9de8&amp;scene=21#wechat_redirect" textvalue="《Go语言设计与实现》纸质书预售了！" linktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2">《Go语言设计与实现》纸质书预售了！</a>中了解更多详细的内容。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我其实并不是一个很好合作的人，除了写书和博客，也有很多其他的工作。在书籍的校正和出版上的很多事情上都做了『甩手掌柜』，英子老师也花了很多时间和精力完成一些本应该由我来完成的工作，从一些图片的编辑、内容的校正再到封面的设计和选择，我是很散漫的、也是很挑剔的，英子老师确实非常辛苦，在这里也一定要感谢她。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果你对这本书的纸质版感兴趣，欢迎购买支持，原价 139 元，这两天有活动，可以长按下面的图片购买，只要 69.9 元。数量有限，如果没货了，大家可以试试【<strong>无货订购</strong>】选项，如果这一项也不能选，那就只能等图书重印了。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="1.34125" data-type="jpeg" data-w="800" style="display: block;margin-right: auto;margin-left: auto;height: auto !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=8cce632b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brefUqsWVibmfNtnrnA0mCJAgcJm2wNJYPJvg2gGsj5kv9BU0I1f1ciatnIfAHwNkB1rnWbJOTVb07g%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 6 - 京东链接</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果大佬们对写书感兴趣，可以随时跟我的策划编辑英子老师（刘美英）交流，她的邮箱是 liumy@turingbook.com。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>文末福利</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">感谢广大粉丝的支持，作者会送出 10 本《Go语言设计与实现》。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作者会选择留言点赞数最高的前 10 人，欢迎大家转发、在看和点赞。开奖时间：12 月 14 日中午 12 点。</p></section><section class="mp_profile_iframe_wrp"><mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-id="MzU5NTAzNjc3Mg==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7brHJjiaESpLopIrUcNJzZudFZe4rSczjIBiakgpbibJ2pjnq7WVUrqBbNeU0eqIId01t3NiaicFGKLtWqQ/0?wx_fmt=png" data-nickname="真没什么逻辑" data-alias="draveness" data-signature="系统设计、微服务架构和云原生技术" data-from="0" data-pluginname="mpprofile"></mpprofile></section></section>



<p><a href="https://union-click.jd.com/jdc?e=%5cx26amp;p=JF8BAL8JK1olXDYCVlpeCEsQAl9MRANLAjZbERscSkAJHTdNTwcKBlMdBgABFksVB2wIG1wUQl9HCANtdUkXXT8STDN2BFJjXV4VCB53eXF7TVcZbQcyV19eC0sTAWwPHGslXQEyAjBdCUoWAm4NH1wSbQcyVFlfAEsTBmkBE1kQWDYFVFdtfQhHRDtXTxlXbTYyV25tOEsnAF9KdVkTVANRVwoODUIQCmpYTFMcCFRXXFZVXEMWCmcIE11GbQQDVVpUOA">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=390da2b6&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247485005%26idx%3D1%26sn%3Dd3455f81e06024dd5981ca6ef88ea7cc%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Sat, 11 Dec 2021 11:56:00 +0800</pubDate>
    </item>
    <item>
      <title>《Go 语言设计与实现》纸质书预售了！</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484981&amp;idx=1&amp;sn=df70633c8a2c5f96a09e47b20fd04bb8</link>
      <description>《Go语言设计与实现》纸质书预售了！有 500 本签名版</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-11-22 09:00</span> <span style="display: inline-block;"></span>
</p>

<p>《Go语言设计与实现》纸质书预售了！有 500 本签名版</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=b638776b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFSr1nI1g1fWicIPUiaPWDAVRJEtX4ur99U49N6tl0XKzDQqTqj5e5nl7FA%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">《Go 语言设计与实现》纸质书预售了！有 500 本签名版，不想听「图书出版背后那些事儿」而要直接下单的读者朋友可以直接扫描下面的二维码。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="1.34125" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="800" src="https://wechat2rss.xlab.app/img-proxy/?k=5362f554&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFSrIHT6eMH0bN0xnGppApqna2zyJblTg8pgqUNADDxj1skaYf5ibcc6oQ%2F640%3Fwx_fmt%3Dpng"/><span style="color: rgb(136, 136, 136);font-size: 14px;text-align: center;letter-spacing: 0px;"></span></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这里将主要从封面设计方案、图书与博客的区别、图书折扣、签名版几个方面，简单跟大家介绍一下。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>封面设计方案</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在《Go 语言设计与实现》纸质书诚邀读者评论这篇文章之后，本来是想请大家看纸质书的封面设计方案的，但是因为怕造成太多打扰，最终没有发文章邀请读者讨论。关于封面，当初也设计了几个方案，最终选择下面的方案作为主图。值得一提的是，这个封面也是编辑老师调研朋友圈，得票最多的方案。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.26944444444444443" style="display: block;margin-right: auto;margin-left: auto;" data-type="other" data-w="1080" src="https://wechat2rss.xlab.app/img-proxy/?k=2b2df1f6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFSytoazu6m4ic89xY1QfwPKENUbWYQobd65tsYdwc87NOvgly3NLXj8Lw%2F640%3Fwx_fmt%3Dother"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 -《Go 语言设计与实现》封面设计方案</strong><br/></figcaption></figure><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="1.307443365695793" style="display: block;margin-right: auto;margin-left: auto;" data-type="other" data-w="618" src="https://wechat2rss.xlab.app/img-proxy/?k=5792d65a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFSOb2xlNVsrwuKxT36icNgicj3zEaUIMlBqDFyqRCobcd21A7MdWTIqMLQ%2F640%3Fwx_fmt%3Dother"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - 最终封面方案</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">实际上，仔细观察一下几个方案，你大体上能猜到我们的原初创意，也能猜到封面上这几个 Gopher 之间的关系。我们希望这本书的内容可以让各位读者能够更深入地了解 Go 语言，成为<strong>勇猛精进的 Gopher</strong>。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>图书与博客的区别</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">有读者在博客和微信下面留言问过这个问题，这是一个具有一定代表性的问题。简单说：主题上区别不大，细节上差异明显。经过编辑出版流程的打磨，图书比博客严谨了不少，阅读体验上也升级了。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">细节差异体现在：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">起承转合等处的内容补充（比如文前、章节开篇、章内内容的衔接、章节结束）</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">图片内容描述更加严谨</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">文字与语句的规范表达</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">术语前后表达的一致性</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">......</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">不得不说，出版是个非常精细的工作，其中有很多地方是我们注意不到的，我在收到编辑老师的第一版校正时，因为整本书做了几千处的修改，Word 文档根本打不开。当然，书中肯定还会存在诸多不足，不论是购买纸质书还是阅读博客的朋友，随时欢迎大家提出问题，我们会力所能及地改进图书和博客的内容质量。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">另外，我想强调的一点是：纸质书是实实在在可感知的实体物品，而这可能也是许多朋友期待纸质
书的一大原因。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">不同于市面上绝大部分技术图书，这本书是全彩印刷。据编辑老师说，400 页以上的全彩印刷图书非常少见，因为定价刹不住，而高定价是个购买门槛。不过呢，我们这本书的首印量比较高 —— 通过提高首印量，编辑老师力所能及地压低了定价，且在此之上“得寸进尺”，书的内文用纸和封面用纸都比较考究，为的就是给大家一个“更好”的阅读体验。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">不瞒大家，因为签名版（后面会跟大家介绍），作为作者，我已经非常荣幸地先于编辑老师见到了图书的一部分内文。个人感觉印刷效果不错，不会辜负“图控”朋友们的期待。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>图书折扣</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然不是 5 折，但确实是一个比 5 折更具有诚意的折扣。为什么这么说呢？</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">其实之前提到过，这本书一共 420 页，全彩印刷。另外，封面采用特种纸，内文采用 80g 胶版纸。这样的基础设定，定价为 139.8 元 —— 关注纸书定价的朋友应该能 Get 这个定价是真的“尽力低”了。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">由于成本率非常高，据说这本书为读者申请折扣的过程也颇费了些周折。最终，申请 7 折成功，折后价为 97.90 元。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">其实我个人对于价格感知不多，不过从编辑老师跟我交流的情况看来，能有目前这个价格挺不容易的，大家且买且珍惜。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>签名版</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">之前断断续续有读者在博客和公众号留言，希望在纸质书出版之后能购买到签名版。于是，这次跟编辑老师商量了一下，非常开心地告诉大家，部分读者的这个有点特别的要求可以得到满足。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">以下是我刚刚签名的扉页（一般是翻开封面之后、除去衬页之后的第一页）。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="0.7497062279670975" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="1702" src="https://wechat2rss.xlab.app/img-proxy/?k=021c30a4&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFSzJ4N6FBqVFj2nu2NRLCdsRNaHic9uxnGHKrzvDFVcAibG5AxW9ibZBHKg%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - 签名扉页</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">做这个签名版其实略微有点麻烦。并不是我拿到印刷好的图书给大家签个名发出去。而是印厂的师傅把印刷好的带有扉页的第一印张快递给我，我在扉页上签好名，再把第一印张快递回印厂。这时候，印厂再将它跟印刷完成的剩余的印张装订到一起，最后外面包上封面，这样装订完成之后才是大家见到的图书（什么是印张呢？有对图书出版感兴趣的读者可以自行搜索）。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">到底有多少朋友需要签名版，其实我和编辑老师都不太确定，我们暂时就先签了 500 本。有需要的朋友，可以考虑尽快下手。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>感谢诸位</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">大家都知道，纸质书是基于我的开源电子书《Go 语言设计与实现》，是我深入学习 Go 的过程中对这门语言底层设计与实现原理的全部心得与体会。不少读者见证了电子书的诞生，期间大家一起学习，一起讨论。回复大家的问题一方面帮助我个人精进了 Go 语言知识，另一方面还纠正了内容中的不少细节问题。借着纸质书出版的机会，在此感谢大家。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">此外，感谢谢孟军、毛剑、万俊峰 Kevin、张磊、无闻等业内人士对纸质书的大力推荐，感谢以 LinkinStar（谢恩信）、Jiekun、nevermosby、湍流、Kylin（谷全琦）等为代表的大量读者的热荐文字，名字没办法一一列出了，请见谅。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="3.331645569620253" style="display: block;margin-right: auto;margin-left: auto;" data-type="other" data-w="790" src="https://wechat2rss.xlab.app/img-proxy/?k=f4220594&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFSk4eDLA6nUKshZTZFaSNBb8uaBR4HPjZgrbTGEHq9m81mvYqiaPb5wVg%2F640%3Fwx_fmt%3Dother"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><br/></figcaption></figure><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>如果你以前从来没接触过我的博客</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">那么，这本书是否适合你呢？你可以通过下面这张图了解一下。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="1" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1080" src="https://wechat2rss.xlab.app/img-proxy/?k=7582f426&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFSDPh64PYmIrzuWdoYEHynWWXopL3ocicDLscbYl6rdicMsvwoXicgBYUWA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><img class="rich_pages wxw-img" data-ratio="1" style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;display: block;margin-right: auto;margin-left: auto;" data-type="other" data-w="1080" src="https://wechat2rss.xlab.app/img-proxy/?k=98a12d6e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFSXeicRqYNgy1m19EFJEkwncZRTcjZJCakmxia7YbKu2BB4K9JFPMgpBnQ%2F640%3Fwx_fmt%3Dother"/></figcaption></figure><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><img class="rich_pages wxw-img" data-ratio="1" style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;display: block;margin-right: auto;margin-left: auto;" data-type="other" data-w="1080" src="https://wechat2rss.xlab.app/img-proxy/?k=92a66892&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFStOFJQS1O2A5NQECbcEicy24C4AN0vdoyQotYqFicXiaENVK1BDIAm2buw%2F640%3Fwx_fmt%3Dother"/></figcaption></figure><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><span style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">对了，再给大家个小惊喜——购买本书会附赠一个纸质书签。</span><span style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">书签的一面是一对 Gopher（也就是我们图书封面上认真探究的那一对），另一面是「勇猛精进」四个大字。</span><span style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">希望这本书到手之后会带给大家不一样的喜悦 —— 不管内容上，还是设计上。</span><br/></figcaption></figure><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="1" style="display: block;margin-right: auto;margin-left: auto;" data-type="other" data-w="1080" src="https://wechat2rss.xlab.app/img-proxy/?k=a47a8e8f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFSicYLkZ0TbJicGsmibvd5JCdEgickjZAZficy9MOh3dggBY8fav3IWnJCPzg%2F640%3Fwx_fmt%3Dother"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 6 - Gopher 书签</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">最后——祝大家在学习 Go 语言的路上勇猛精进，一如编程世界里的 Go 语言！</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img class="rich_pages wxw-img" data-ratio="1.34125" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="800" src="https://wechat2rss.xlab.app/img-proxy/?k=5362f554&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boJBkUeYumcdvcWiapGmPsFSrIHT6eMH0bN0xnGppApqna2zyJblTg8pgqUNADDxj1skaYf5ibcc6oQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><br/></figcaption></figure></section>



<p><a href="https://item.jd.com/13521160.html?cu=true%5cx26amp;utm_source=draveness.me%5cx26amp;utm_medium=tuiguang%5cx26amp;utm_campaign=t_2022153266_%5cx26amp;utm_term=26ae6dec476a48bf9af0cdd7396e8a68">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=29242cfc&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484981%26idx%3D1%26sn%3Ddf70633c8a2c5f96a09e47b20fd04bb8%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Mon, 22 Nov 2021 09:00:00 +0800</pubDate>
    </item>
    <item>
      <title>《Go语言设计与实现》纸质书诚邀读者评论</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484963&amp;idx=1&amp;sn=0b1d9ffc9f9ceb172b65275fc71abbe7</link>
      <description>《Go语言设计与实现》纸质书预计11月下旬出版，征集 5 条读者评论作为推荐语！</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-10-19 10:18</span> <span style="display: inline-block;"></span>
</p>

<p>《Go语言设计与实现》纸质书预计11月下旬出版，征集 5 条读者评论作为推荐语！</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=7f1df686&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bpE140jE3R6gmr9rttTxZtV5u4npeBbXUAibOExYUWgoyVFCXkVL2qV9pbzgz9p0czSE92aa63aBmg%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="letter-spacing: 0px;">跟大家汇报下，《Go语言设计与实现》已经拿到书号（978-7-115-57661-3），出版指日可待，大约在 11 月下旬。</span><br/></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">很高兴地告诉大家，<strong>为了还原图书的近 200 幅高清彩色图片，出版社在再三权衡（成本）之后，最终还是采用了全彩印刷</strong>，这在技术图书中并不常见——虽然价格比我预想得高一些，但是出版社已经对成本控了又控，实在是不能再低了（最终 420 面，定价 139.8 元）。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这里放上几张纸质书中的内容截图：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-fileid="100001307" data-ratio="0.625" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="3000" src="https://wechat2rss.xlab.app/img-proxy/?k=5354687c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bpE140jE3R6gmr9rttTxZtVLnbLXLwOpoCHbKzs0JQw3oxGUAicXpVNb8BHwERlD0o3kua9gSAgYpQ%2F640%3Fwx_fmt%3Djpeg"/></figure><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-fileid="100001306" data-ratio="0.625" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="3000" src="https://wechat2rss.xlab.app/img-proxy/?k=1154bb4c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bpE140jE3R6gmr9rttTxZtVKactcxYqECDEGmNNgDpribcwsqWQ33AInkQ3cRDvtCMKm12RIyPQBsg%2F640%3Fwx_fmt%3Djpeg"/></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">目前，图书正在封面设计环节。我跟编辑老师商量，因为纸质书是基于开源电子版的，而开源电子版的写作过程得到了无数读者的见证。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">发布这条微信是想征集一些读者评论作为封底推荐语，在此我郑重地邀请曾经阅读过《Go语言设计与实现》开源电子书的朋友，写写你的感悟与体会。不过，鉴于封底位置有限，目前只能选择 5 条。虽然纸质书最终只能选择 5 条，但我会在《Go语言设计与实现》的开源电子书中专门开辟一篇文章，将所有评论汇集到一起 —— 感谢大家的分享，你的分享会让其他读者更了解这本书，也能更好地学习 Go 语言。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">推荐语的要求如下（是不是有种写小作文的感觉）：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">40 ~ 100 字之间（大约两三句话的样子）</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">言之有物（来自切身阅读体验）</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">加上你的署名（可以是真实姓名，也可以是网名）</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">征集截止日期 —— 10 月 21 日 12：00（周四）</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">对于你的推荐语，提交给我意味着：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">授权使用在图书的封底文字中；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">允许对推荐语进行简单的字词句校正与修改；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">对于选中的推荐语，我会通过评论通知你，其他的推荐语抱歉不能一一回复。</section></li></ul><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">如果你想要在书中留下你的推荐语，请在<strong>微信的评论区留言</strong>。<br/></section><section class="mp_profile_iframe_wrp"><mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzU5NTAzNjc3Mg==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7brHJjiaESpLopIrUcNJzZudFZe4rSczjIBiakgpbibJ2pjnq7WVUrqBbNeU0eqIId01t3NiaicFGKLtWqQ/0?wx_fmt=png" data-nickname="真没什么逻辑" data-alias="draveness" data-signature="系统设计、微服务架构和云原生技术" data-from="0"></mpprofile></section><p><br/></p></section>



<p><a href="2247484963">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=920ae95e&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484963%26idx%3D1%26sn%3D0b1d9ffc9f9ceb172b65275fc71abbe7%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 19 Oct 2021 10:18:00 +0800</pubDate>
    </item>
    <item>
      <title>程序员可能必读书单推荐（一）</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484951&amp;idx=1&amp;sn=3f92c4aa4550821aa16f24f7f81f6de5</link>
      <description>公众号到了三万粉丝，分享一下作者完整读过的三本书</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-07-20 08:30</span> <span style="display: inline-block;"></span>
</p>

<p>公众号到了三万粉丝，分享一下作者完整读过的三本书</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=b367bb56&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brfy0Qg3padVSNWXtgcI7q5vOia4ibtGd1LPHxeDAj6nkxE7uOQicoKYQdH1EPhefy5fzic6ceIwElpBg%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>写在前面</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">从最开始写博客到今天已经有将近七年的时间了，有很多博客的读者都向作者要过书单，但是一直以来这件事情都没有提上日程。作者一直都觉得分享书单和推荐书籍是一件很严肃的事情，大多数工程师的时间和精力都很有限，不希望因为推荐的不合适书籍而浪费大家的时间。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这篇文章以及可能存在的后续文章会审慎地推荐书籍，虽然所有的书都是作者读过并且严格挑选的，但是因为读者的背景不同会有完全不同的体验，这些额外的变量是作者无法控制的，希望各位读者可以理解。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>概述</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">可能是因为大多数英文书籍的名字都很长，很多比较出名的书籍都有广为人知的缩写，今天要介绍的三本书籍也都有各自的缩写，SICP、CTMCP 和 DDIA，可能很多人都听过这三本书的名字，这三本书对作者都有很大的影响。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>SICP</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在这里首先要介绍和推荐的就是《计算机程序的构造和解释》[^1]（Structure and Interpretation of Computer Programs、SICP），相信很多人都曾经看到过这本书的推荐，而作者在这里也不能免俗。我们想谈一谈作者在阅读这本书时的一些经历和主观上的感受。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="1.4475" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="400" src="https://wechat2rss.xlab.app/img-proxy/?k=42c70751&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brfy0Qg3padVSNWXtgcI7q5PE3XiaOsXFRIxaS5ZcNt71AwxERXOv4dddeYGy5dt1B540rLZ4efuiaA%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">SICP</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 1 - 计算机程序的构造和解释</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">第一次听说这本书的时候是在大二，具体从哪里听到过这本书已经不记得了，最开始觉得这本书的名字有一点故弄玄虚，仅凭书名无法想象这本书的内容（这可能也是很多好书被埋没的原因）。因为之前一直接触的都是面向对象的编程语言，所以开始学习这本书时受到了极大的震撼，它为我理解计算机程序提供了非常不一样的视角。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作为一本介绍计算机程序的书，它选择一门非常小众的编程语言 Scheme[^2]，Scheme 是 Lisp 的方言，作为 1970 年从 MIT 实验室中走出的语言，它却一开始就支持了函数式的编程范式。与今天复杂的编程语言相比，该语言中的概念和元素非常少，然而越是简单的工具越能揭示编写计算机程序时遇到的基本问题。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">近些年来，函数式编程的思想基本上已经入侵了大多数的编程语言，无论是 Objective-C 中的代码块、C++ 中的成员函数引用、Java 和其他编程语言中的匿名函数都会有一些函数式编程的意味。然而 Scheme 作为根正苗红的函数式编程语言，我们能更清晰地理解函数式编程到底是什么，如何使用函数式的编程范式如何构建复杂的软件系统。</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_svg/qE9MKluetOnroDg3ga0LQkkib8ljLrZ0ibuFeicx7sxciaLib0ucUg3TYP7LHLDEic00zPyoEpd8ZibPk81wWickWSmDia0QictibHHWMfe/640?wx_fmt=svg&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;">(let loop ((n 1))<br/>  (if (&gt; n 10)<br/>      &#39;()<br/>      (cons n<br/>	    (loop (+ n 1)))))<br/>===&gt; (1 2 3 4 5 6 7 8 9 10)<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然学习这本书的过程异常艰难，作者大概花了三、四个月的时间阅读这本书，作者还将 1986 年该书的作者 Hal Abelson 和 Gerald Jay Sussman 在 HP 公司上课的视频作为辅助资料学习。我相信阅读这本书并观看相关的视频对于没有接触过函数式编程的读者来说，会从根本上改变对编程的看法。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作者在学习完 SICP 之后，还曾经痴迷于 Haskell 并且在 Coursera 上学习过函数式编程语言的相关课程，而函数式编程语言的学习也是对作者的影响最深的几个事件之一。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>CTMCP</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">《计算机程序设计的概念、技术和模型》[^3]（Concepts, Techniques and Models of Programming Language、CTMCP）是 Peter Van Roy 和 Seif Haridi 在 2004 年出版的大部头。这本书通过统一的方式介绍主流的全部<strong>编程范式</strong>、它们之间的关系以及它们的具体应用。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="1.2402826855123674" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="283" src="https://wechat2rss.xlab.app/img-proxy/?k=f7840bcd&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brfy0Qg3padVSNWXtgcI7q554kyOBLtSFHtz2UKZAlKicibRRcmiapXAXbOe8a2KOxhicmaIRZ0SNicThA%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">CTMCP</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 2 - 计算机程序的概念、技术和模型</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">编程范式在今天应该不是一个令人感到陌生的名字，面向对象编程、函数式编程都是编程范式的一种，今天的编程语言往往由多个复杂的编程范式组成的。在某种程度上，我们可以将编程语言理解为编程范式的集合。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.2833333333333333" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=bb0d2a7e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brfy0Qg3padVSNWXtgcI7q5DRBOf25mqiakgT8mjq235OoCicbtibZpBh0VIakZ36EJLehrvamt5uIfQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">programming-paradigms</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 3 - 编程范式</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这本书的作者为了展示书中介绍的不同编程范式，特意设计并实现了名为 Oz 的多编程范式的编程语言[^4]。该编程语言包含了大多数主流的编程范式，其中包括逻辑、函数式、命令式、面向对象、约束式、分布式和并行编程，这种设计使得比较不同范式时不会遇到上下文不同导致的问题，避免了因为编程范式实现细节的不同而陷入的琐碎问题。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果对这本书没有那么感兴趣，其实快速阅读该书的前九章就足够了，感兴趣的读者也可以阅读剩余的内容，虽然书中有很多代码，但是因为语言不是主流的编程语言，所以可能会遇到示例代码无法跑通的情况。我们在阅读这本书时也应该更加关注概念以及形而上的知识，而不是形而下的一些具体实现。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>DDIA</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">最后要介绍的是豆瓣上评分 9.7 分的《设计数据密集型应用》[^5]（Designing Data-Intensive Applications、DDIA），该书是 2018 年出版的一本新书，初看这个名字可能会误认为这是仅仅给数据开发者阅读的书籍，但是作者相信这本书能给所有的开发者带来不同的阅读体验。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.35765379113018597" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1398" src="https://wechat2rss.xlab.app/img-proxy/?k=98c9704e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brfy0Qg3padVSNWXtgcI7q52AmEn1JwOIj9SK3Fx2CBDu9icZq8vJzp58Uw2zjG5gOicZmcIngRZSaw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">DDIA</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 4 - 设计数据密集型应用</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">该书的第一部分是围绕数据本身展开的，其中介绍了数据的模型、查询语言、存储和获取以及编码方式；第二部分介绍了分布式的数据应该如何处理，其中包括副本、分片、事务、一致性和共识等内容；最后一部分主要介绍的是衍生数据的处理，其中包括批处理、流处理和未来的数据系统，而真正与研究大数据开发方向的同学关系紧密也是这一部分。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作者认为，虽然这本书没有创新性的引入一些新的概念和技术，书中描述的大多数问题和解决方案都能在网络上找到，但是这本书的阅读可以帮助我们重新构建系统性的知识体系、打通不同知识之间的联系。它能让我们在看待数据和分布式系统时带着更加通透的感觉，而这也是作者想要推荐这本书的目的。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在这里还是想强调一点，上面的这三本书都是作者阅读过的书籍，这里做的评价都仅仅出于作者的主观判断。虽然作者确实从这三本书中获得了大家所说的 &#34;Aha moment&#34;，但是这三本书都是大部头，认真阅读这三本书可能需要花费大半年的时间，作者不能对各位读者自身的阅读体验负责，也希望各位读者在选择输入源时有自己的判断、也更加谨慎地思考。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果各位读者对这个系列比较感兴趣，作者在后面还会推荐一些自己阅读过的其他书籍，也欢迎大家在评论中留下带给自己 &#34;Aha moment&#34; 的书籍，我们可以一同学习和讨论。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: 计算机程序的构造和解释(原书第2版) <a href="https://book.douban.com/subject/1148282/" target="_blank">https://book.douban.com/subject/1148282/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^2]: Wikipedia: Scheme <a href="https://en.wikipedia.org/wiki/Scheme_(programming_language)" target="_blank">https://en.wikipedia.org/wiki/Scheme_(programming_language)</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^3]: Concepts, Techniques and Models of Programming Language <a href="https://book.douban.com/subject/1782316/" target="_blank">https://book.douban.com/subject/1782316/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^4]: Wikipedia Oz (programming language)<a href="https://en.wikipedia.org/wiki/Oz_(programming_language)" target="_blank">https://en.wikipedia.org/wiki/Oz_(programming_language)</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^5]: 数据密集型应用系统设计 <a href="https://book.douban.com/subject/30329536/" target="_blank">https://book.douban.com/subject/30329536/</a></p></section><p><br/></p>



<p><a href="https://draveness.me/books-1/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=13222837&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484951%26idx%3D1%26sn%3D3f92c4aa4550821aa16f24f7f81f6de5%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 20 Jul 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>为什么 Linux 和 macOS 不需要碎片整理</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484942&amp;idx=1&amp;sn=cb69b92e77a5ad1c528c3584e1adec54</link>
      <description></description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-04-27 08:51</span> <span style="display: inline-block;"></span>
</p>

<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=9b5c7211&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7boudwndayFFtxN3AOJoeq8So0ejNdMKmloXlXGkQE273ux4sS8Zgbdx0ww24xszVz426R72YE7xGw%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">为什么这么设计（Why’s THE Design）是一系列关于计算机领域中程序设计决策的文章，我们在这个系列的每一篇文章中都会提出一个具体的问题并从不同的角度讨论这种设计的优缺点、对具体实现造成的影响。如果你有想要了解的问题，可以在文章下面留言。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">相信今天很多的软件工程师使用的都是 Linux 或者 macOS 系统，与 Windows 不同，我们很难看到磁盘碎片整理这一概念，从个人的经验来看，作者在过去七八年没有在 macOS 中整理过磁盘的碎片，你在今天的磁盘工具中也找不到相关的操作，只能通过 diskutil 命令设置某一块磁盘是否开启或者关闭碎片整理。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.6307339449541285" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1744" src="https://wechat2rss.xlab.app/img-proxy/?k=ae086a0d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boudwndayFFtxN3AOJoeq8Sata82pKWqB7QQicAfm44JNY7kQDbibTSXq0UXPMA11G7nlpBJpb8Eicbw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - macOS 磁盘工具</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们在 <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484891&amp;idx=1&amp;sn=95df2da5d622ba3f073755c7ba89d84d&amp;chksm=fe795ad0c90ed3c68b2fd011e0296eae5d710c7733b166a81108cf7fe200c97e35e9d740185e&amp;scene=21#wechat_redirect" textvalue="前一篇文章" data-itemshowtype="0" tab="innerlink" data-linktype="2">前一篇文章</a> 中曾经分析过为什么早期 Windows 操作系统每隔一段时间可能需要整理磁盘上的碎片[^1]，该问题背后有两个原因，其一是 Windows 使用的 FAT 是很简单的文件系统，该文件系统的设计决定了同一份文件可能会散落在磁盘的不同位置，其二是固态硬盘在上古时代没有普及，机械硬盘的随机读写性能很差。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Linux 和 macOS 系统不需要碎片整理的原因与 Windows 需要碎片整理的原因正好相反：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Linux 和 macOS 使用的文件系统或者降低了碎片发生的概率或者实现自动整理碎片的特性；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">固态硬盘与机械硬盘具有不同的特性，碎片整理可能不仅对提高读写性能没有显著的帮助，还不利于硬件的使用寿命；</section></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><span>文件系统</span><span></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Linux 一般都使用 Ext2、Ext3 和 Ext4 文件系统，今天的大多数 Linux 发行版都选择了 Ext4。与 Windows 将多个文件连续存储的方式不同，Linux 会把文件散落到磁盘的不同地方存储，同时在文件之间留下一些空间，保证文件在修改或者更新时不会造成碎片。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.7" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="800" src="https://wechat2rss.xlab.app/img-proxy/?k=e3157ec0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boudwndayFFtxN3AOJoeq8ScGGy04jvvGhfnDeAicOvTiahQJoBQ56xIjrPunxe8ib7LOlE5mo3SvtGg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - Linux 文件系统[^2]</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今天的 macOS 多数都使用 APFS 文件系统[^3]，它是苹果专门为固态硬盘等设备优化的文件系统。更早的 HFS 和 HFS+ 都是用了基于区块（Extent）的设计，每个区块都包含序号和一段连续的存储空间，这种分配方式会在文件系统中查找几个连续的区块来提供所需的空间。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.46153846153846156" style="display: block;margin-right: auto;margin-left: auto;" data-type="other" data-w="650" src="https://wechat2rss.xlab.app/img-proxy/?k=1e045b7b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz%2FPvFQOcBK7boudwndayFFtxN3AOJoeq8SKkUHwx0WYTafvDribt0BedQG5I40fHic6W9M6ypJpUPQZ9pcu6XYVwRA%2F640%3Fwx_fmt%3Dother"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - macOS 文件系统</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">无论是 Linux 还是 macOS，它们的文件系统都是基于区块设计的，而磁盘的空间分配也相对比较合理，所以不会出现 Windows 系统上碎片化的磁盘。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了文件系统在设计就避免了碎片的出现之外，Linux 和 macOS 也都是引入了延迟分配空间的策略，它们会通过缓冲区尽可能延迟磁盘写入的时间，这样不仅能够降低刷盘的概率，还能增加文件写入相邻区块的概率，然而这种机制并不是没有副作用，在系统断电或者崩溃时可能会丢失更多的数据。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果磁盘上确实出现了碎片，那么 Linux 和 macOS 的文件系统也会尝试移动出现碎片的文件，不需要额外的碎片整理工具，这种设计带来的用户体验会比手动触发耗时较长的碎片整理好很多。macOS 上的 HFS+ 系统还支持实时的去碎片化，当满足以下条件时会触发碎片整理[^4]：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">文件小于 20 MB；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">文件存储在 8 个以上的区块上；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">文件在过去一分钟没有被更新；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">系统已经启动了三分钟；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在多数情况下，这些操作系统中的磁盘碎片比例都非常低，只有在磁盘空间不足时才会开始出现碎片，所以在这时我们其实需要的是一个更大的磁盘或者更新的电脑，而不是整理磁盘上的碎片。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><span>固态硬盘</span><span></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">固态硬盘其实已经是有着三十年历史的存储介质了，但是由于固态硬盘的价格在过去一直都十分昂贵，所以没有在数据中心和个人电脑中普及开来。哪怕是在今天，机械磁盘的价格与固态硬盘相比也有比较明显的优势。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5463157894736842" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="950" src="https://wechat2rss.xlab.app/img-proxy/?k=0c69edfc&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boudwndayFFtxN3AOJoeq8Sf3RUTiaQXDCZphKHk4e2LYnQKB7decobMEhqHMZN5MtRu618KaUgzCA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - 固态硬盘和机械硬盘价格对比</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">新型的存储介质带来了全新的特性和性能，我们在前一篇文章中曾经介绍过，因为机械硬盘的机械结构，所以它的随机 I/O 与顺序的 I/O 性能可能相差几百倍，碎片整理可以将散落在磁盘上的数据合并到一处，随机 I/O 的次数减少自然也会提高读写文件的性能。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">固态硬盘的顺序 I/O 和随机 I/O 在性能上虽然也有差异，但是差距可能在十几倍到几十倍之间，而固态硬盘的随机 I/O 延迟也比机械磁盘好几十倍甚至上千倍，到现在来看整理固态硬盘上的碎片虽然有收益，但是也比较有限。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.85" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="520" src="https://wechat2rss.xlab.app/img-proxy/?k=ce867baa&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boudwndayFFtxN3AOJoeq8S1TeO391FdMvXflwHHnKWibwm24Ocdcpwq4KE6DsAZmjoP5dKyL5S5Ug%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 5 - NAND 闪存的演进</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作为电子元件的固态硬盘虽然有着较好的性能表现，但是固态硬盘都有循环擦写的次数限制，也被称作 P/E。它的寿命与机械硬盘相比却比较有限。如果一个 512 GB 的固态硬盘的擦写数目是 1000 次，每次写满数据都会消耗一次寿命，等擦写次数达到 1000 次之后硬盘就会报废，碎片整理其实就是主动移动硬盘上的数据，自然会影响硬件的寿命。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><span>总结</span><span></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在软件工程中有一个非常有趣的现象，做硬件和基础架构的工程师都在拼命优化系统的性能，然而应用层的工程师很多时候并不在乎性能上的微小差异，而这也是工作职责上的差异带来的结果，不同的位置决定了不同的关注点。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">硬件的演进和革新深深地影响着上层软件的设计，想要设计出通用的系统是异常困难的，在设计文件系统时如果不考虑底层硬件的特性，也就无法充分利用硬件提供的性能并得到期望的结果。这里简单总结一下 Linux 和 macOS 不需要碎片整理的两个原因：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">文件系统基于区块分配的设计使得磁盘上出现碎片的概率很低，延迟分配和自动的整理策略解放了操作系统的使用者，在多数情况下不需要考虑磁盘的碎片化；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">固态硬盘的随机读写性能远远好于机械硬盘，随机读写和顺序读写虽然也有性能差异，但是没有机械硬盘的差异巨大，而频繁的碎片整理也会影响固态硬盘的使用寿命；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">到最后，我们还是来看一些比较开放的相关问题，有兴趣的读者可以仔细思考一下下面的问题，与前面的问题不同，作者会在后面的文章中解答这两个问题：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">为什么固态硬盘的擦写次数有上限？</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">机械硬盘在哪些情况下更容易损坏？</section></li></ul><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">如果对文章中的内容有疑问或者想要了解更多软件工程上一些设计决策背后的原因，可以在博客下面留言，作者会及时回复本文相关的疑问并选择其中合适的主题作为后续的内容。</p></blockquote><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><span>推荐阅读</span><span></span></h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484464&amp;idx=1&amp;sn=32bd106c7495207cabe97368a96697bb&amp;chksm=fe795b3bc90ed22dbd0dd0a520a74b6a1e27d4126a930ec2bba15773c38aafab166abfc42899&amp;scene=21#wechat_redirect" textvalue="为什么 CPU 访问硬盘很慢" data-itemshowtype="0" tab="innerlink" data-linktype="2">为什么 CPU 访问硬盘很慢</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484691&amp;idx=1&amp;sn=673d4a95402a948b8fce9fd410510d06&amp;chksm=fe795a18c90ed30e6452ee4e29cea2da04d79c8037c51c0ae5bacb88909c702d3b91eea6dc27&amp;scene=21#wechat_redirect" textvalue="为什么 HugePages 可以提升数据库性能" data-itemshowtype="0" tab="innerlink" data-linktype="2">为什么 HugePages 可以提升数据库性能</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484891&amp;idx=1&amp;sn=95df2da5d622ba3f073755c7ba89d84d&amp;chksm=fe795ad0c90ed3c68b2fd011e0296eae5d710c7733b166a81108cf7fe200c97e35e9d740185e&amp;scene=21#wechat_redirect" textvalue="" data-itemshowtype="0" tab="innerlink" data-linktype="2"></a><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484891&amp;idx=1&amp;sn=95df2da5d622ba3f073755c7ba89d84d&amp;chksm=fe795ad0c90ed3c68b2fd011e0296eae5d710c7733b166a81108cf7fe200c97e35e9d740185e&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">为什么早期的 Windows 需要整理碎片</a></section></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><span>参考资料</span><span></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: 为什么早期的 Windows 需要整理碎片 <a href="https://draveness.me/whys-the-design-windows-defragmentation/" target="_blank">https://draveness.me/whys-the-design-windows-defragmentation/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^2]: Is there a tool to visualize a filesystem allocation map on Linux? <a href="https://unix.stackexchange.com/questions/30743/is-there-a-tool-to-visualize-a-filesystem-allocation-map-on-linux" target="_blank">https://unix.stackexchange.com/questions/30743/is-there-a-tool-to-visualize-a-filesystem-allocation-map-on-linux</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^3]: Wikipedia: Apple File System <a href="https://en.wikipedia.org/wiki/Apple_File_System" target="_blank">https://en.wikipedia.org/wiki/Apple_File_System</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^4]: HFS+ and File System Fragmentation <a href="https://developercoach.com/file-system-fragmentation/" target="_blank">https://developercoach.com/file-system-fragmentation/</a></p></section><section class="mp_profile_iframe_wrp"><mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzU5NTAzNjc3Mg==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7brHJjiaESpLopIrUcNJzZudFZe4rSczjIBiakgpbibJ2pjnq7WVUrqBbNeU0eqIId01t3NiaicFGKLtWqQ/0?wx_fmt=png" data-nickname="真没什么逻辑" data-alias="draveness" data-signature="系统设计、微服务架构和云原生技术"></mpprofile></section><p><br/></p>



<p><a href="https://draveness.me/whys-the-design-linux-macos-fragmentation/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=e0aa2b98&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484942%26idx%3D1%26sn%3Dcb69b92e77a5ad1c528c3584e1adec54%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 27 Apr 2021 08:51:00 +0800</pubDate>
    </item>
    <item>
      <title>谈谈 Kubernetes 的问题和局限性</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484931&amp;idx=1&amp;sn=7418d82eaee37c9904669f77f40b57f2</link>
      <description>谈谈 Kubernetes 在多集群管理和一些应用场景中的局限性，大集群、联邦集群、应用分发、批处理调度和多租户等</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-04-20 08:30</span> <span style="display: inline-block;"></span>
</p>

<p>谈谈 Kubernetes 在多集群管理和一些应用场景中的局限性，大集群、联邦集群、应用分发、批处理调度和多租户等</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=1f47d0aa&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7boKzpBxEpjhL2OZbHT53tMGLAKslHyUV1Tm8gsPvmy7kcYCZD0gTeqBrlnzmric2ytH5KN3yCAO8vg%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">2014 年发布的 Kubernetes 在今天俨然已成为容器编排领域的事实标准，相信谈到 Kubernetes 的开发者都会一再复述上述现象。如下图所示，今天的大多数个人或者团队都会选择 Kubernetes 管理容器，而也有 75% 的人会在生产环境中使用 Kubernetes。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.44333333333333336" style="display: block;margin-right: auto;margin-left: auto;" data-type="other" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=5d73d8c5&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz%2FPvFQOcBK7boKzpBxEpjhL2OZbHT53tMGnHfibVd4CGe9ojSfOkxxXGjyoicUfBiaNucA4NQxiar1Pxnc0rQJIKRvBw%2F640%3Fwx_fmt%3Dother"/></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;text-align: center;"><strong>图 1 - Kubernetes 容器编排[^1]</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在这种全民学习和使用 Kubernetes 的大背景下，我们也应该非常清晰地知道 Kubernetes 有哪些局限性。虽然 Kubernetes 能够解决容器编排领域的大多数问题，但是仍然有一些场景是它很难处理、甚至无法处理的，只有对这些潜在的风险有清晰的认识，才能更好地驾驭这项技术，这篇文章将从集群管理和应用场景两个部分谈谈 Kubernetes 社区目前的发展和一些局限性。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>集群管理</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">集群是一组能够在一起协同工作的计算机，我们可以将集群中的所有计算机看成一个整体，所有资源调度系统都是以集群为维度进行管理的，集群中的所有机器构成了资源池，这个巨大的资源池会为待运行的容器提供资源执行计算任务，这里简单谈一谈 Kubernetes 集群管理面对的几个复杂问题。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>水平扩展性<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">集群大小是我们在评估资源管理系统时需要关注的重要指标之一，然而 Kubernetes 能够管理的集群规模远远小于业界的其他资源管理系统。集群大小为什么重要呢，我们先来看另一个同样重要的指标 — 资源利用率，很多工程师可能没有在公有云平台上申请过资源，这些资源都相当昂贵，在 AWS 上申请一个与主机差不多配置的虚拟机实例（8 CPU、16 GB）每个月大概需要 150 美金，约为 1000 人民币[^2]。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.44596912521440824" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="2332" src="https://wechat2rss.xlab.app/img-proxy/?k=a9c76dd0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boKzpBxEpjhL2OZbHT53tMGnxWUuxjCdUrApBZ2oiawRtzcGJVvakMtYgg1iaDD2VckTklpeAVTg4RA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - AWS EC2 价格</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">大多数的集群都会使用 48 CPU 或者 64 CPU 的物理机或者虚拟机作为集群中的节点，如果我们的集群中需要包含 5,000 个节点，那么这些节点每个月大概要 8,000,000 美元，约为 50,000,000 人民币，在这样的集群中<strong>提升 1% 的资源利用率就相当于每个月节省了 500,000 的成本</strong>。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">多数在线任务的资源利用率都很低，更大的集群意味着能够运行更多的工作负载，而多种高峰和低谷期不同的负载部署在一起可以实现超售，这样能够显著地提高集群的资源利用率，如果单个集群的节点数足够多，我们在部署不同类型的任务时会有更合理的组合，可以完美错开不同服务的高峰期。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 社区对外宣传的是单个集群最多支持 5,000 节点，Pod 总数不超过 150,000，容器总数不超过 300,000 以及单节点 Pod 数量不超过 100 个[^3]，与几万节点的 Apache Mesos 集群、50,000 节点的微软 YARN 集群[^4]相比，Kubernetes 的集群规模整整差了一个数量级。虽然阿里云的工程师也通过优化 Kubernetes 的各个组件实现了 5 位数的集群规模，但是与其他的资源管理方式相比却有比较大的差距[^5]。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5233333333333333" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=f5a3242b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7boKzpBxEpjhL2OZbHT53tMGxPLuw9NouVJtOKtibmA0ydUaHiaDEic3p71e0E4PuVm4v8eVPN1GlcsTg%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - Apache Mesos 与 Hadoop YARN</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">需要注意的是 Kubernetes 社区虽然对外宣称单集群可以支持 5,000 节点，同时社区也有各种各样的集成测试保证每个改动都不会影响它的伸缩性[^6]，但是 Kubernetes 真的非常复杂，我们没有办法保证你使用的每个功能在扩容的过程中都不出问题。而在生产环境中，我们甚至可能在集群扩容到 1000 ~ 1500 节点时遇到瓶颈。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">每个稍具规模的大公司都想要实现更大规模的 Kubernetes 集群，但是这不是一个改几行代码就能解决的简单问题，它可能需要我们限制 Kubernetes 中一些功能的使用，在扩容的过程中，etcd、API 服务器、调度器以及控制器都有可能出现问题。社区中已经有一些开发者注意到了其中的一些问题，例如在节点上增加缓存降低 API 服务器的负载[^7]，但是要推动类似的改变还是很困难的，有志之士可以尝试在社区推动类似的项目。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>多集群管理<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">单个集群的容量再大也无法解决企业面对的问题，哪怕有一天 Kubernetes 集群可以达到 50,000 节点的规模，我们仍然需要管理多个集群，多集群管理也是 Kubernetes 社区目前正在探索的方向，社区中的多集群兴趣小组（SIG Multi-Cluster）目前就在完成相关的工作[^8]。在作者看来，Kubernetes 的多集群会带来资源不平衡、跨集群访问困难以及提高运维和管理成本三大问题，我们在这里谈一谈目前在开源社区和业界几种可供参考和选择的解决方案。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>kubefed<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">首先要介绍的是 kubefed，该项目是 Kubernetes 社区给出的解决方案，它同时提供了跨集群的资源和网络管理的功能，社区的多集群兴趣小组（SIG Multi-Cluster）负责了该项目的开发工作：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.75" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="960" src="https://wechat2rss.xlab.app/img-proxy/?k=52ff8c4c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boKzpBxEpjhL2OZbHT53tMGgaODlFbAUkjKFyXk93sPtnIon8GuDo1A68u8RA21ibjBq9wyAEGcaZw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - Kubernetes 联邦</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">kubefed 通过一个中心化的联邦控制面板管理多集群中的元数据，上层的控制面板会为管理器群中的资源创建对应的联邦对象，例如：<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">FederatedDeployment</code>：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_svg/qE9MKluetOnroDg3ga0LQvMX7AMcH54vCJ36gAElfQpI3Fk0K5Nr1afJalIUhLcwG2mmpWLKu3vDvSRib9qlsXfKBhymkvDCt/640?wx_fmt=svg&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">FederatedDeployment</span><br/><span style="color: #d14;line-height: 26px;">...</span><br/><span style="line-height: 26px;">spec:</span><br/>  <span style="color: #d14;line-height: 26px;">...</span><br/>  <span style="line-height: 26px;">overrides:</span><br/>  <span style="color: #998;font-style: italic;line-height: 26px;"># Apply overrides to cluster1</span><br/>    <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">clusterName:</span> <span style="color: #d14;line-height: 26px;">cluster1</span><br/>      <span style="line-height: 26px;">clusterOverrides:</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># Set the replicas field to 5</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">path:</span> <span style="color: #d14;line-height: 26px;">&#34;/spec/replicas&#34;</span><br/>          <span style="line-height: 26px;">value:</span> <span style="color: #008080;line-height: 26px;">5</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># Set the image of the first container</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">path:</span> <span style="color: #d14;line-height: 26px;">&#34;/spec/template/spec/containers/0/image&#34;</span><br/>          <span style="line-height: 26px;">value:</span> <span style="color: #d14;line-height: 26px;">&#34;nginx:1.17.0-alpine&#34;</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># Ensure the annotation &#34;foo: bar&#34; exists</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">path:</span> <span style="color: #d14;line-height: 26px;">&#34;/metadata/annotations&#34;</span><br/>          <span style="line-height: 26px;">op:</span> <span style="color: #d14;line-height: 26px;">&#34;add&#34;</span><br/>          <span style="line-height: 26px;">value:</span><br/>            <span style="line-height: 26px;">foo:</span> <span style="color: #d14;line-height: 26px;">bar</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># Ensure an annotation with key &#34;foo&#34; does not exist</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">path:</span> <span style="color: #d14;line-height: 26px;">&#34;/metadata/annotations/foo&#34;</span><br/>          <span style="line-height: 26px;">op:</span> <span style="color: #d14;line-height: 26px;">&#34;remove&#34;</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># Adds an argument `-q` at index 0 of the args list</span><br/>        <span style="color: #998;font-style: italic;line-height: 26px;"># this will obviously shift the existing arguments, if any</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">path:</span> <span style="color: #d14;line-height: 26px;">&#34;/spec/template/spec/containers/0/args/0&#34;</span><br/>          <span style="line-height: 26px;">op:</span> <span style="color: #d14;line-height: 26px;">&#34;add&#34;</span><br/>          <span style="line-height: 26px;">value:</span> <span style="color: #d14;line-height: 26px;">&#34;-q&#34;</span><br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上层的控制面板会根据联邦对象 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">FederatedDeployment</code> 的规格文件生成对应的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Deployment</code> 并推送到下层的集群，下层集群可以正常根据 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Deployment</code> 中的定义创建特定数量的副本。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.2833333333333333" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=3a4f72dc&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boKzpBxEpjhL2OZbHT53tMGJokrTOx8zSRHLXZdHahu0XjDWGjEowIgufp2YDOhe3Rq48KXabF0Bw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 5 - 从联邦对象到普通对象</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">FederatedDeployment</code> 只是一种最简单的分发策略，在生产环境中我们希望通过联邦的集群实现容灾等复杂功能，这时可以利用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">ReplicaSchedulingPreference</code> 在不同集群中实现更加智能的分发策略：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_svg/qE9MKluetOnroDg3ga0LQvMX7AMcH54vCJ36gAElfQpI3Fk0K5Nr1afJalIUhLcwG2mmpWLKu3vDvSRib9qlsXfKBhymkvDCt/640?wx_fmt=svg&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="line-height: 26px;">apiVersion:</span> <span style="color: #d14;line-height: 26px;">scheduling.kubefed.io/v1alpha1</span><br/><span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">ReplicaSchedulingPreference</span><br/><span style="line-height: 26px;">metadata:</span><br/>  <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">test-deployment</span><br/>  <span style="line-height: 26px;">namespace:</span> <span style="color: #d14;line-height: 26px;">test-ns</span><br/><span style="line-height: 26px;">spec:</span><br/>  <span style="line-height: 26px;">targetKind:</span> <span style="color: #d14;line-height: 26px;">FederatedDeployment</span><br/>  <span style="line-height: 26px;">totalReplicas:</span> <span style="color: #008080;line-height: 26px;">9</span><br/>  <span style="line-height: 26px;">clusters:</span><br/>    <span style="line-height: 26px;">A:</span><br/>      <span style="line-height: 26px;">minReplicas:</span> <span style="color: #008080;line-height: 26px;">4</span><br/>      <span style="line-height: 26px;">maxReplicas:</span> <span style="color: #008080;line-height: 26px;">6</span><br/>      <span style="line-height: 26px;">weight:</span> <span style="color: #008080;line-height: 26px;">1</span><br/>    <span style="line-height: 26px;">B:</span><br/>      <span style="line-height: 26px;">minReplicas:</span> <span style="color: #008080;line-height: 26px;">4</span><br/>      <span style="line-height: 26px;">maxReplicas:</span> <span style="color: #008080;line-height: 26px;">8</span><br/>      <span style="line-height: 26px;">weight:</span> <span style="color: #008080;line-height: 26px;">2</span><br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上述调度的策略可以实现工作负载在不同集群之间的权重，在集群资源不足甚至出现问题时将实例迁移到其他集群，这样既能够提高服务部署的灵活性和可用性，基础架构工程师也可以更好地平衡多个集群的负载。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们可以认为 kubefed 的主要作用是将多个松散的集群组成强耦合的联邦集群，并提供更加高级的网络和部署功能，这样我们可以更容易地解决集群之间资源不平衡和连通性的一些问题，然而该项目的关注点不包含集群生命周期的管理，</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>集群接口<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Cluster API 也是 Kubernetes 社区中与多集群管理相关的项目，该项目由集群生命周期小组（SIG Cluster-Lifecycle）负责开发，其主要目标是通过声明式的 API 简化多集群的准备、更新和运维工作，你在该项目的 设计提案 中能够找到它的职责范围[^9]。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.7841823056300268" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1492" src="https://wechat2rss.xlab.app/img-proxy/?k=4b98e416&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boKzpBxEpjhL2OZbHT53tMGCNAGGtaiazDQ9Y6rHeaWKySAUHfWibJ3nH7h50u5OnJAWljnnWXJrm1g%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 6 - Cluster API 概念</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在该项目中最重要的资源就是 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Machine</code>，它表示一个 Kubernetes 集群中的节点。当该资源被创建时，特定提供商的控制器会根据机器的定义初始化并将新的节点注册到集群中，在该资源被更新或者删除时，也会执行操作达到用户的状态。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这种策略与阿里的多集群管理的方式有一些相似，它们都使用声明式的 API 定义机器和集群的状态，然后使用 Kubernetes 原生的 Operator 模型在更高一层的集群中管理下层集群，这能够极大降低集群的运维成本并提高集群的运行效率[^10]，不过类似的项目都没有考虑跨集群的资源管理和网络管理。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>应用场景</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们在这一节将谈谈 Kubernetes 中一些有趣的应用场景，其中包括应用分发方式的现状、批处理调度任务以及硬多租户在集群中的支持，这些是社区中比较关注的问题，也是 Kubernetes 目前的盲点。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>应用分发<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 主项目提供了几种部署应用的最基本方式，分别是 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Deployment</code>、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">StatefulSet</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">DaemonSet</code>，这些资源分别适用于无状态服务、有状态服务和节点上的守护进程，这些资源能够提供最基本的策略，但是它们无法处理更加复杂的应用。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.16666666666666666" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=1fd0e541&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boKzpBxEpjhL2OZbHT53tMGmT9keRDicwkWLFyP45FcFnlRIvZQ6hzrO8DbjBzQwrQFRNmFZBzME5g%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 7 - Kubernetes 应用管理</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">随着 CRD 的引入，目前社区的应用管理小组（SIG Apps）基本不会向 Kubernetes 主仓库引入较大的改动，大多数的改动都是在现有资源上进行的修补，很多常见的场景，例如只运行一次的 DaemonSet[^11] 以及金丝雀和蓝绿部署等功能，现在的资源也存在很多问题，例如 StatefulSet 在初始化容器中卡住无法回滚和更新[^12]。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们可以理解社区不想在 Kubernetes 中维护更多的基本资源，通过几个基本的资源可以覆盖 90% 的场景，剩下的各种复杂场景可以让其他社区通过 CRD 的方式实现。不过作者认为如果社区能够在上游实现更多高质量的组件，这对于整个生态都是很有价值并且很重要的工作，需要注意的是假如各位读者想要在 Kubernetes 项目中成为贡献者，SIG Apps 可能不是一个很好的选择。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>批处理调度<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">机器学习、批处理任务和流式任务等工作负载的运行从 Kubernetes 诞生第一天起到今天都不是它的强项，大多数的公司都会使用 Kubernetes 运行在线服务处理用户请求，用 Yarn 管理的集群运行批处理的负载。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5625" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=5eec1004&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boKzpBxEpjhL2OZbHT53tMGVrT6jBiaCPIRbupUOODL3tjW4Kxib8dKfWZrGibVKCCk2SQP8TbnyPvzQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">hadoop-yarn</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 8 - Hadoop Yarn</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在线任务和离线任务往往是两种截然不同的作业，大多数的在线任务都是无状态的服务，它们可以在不同机器上进行迁移，彼此很难有极强的依赖关系；但是很多离线任务的拓扑结构都很复杂，有些任务需要多个作业一同执行，而有些任务需要按照依赖关系先后执行，这种复杂的调度场景在 Kubernetes 中比较难以处理。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在 Kubernetes 调度器引入调度框架之前，所有的 Pod 在调度器看来是没有任何关联的，不过有了调度框架，我们可以在调度系统中实现更加复杂的调度策略，例如保证一组 Pod 同时调度的 PodGroup[^13]，这对于 Spark 和 TensorFlow 任务非常有用。</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_svg/qE9MKluetOnroDg3ga0LQvMX7AMcH54vCJ36gAElfQpI3Fk0K5Nr1afJalIUhLcwG2mmpWLKu3vDvSRib9qlsXfKBhymkvDCt/640?wx_fmt=svg&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="color: #998;font-style: italic;line-height: 26px;"># PodGroup CRD spec</span><br/><span style="line-height: 26px;">apiVersion:</span> <span style="color: #d14;line-height: 26px;">scheduling.sigs.k8s.io/v1alpha1</span><br/><span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">PodGroup</span><br/><span style="line-height: 26px;">metadata:</span><br/>  <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">nginx</span><br/><span style="line-height: 26px;">spec:</span><br/>  <span style="line-height: 26px;">scheduleTimeoutSeconds:</span> <span style="color: #008080;line-height: 26px;">10</span><br/>  <span style="line-height: 26px;">minMember:</span> <span style="color: #008080;line-height: 26px;">3</span><br/><span style="color: #999;font-weight: bold;line-height: 26px;">---</span><br/><span style="color: #998;font-style: italic;line-height: 26px;"># Add a label `pod-group.scheduling.sigs.k8s.io` to mark the pod belongs to a group</span><br/><span style="line-height: 26px;">labels:</span><br/>  <span style="line-height: 26px;">pod-group.scheduling.sigs.k8s.io:</span> <span style="color: #d14;line-height: 26px;">nginx</span><br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Volcano 也是在 Kubernetes 上构建的批处理任务管理系统[^14]，它能够处理机器学习、深度学习以及其他大数据应用，可以支持包括 TensorFlow、Spark、PyTorch 和 MPI 在内的多个框架。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.20120320855614973" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1496" src="https://wechat2rss.xlab.app/img-proxy/?k=850a8537&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boKzpBxEpjhL2OZbHT53tMGSRwibWvKocfnEjECdDPiau7ziaSiasWjGibJuBab0AoofnkpkTa8cU2kofA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 9 - Volcano</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然 Kubernetes 能够运行一些批处理任务，但是距离在这个领域上取代 Yarn 等老牌资源管理系统上还有非常大的差距，相信在较长的一段时间内，大多数公司都会同时维护 Kubernetes 和 Yarn 两种技术栈，分别管理和运行不同类型的工作负载。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>硬多租户<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">多租户是指同一个软件实例可以为不同的用户组提供服务，Kubernetes 的多租户是指多个用户或者用户组使用同一个 Kubernetes 集群，今天的 Kubernetes 还很难做到硬多租户支持，也就是同一个集群的多个租户不会相互影响，也感知不到彼此的存在。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">硬多租户在 Kubernetes 中是一个很重要、也很困难的课题，合租公寓就是一个典型的多租户场景，多个租客共享房屋内的基础设施，硬多租户要求多个访客之间不会相互影响，你可以想象这有多么困难，Kubernetes 社区甚至有一个工作小组专门讨论和研究相关的问题[^15]，然而虽然感兴趣的工程师很多，但是成果却非常有限。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5625" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="1920" src="https://wechat2rss.xlab.app/img-proxy/?k=bc09b612&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7boKzpBxEpjhL2OZbHT53tMGSRa6vtbVPayrVhQb4JOFmZ97ibNjfibUnvsQVqI4sZ9QKdmQs3ibuddSw%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 10 - 多租户</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">尽管 Kubernetes 使用命名空间来划分虚拟机群，然而这也很难实现真正的多租户。多租户的支持到底有哪些作用呢，这里简单列几个多租户带来的好处：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Kubernetes 带来的额外部署成本对于小集群来说非常高昂，稳定的 Kubernetes 集群一般都需要至少三个运行 etcd 的主节点，如果大多数的集群都是小集群，这些额外的机器会带来很高的额外开销；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Kubernetes 中运行的容器可能需要共享物理机和虚拟机，一些开发者可能在公司内部遇到过自己的服务被其他业务影响，因为主机上容器可能隔离了 CPU 和内存资源，但是没有隔离 I/O、网络 和 CPU 缓存等资源，这些资源的隔离是相对困难的；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果 Kubernetes 能够实现硬多租户，这不仅对云服务商和小集群的使用者来说都是个福音，它还能够隔离不同容器之间的影响并防止潜在安全问题的发生，不过这在现阶段还是比较难实现的。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">每个技术都有自己的生命周期，越底层的技术生命周期会越长，而越上层的技术生命周期也就越短，虽然 Kubernetes 是当今容器界的扛把子，但是未来的事情没有人可以说的准。我们要时刻清楚手中工具的优点和缺点，花一些时间学习 Kubernetes 中设计的精髓，不过如果在未来的某一天 Kubernetes 也成为了过去，我们也应该感到喜悦，因为会有更好的工具取代它。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: Kubernetes and Container Security and Adoption Trends <a href="https://www.stackrox.com/kubernetes-adoption-security-and-market-share-for-containers/" target="_blank">https://www.stackrox.com/kubernetes-adoption-security-and-market-share-for-containers/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^2]: AWS Pricing Calculator <a href="https://calculator.aws/#/createCalculator/EC2" target="_blank">https://calculator.aws/#/createCalculator/EC2</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^3]: Considerations for large clusters <a href="https://kubernetes.io/docs/setup/best-practices/cluster-large/" target="_blank">https://kubernetes.io/docs/setup/best-practices/cluster-large/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^4]: How Microsoft drives exabyte analytics on the world’s largest YARN cluster <a href="https://azure.microsoft.com/en-us/blog/how-microsoft-drives-exabyte-analytics-on-the-world-s-largest-yarn-cluster/" target="_blank">https://azure.microsoft.com/en-us/blog/how-microsoft-drives-exabyte-analytics-on-the-world-s-largest-yarn-cluster/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^5]: 备战双 11！蚂蚁金服万级规模 K8s 集群管理系统如何设计？<a href="https://www.sofastack.tech/blog/ant-financial-managing-large-scale-kubernetes-clusters/" target="_blank">https://www.sofastack.tech/blog/ant-financial-managing-large-scale-kubernetes-clusters/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^6]: sig-scalability-kubemark dashboard <a href="https://testgrid.k8s.io/sig-scalability-kubemark#kubemark-5000" target="_blank">https://testgrid.k8s.io/sig-scalability-kubemark#kubemark-5000</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^7]: Node-local API cache #84248 <a href="https://github.com/kubernetes/kubernetes/issues/84248" target="_blank">https://github.com/kubernetes/kubernetes/issues/84248</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^8]: Multicluster Special Interest Group <a href="https://github.com/kubernetes/community/tree/master/sig-multicluster" target="_blank">https://github.com/kubernetes/community/tree/master/sig-multicluster</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^9]: Cluster API Scope and Objectives <a href="https://github.com/kubernetes-sigs/cluster-api/blob/master/docs/scope-and-objectives.md" target="_blank">https://github.com/kubernetes-sigs/cluster-api/blob/master/docs/scope-and-objectives.md</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^10]: Demystifying Kubernetes as a service – How Alibaba cloud manages 10,000s of Kubernetes clusters <a href="https://www.cncf.io/blog/2019/12/12/demystifying-kubernetes-as-a-service-how-does-alibaba-cloud-manage-10000s-of-kubernetes-clusters/" target="_blank">https://www.cncf.io/blog/2019/12/12/demystifying-kubernetes-as-a-service-how-does-alibaba-cloud-manage-10000s-of-kubernetes-clusters/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^11]: Run job on each node once to help with setup #64623 <a href="https://github.com/kubernetes/kubernetes/issues/64623" target="_blank">https://github.com/kubernetes/kubernetes/issues/64623</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^12]: StatefulSet does not upgrade to a newer version of manifests #78007 <a href="https://github.com/kubernetes/kubernetes/issues/78007" target="_blank">https://github.com/kubernetes/kubernetes/issues/78007</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^13]: Coscheduling based on PodGroup CRD <a href="https://github.com/kubernetes-sigs/scheduler-plugins/tree/master/kep/42-podgroup-coscheduling" target="_blank">https://github.com/kubernetes-sigs/scheduler-plugins/tree/master/kep/42-podgroup-coscheduling</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^14]: Volcano · A Kubernetes Native Batch System <a href="https://github.com/volcano-sh/volcano" target="_blank">https://github.com/volcano-sh/volcano</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^15]: Kubernetes Working Group for Multi-Tenancy <a href="https://github.com/kubernetes-sigs/multi-tenancy" target="_blank">https://github.com/kubernetes-sigs/multi-tenancy</a></p></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"/></p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-ratio="0.3648148148148148" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" data-type="jpeg" data-w="1080" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p><p><br/></p>



<p><a href="https://draveness.me/kuberentes-limitations/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=b05cf257&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484931%26idx%3D1%26sn%3D7418d82eaee37c9904669f77f40b57f2%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 20 Apr 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>CPU 和 GPU - 异构计算的演进与发展</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484913&amp;idx=1&amp;sn=33715933aeaa3c89ea77d8dd7d5d3596</link>
      <description>世界上大多数事物的发展规律是相似的，在最开始往往都会出现相对通用的方案解决绝大多数的问题，随后会出现为某一场</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-04-13 08:30</span> <span style="display: inline-block;"></span>
</p>

<p>世界上大多数事物的发展规律是相似的，在最开始往往都会出现相对通用的方案解决绝大多数的问题，随后会出现为某一场</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=d0870abf&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSeHtxvkEd6yknfPxunV9b9eE0PBR8ibfvHVgapAiacUvCEqIiapOlI060Q%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">世界上大多数事物的发展规律是相似的，在最开始往往都会出现相对通用的方案解决绝大多数的问题，随后会出现为某一场景专门设计的解决方案，这些解决方案不能解决通用的问题，但是在某些具体的领域会有极其出色的表现。而在计算领域中，CPU（Central Processing Unit）和 GPU（Graphics Processing Unit）分别是通用的和特定的方案，前者可以提供最基本的计算能力解决几乎所有问题，而后者在图形计算和机器学习等领域内表现优异。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="800" src="https://wechat2rss.xlab.app/img-proxy/?k=824d006a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSnQj6zUoE8mmaPCQMjsMnpa9yQxgNc5uxXnhUPicOGtEYnIun979GYRg%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - CPU 和 GPU</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">异构计算是指系统同时使用多种处理器或者核心，这些系统通过增加不同的协处理器（Coprocessors）提高整体的性能或者资源的利用率[^1]，这些协处理器可以负责处理系统中特定的任务，例如用来渲染图形的 GPU 以及用来挖矿的 ASIC 集成电路。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="letter-spacing: 0px;">中心处理单元（Central Processing Unit、CPU）[^2]一词诞生于 1955 年，已经诞生 70 多年的 CPU 在今天已经是很成熟的技术了，不过它虽然能够很好地处理通用的计算任务，但是因为核心数量的限制在图形领域却远远不如图形处理单元（Graphics Processing Unit、GPU）[^3]，复杂的图形渲染、全局光照等问题仍然需要 GPU 来解决，而大数据、机器学习和人工智能等技术的发展也推动着 GPU 的演进。</span></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今天的软件工程师，尤其是数据中心和云计算的工程师因为异构计算的发展面对着更加复杂的场景，我们在这篇文章中主要谈一谈 CPU 和 GPU 的演进过程，重新回顾一下在过去几十年的时间里，工程师为它们增加了哪些有趣的功能。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><span>CPU</span><span></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">更高、更快和更强是人类永恒的追求，在科技上的进步也不例外，CPU 的主要演进方向其实只有一个：消耗最少的能源实现最快的计算速度，无数工程师的工作都是为了实现这个看起来简单的目的。然而在 CPU 已经逐渐成熟的今天，想要提高它的性能需要花费极大的努力，我们在这一节简单展示历史上引入了哪些技术来提高 CPU 的性能。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span>制程</span><span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当我们讨论 CPU 的发展时，制程（Fabrication Process）[^4]是绕不开的关键字，相信不了解计算机的人也都听说过 Intel 处理器 10nm、7nm 的制程，而目前各个 CPU 制造厂商也都有各自的路线图来实现更小的制程，例如台积电准备在 2022 和 2023 年分别实现 3nm 和 2nm 的制造工艺。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^4]: Wikipedia: Semiconductor device fabrication <a href="https://en.wikipedia.org/wiki/Semiconductor_device_fabrication" target="_blank">https://en.wikipedia.org/wiki/Semiconductor_device_fabrication</a></p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5626213592233009" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="2060" src="https://wechat2rss.xlab.app/img-proxy/?k=495aa5f0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SS8DjoxXqicZofO3FDGcG6xUqmj76vdFjYgBIXkjyibqG90bKynEBAKqpg%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - Intel CPU 制程</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在大多数人眼中，仿佛 CPU 的制程越少就越先进，性能也会越好，但是制程并不是衡量 CPU 性能的标准，最起码制程的演进不会直接提高 CPU 的性能。工艺制程的每次提升，都可以让我们在单位面积内容纳更多的晶体管（Transistor），只有越多的晶体管才意味着越强的性能。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">越小的晶体管在开关时消耗的能量越少，既然晶体管需要一些时间充电和放电，那么消耗的能量也就越少，速度也越快，而这也解释了为什么增加 CPU 的电压可以提高它的运行速度。除此之外，更小的晶体管间隔使得信号的传输变得更快，这也能够加快 CPU 的处理速度[^5]。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="font-size: 20px;font-weight: bold;letter-spacing: 0px;">缓存</span><br/></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">缓存也是 CPU 的重要组成部分，它能够减少 CPU 访问内存所需要的时间，相信很多开发者都看过如下所示的表格，我们可以看到从 CPU 的一级缓存中读取数据大约是主存的 200 倍，哪怕是二级缓存也有将近 30 倍的提升：</p><section data-tool="mdnice编辑器" style="overflow-x: auto;"><table><thead><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><th style="border-top-width: 1px;border-color: rgb(204, 204, 204);background-color: rgb(240, 240, 240);min-width: 85px;text-align: left;">Work</th><th style="border-top-width: 1px;border-color: rgb(204, 204, 204);background-color: rgb(240, 240, 240);min-width: 85px;text-align: right;">Latency</th></tr></thead><tbody style="border-width: 0px;border-style: initial;border-color: initial;"><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">L1 cache reference</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">0.5 ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Branch mispredict</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">5   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">L2 cache reference</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">7   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Mutex lock/unlock</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">25   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Main memory reference</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">100   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Compress 1K bytes with Zippy</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">3,000   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Send 1K bytes over 1 Gbps network</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">10,000   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Read 4K randomly from SSD*</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">150,000   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Read 1 MB sequentially from memory</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">250,000   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Round trip within same datacenter</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">500,000   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Read 1 MB sequentially from SSD*</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">1,000,000   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Disk seek</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">10,000,000   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Read 1 MB sequentially from disk</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">20,000,000   ns</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">Send packet CA-&gt;Netherlands-&gt;CA</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;text-align: right;">150,000,000   ns</td></tr></tbody></table></section><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>表 1 - 2012 年延迟数字对比[^6]</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今天的 CPU 一般都包含 L1、L2 和 L3 三级缓存，CPU 访问这些缓存的速度仅次于访问寄存器，虽然缓存的速度很快，但是因为高性能需要保证尽可能靠近 CPU，所以它的成本异常昂贵。Intel 等 CPU 厂商也会通过增加 CPU 缓存的方式提高性能，更大的 CPU 缓存意味着更高的缓存命中率，也意味着更快的速度。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.49166666666666664" style="letter-spacing: 0px;display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=4587dc99&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSFCG28aOic6TohXl4ibwEiaibWLMLp8oRcsLYd383Kyrf1tVg5CQFPjWiaXQ%2F640%3Fwx_fmt%3Dpng"/></p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - CPU 缓存</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Intel 的处理器就在过去几十年的时间中不断增加 L1、L2 和 L3 的缓存大小、将 L1 和 L2 缓存集成在 CPU 中以提高访问速度并在 L1 缓存中区分数据缓存和指令缓存以提高缓存的命中率。今天的 Core i9 处理器每个核心都有 64 KB 的 L1 缓存和 256 KB 的 L2 缓存，所有的 CPU 还会共享 16 MB 的 L3 缓存[^7]。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="font-size: 20px;font-weight: bold;letter-spacing: 0px;">并行计算</span><br/></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">多线程编程在今天几乎已经是工程师的必修课了，主机上越来越多的 CPU 核心让工程师不得不去思考如何才能通过多线程尽可能利用硬件的潜力，很多人可能都认为 CPU 会按照编写的程序串行执行命令，但是真正的现实往往比这复杂得多，早在很多年前嵌入式工程师就开始尝试在单个 CPU 上并行执行指令。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">从软件工程师的角度，我们确实可以认为每一条汇编指令都是原子操作，而原子操作意味着该操作要么处于未执行的状态，要么处于已执行的状态，而数据库事务、日志以及并发控制都建立在原子操作上。不过如果再次放大指令的执行过程，我们会发现指令执行的过程并不是原子的：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.175" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=6c8a697e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSRxfblv3LdusPapysytDNfXLicictsMWF8N536nroQExia8GibocVxZGOlw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - 指令执行的步骤</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">不同机器架构执行指令的过程会有所差别，上面是经典的精简指令集架构（RISC）中命令执行需要经过的 5 个步骤，其中包括获取指令、解码指令、执行、访问内存以及写回寄存器。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">超标量处理器是可以实现指令级别并行的 CPU，它通过向处理器上的其他执行单元派发指令在一个时钟周期内同时执行多条指令[^8]，这里的执行单元是 CPU 内的资源，例如算术逻辑单元、浮点数单元等[^9]。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">超标量设计意味着处理器会在一个时钟周期发出多条指令，该技术往往都与指令流水线一起使用[^10]，流水线会将执行拆分成多个步骤，而处理器的不同部分会分别负责这些步骤的处理，例如：因为指令的获取和解码由不同的执行单元处理，所以它们可以并行执行。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.4583333333333333" style="letter-spacing: 0px;display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=841685e4&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SS9K6kOphibficglRPkx9r711WiaJ700qX1KBdZIsxaVHqOP9XTOCia53vqg%2F640%3Fwx_fmt%3Dpng"/></p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 5 - 超标量和流水线</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了超标量和流水线技术之外，嵌入式工程师们还引入了乱序执行以及分支预测等更加复杂的技术，其中乱序执行也被称作动态执行，因为 CPU 执行指令时需要先将数据加载到寄存器中，所以我们分析 CPU 的寄存器操作确定哪些指令可以乱序执行。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.2833333333333333" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=26eac9c3&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SS2s7YnnJjU4nmCFpejDKicfSusdWqGq7zpdcW8nYmobgJC8NJZSaAGmA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 6 - 乱序执行</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如上图所示，其中包含 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">R1 = R0 + R1</code>、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">R2 = R1 - R0</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">R3 = R3 + R5</code> 三条指令，其中第三条指令使用的两个寄存器与前两条无关，所以该指令可以与前两条指令并行执行，也就能减少这段代码执行所需要的时间。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为分支条件是程序中的常见逻辑，当我们在 CPU 的执行中引入流水线和乱序执行之后，如果遇到条件分支仍然需要等待分支确定才继续执行后面的代码，那么处理器可能会浪费很多时钟周期等待条件的确定。在计算机架构中，分支预测器是用来在分支确定前预判的数字电路，在遇到条件跳转指令时，它会预测条件的执行结果并选择分支执行[^11]：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">如果预判正确，可以节约等待所需要的时钟周期，提高 CPU 的利用率；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">如果预判失败，需要丢弃预判执行的全部或者部分结果，重新执行正确的分支；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为预判失败需要付出较大的代价，一般在 10 ~ 20 个时钟周期之间，所以如何提高分支预测器的准确率成为了比较重要的课题，常见的实现包括静态分支预测、动态分支预测和随机分支预测等。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上面的这些指令级并行仅仅存在于实现细节中，<strong>CPU 的使用者在外界观察时仍然会得到串行执行的观察结果</strong>，所以工程师可以认为 CPU 是能够串行执行指令的黑箱。想要充分利用多个 CPU 的资源，仍然需要工程师理解多线程模型并掌握操作系统中一些并发控制机制。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">单核的超标量处理器一般被分类为单指令单数据流（Single Instruction stream, Single Data stream、SISD）处理器，而如果处理器支持向量操作，就被分为单指令多数据流（Single Instruction stream, Multiple Data streams、SIMD）处理器，而 CPU 厂商会引入 SIMD 指令来提高 CPU 的处理能力。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span>片内布局</span><span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">前端总线是 Intel 在 1990 年在芯片中使用的通信接口，AMD 在 CPU 中也引入了类似的接口，它们的作用都是在 CPU 和内存控制器中心（也被称作北桥）之间传递数据。前端总线在刚设计时不仅灵活，而且成本很低，但是这种设计很难支持芯片中越来越多的 CPU：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="1.5706521739130435" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="368" src="https://wechat2rss.xlab.app/img-proxy/?k=497d1384&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSSkHiavJr7Ha69J3sMMpjhS2UicIJxXS5NKcibOiaYPNYPcOicnJzhvp7k6g%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 6 - 常见芯片布局</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果 CPU 不能从主存中快速获取指令和数据，那么它会花费大量的事件等待读写主存中的数据，所以越高端的处理器越需要高带宽和低延迟，而速度较慢的前端总线无法满足这样的需求。Intel 和 AMD 分别引入了点对点连接的 HyperTransport 和 QuickPath Interconnect（QPI）机制解决这个问题，上图中的南桥被新的传输机制取代了，CPU 通过集成在内部的内存控制访问内存，通过 QPI 连接其他 CPU 以及 I/O 控制器。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5622950819672131" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="610" src="https://wechat2rss.xlab.app/img-proxy/?k=5c68be8c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSuQPFkovibdkibbQqBDBHc53WOyh6syw0NsHFQ5O3842cGD80XKF0j0aw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 7 - Intel QPI</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">使用 QPI 让 CPU 直接连接其他组件确实可以提高效率，但是随着 CPU 核心数量的增加，这种连接的方式限制了核心的数量，所以 Intel 在 Sandy Bridge 微架构中引入了如下所示的环形总线（Ring Bus）[^12]：</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.7066666666666667" style="letter-spacing: 0px;display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="600" src="https://wechat2rss.xlab.app/img-proxy/?k=cacaa517&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSjYJwqMBwgueoK8FHCrbupVmS2y4wnRAWJMdmEXiaVg388jDibV82iaWmg%2F640%3Fwx_fmt%3Djpeg"/></p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 8 - 环形总线</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Sandy Bridge 在架构中引入了片内的 GPU 和视频解码器，这些组件也需要与 CPU 共享 L3 缓存，如果所有的组件都与 L3 缓存直接连接，那么片内会出现大量的连接，而这是芯片工程师不能接受的。片内环形总线连接了 CPU、GPU、L3 缓存、PCIe 控制器、DMI 和内存等部分，其中包含四个功能各异的环：数据、请求、确认和监听[^13]，这种设计减少了不同组件内部的连接同时也具有较好的可扩展性。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="letter-spacing: 0px;">然而随着 CPU 核心数量的继续增加，环形的连接会不断变大，这会增加环的大小进而影响整个环上组件之间的访问延迟，导致该设计遇到瓶颈。</span><span style="letter-spacing: 0px;">Intel 由此引入了一种新的网格微架构（Mesh Interconnect Architecture）[^14]：</span><br/></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.5791666666666667" style="letter-spacing: 0px;display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=acfac502&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSUicyaLQV4ZpdleyI4mCY8dAK8FBCECHiakUW0dxLmhgdpBA1GnzqhhUQ%2F640%3Fwx_fmt%3Dpng"/></p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 9 - 网格架构</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如上所示，Intel 的 Mesh 架构是一个二维的 CPU 阵列，网络中有两种不同的组件，一种是上图中蓝色的 CPU 核心，另一种是上图中黄色的集成内存控制器，这些组件不会直接相连，相邻的模块会通过聚合网格站（Converged Mesh Stop、CMS）连接，这与我们今天看到的服务网格非常相似。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当不同组件需要传输数据时，数据包会由 CMS 负责传输，先纵向路由后水平路由，数据到达目标组件后，CMS 会将数据传给 CPU 或者集成的内存控制器。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span><span>GPU</span><span></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">图形处理单元（Graphics Processing Unit、GPU）是在缓冲区中快速操作和修改内存的专用电路，因为可以加速图片的创建和渲染，所以在嵌入式系统、移动设备、个人电脑以及工作站等设备上应用都很广泛[^15]。然而随着机器学习和大数据的发展，很多公司都会使用 GPU 加速训练任务的执行，这也是今天数据中心中比较常见的用例。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="letter-spacing: 0px;">大多数的 CPU 不仅期望在尽可能短的时间内更快地完成任务以降低系统的延迟，还需要在不同任务之间快速切换保证实时性，正是因为这样的需求，CPU 往往都会串行地执行任务。</span><span style="letter-spacing: 0px;">GPU 的设计与 CPU 完全不同，它期望提高系统的吞吐量，在同一时间竭尽全力处理更多的任务，而设计理念上的差异最终反映到了 CPU 和 GPU 的核心数量上[^16]：</span><br/></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><img data-ratio="0.6238894373149062" style="letter-spacing: 0px;display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1013" src="https://wechat2rss.xlab.app/img-proxy/?k=bf2256bc&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SS434EB4ByiclIuInb6PKSibu0t7Kj7tpO3OhDVhDwXaNIXxZdGwgjo7rg%2F640%3Fwx_fmt%3Dpng"/></p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 10 - CPU 和 GPU 的核心</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然 GPU 在过去几十年的时间有着很大的发展，但是不同 GPU 的架构大同小异，我们在这里简单介绍下面的流式多处理器中不同组件的作用：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="1.6207627118644068" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="944" src="https://wechat2rss.xlab.app/img-proxy/?k=30144df1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSd2NgibSAR4jRgfdl3lcqbSIbTtCdJW2kHeRSqgbm1nRZ7GOZWGkDnCA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 11 - 流式多处理器</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">流式多处理器（Streaming Multiprocessor、SM）是 GPU 的基本单元，每个 GPU 都由一组 SM 构成，SM 中最重要的结构就是计算核心 Core，上图中的 SM 包含以下组成部分：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">线程调度器（Warp Scheduler）：线程束（Warp）是最基本的单元，每个线程束中包含 32 个并行的线程，它们使用不同的数据执行相同的命令，调度器会负责这些线程的调度；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">访问存储单元（Load/Store Queues）：在核心和内存之间快速传输数据；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">核心（Core）：GPU 最基本的处理单元，也被称作流处理器（Streaming Processor），每个核心都可以负责整数和单精度浮点数的计算；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了上述这些组件之外，SM 中还包含特殊函数的计算单元（Special Functions Unit、SPU）以及用于存储和缓存数据的寄存器文件（Register File）、共享内存（Shared Memory）、一级缓存和通用缓存。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span>水平扩容</span><span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">与 CPU 一样，增加架构中的核心数目是提高 GPU 性能和吞吐量最简单粗暴的手段。Fermi[^17] 是 Nvidia 早期图形处理器的微架构，在如下所示的架构中，共包含 16 个流式多处理器，512 个 CUDA 核心以及 3,000,000,000 个晶体管：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.816" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="500" src="https://wechat2rss.xlab.app/img-proxy/?k=f9e899c4&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSdAV7Xsll3hEVic45v2S1DHcQ5kKJrvibZS26XVEdZIPyj10Meotuneww%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 12 - Nvidia Fermi 架构</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了 512 个 CUDA 核心之外，上述架构中还包含 256 个用于传输数据的访问存储单元和 64 个特殊函数单元。如果我们把 2010 年发布的 Fermi 架构和 2020 年发布的 Ampere 做一个简单的对比，就可以发现两者核心数量的巨大差别：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5027844073190135" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="2514" src="https://wechat2rss.xlab.app/img-proxy/?k=6025588f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSMwZGwXlHTicoGu339BBU5ZLJDico8qTCJOO9uXEmO1Ay0sJdb5I95lGw%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 12 - Nvidia Ampere 架构</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Ampere 架构中的流式多处理器增加到了 128 个，而每个处理器中的核心数也增加到了 64 个，整张显卡上一共包含 8,192 个 CUDA 核心，是 Fermi 架构中核心数量的 16 倍。为了提高系统的吞吐量，新的 GPU 架构不只拥有了更多的核心数量，它还需要更大的寄存器、内存、缓存以及带宽满足计算和传输的需求。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span>专用核心</span><span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">最初的 GPU 仅仅是为了更快地创建和渲染图片，它们广泛存在于个人主机上承担着图像渲染的任务，但是随着机器学习等技术的发展，GPU 中出现了更多种类的专用核心来支撑特定的场景，我们在这里介绍两种 GPU 中存在的专用核心：张量核心（Tensor Core）和光线追踪核心（Ray-Tracing Core）：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.175" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=6a9b3fd7&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSYVoO8zw0pR3nZCcMoeK6RkfsX1PbzjUE0MbBVzwt47TpzI2prEpUqQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 13 - 专用核心</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">与个人电脑上的 GPU 不同，数据中心中的 GPU 往往都会用来执行高性能计算和 AI 模型的训练任务。正是因为社区有了类似的需求，Nvidia 才会在 GPU 中加入张量核心（Tensor Core）[^19]专门处理相关的任务。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><span style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">张量核心与普通的 CUDA 核心其实有很大的区别，CUDA 核心在每个时钟周期都可以准确的执行一次整数或者浮点数的运算，时钟的速度和核心的数量都会影响整体性能。</span><span style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">张量核心通过牺牲一定的精度可以在每个时钟计算执行一次 4 x 4 的矩阵运算，它的引入使得游戏中的实时深度学习任务成为了可能，能够加速度图像的生成和渲染[^20]。</span><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="letter-spacing: 0px;">计算机图形领域的圣杯是实时的全局光照，实现更好的光线追踪可以帮助我们在屏幕上渲染更加真实的图像，然而全局光照需要 GPU 进行大量的计算，而实时的全局光照更是对性能有着非常高的要求。</span><span style="letter-spacing: 0px;">传统的 GPU 架构并不擅长光线追踪等任务，所以 Nvidia 在 Turing 架构中首次引入了光线追踪核心（Ray-Tracing Core、RT Core）。</span><br/></p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.56125" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="800" src="https://wechat2rss.xlab.app/img-proxy/?k=033c5c8b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SS6ltia6ApRIpvFSYEXTRBqTDI4Kv6rQSInje7XYLp1lLJGur33hJ9iaZA%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 15 - 光线追踪核心</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Nvidia 的光线追踪核心实际上是为追踪光线设计的特殊电路，光线追踪中比较常见的算法就是 Bounding Volume Hierarchy（BVH）遍历和光线三角形相交测试，使用流式多处理器计算该算法每条光线都会花费上千条指令[^21]，而光线追踪核心可以加速这一过程。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="font-size: 20px;font-weight: bold;letter-spacing: 0px;">多租户</span><br/></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今天 GPU 的性能已经非常强大，但是无论使用数据中心提供的 GPU 实例，还是自己搭建服务器运行计算任务都很昂贵，然而 GPU 算力的拆分在目前仍然是一个比较复杂的问题，运行简单的训练任务可能占用整块 GPU，在这种情况下每提升一点 GPU 的利用率都可以降低一些成本。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.525" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=13f71321&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brRhQwobMd0Wmt6gB4fd4SSdauuf9BP24rdUuXXYEGnwpTXIH3hicG9f6zLiaiad4F6UTte7hPbicCQWQ%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 16 - 多实例 GPU</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Nvidia 最新的 Ampere 架构支持多实例 GPU（Multi-Instance GPU、MIG）技术，它能够水平切分 GPU 资源[^18]。每个 A100 GPU 都可以被拆分成 7 个 GPU 实例，每个实例都有隔离的内存、缓存和计算核心，这不仅可以满足数据中心分割 GPU 资源的需要，还能在同一张显卡上并行运行不同的训练任务。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="font-size: 22px;font-weight: bold;letter-spacing: 0px;">总结</span><br/></p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span></span></h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">从 CPU 和 GPU 的演进过程我们可以看到，所有的计算单元都受益于更精细的制作工艺，我们尝试在相同的面积内放入更多的晶体管并增加更多的计算单元、使用更大的缓存，当这种『简单粗暴』的方式因为物理上的瓶颈逐渐变得困难时，我们开始为特定领域设计专门的计算单元。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">文中没有提到的 ASIC 和 FPGA 是更加特殊的电路，在图像渲染领域之外，我们可以通过设计适用于特定领域的 ASIC 和 FPGA 电路提高某一项任务的性能，OSDI ’20 的最佳论文 hXDP: Efficient Software Packet Processing on FPGA NICs[^23] 就研究了如何使用可编程的 FPGA 更高效地处理数据包的转发，而在未来越来越多的任务会使用专门的硬件。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="font-size: 22px;font-weight: bold;letter-spacing: 0px;">推荐阅读</span><br/></p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span></span></h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">An Introduction to Modern GPU Arhitecture <a href="http://download.nvidia.com/developer/cuda/seminar/TDCI_Arch.pdf" target="_blank">http://download.nvidia.com/developer/cuda/seminar/TDCI_Arch.pdf</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Wikiwand: Tick–tock model <a href="https://www.wikiwand.com/en/Tick–tock_model" target="_blank">https://www.wikiwand.com/en/Tick–tock_model</a></section></li></ul></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"/></p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p>



<p><a href="https://draveness.me/heterogeneous-computing/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=880eb20f&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484913%26idx%3D1%26sn%3D33715933aeaa3c89ea77d8dd7d5d3596%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 13 Apr 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>为什么早期的 Windows 需要整理碎片</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484891&amp;idx=1&amp;sn=95df2da5d622ba3f073755c7ba89d84d</link>
      <description></description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-04-06 08:30</span> <span style="display: inline-block;"></span>
</p>

<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=9a8bdf77&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bp0QRnuUP8xGFx8V0GicF78RI08OtlpKHFJFnwJPFicDc8mMHKrJqfuIl6EBTmHAVMMacLVYIUdmeibQ%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">为什么这么设计（Why’s THE Design）是一系列关于计算机领域中程序设计决策的文章，我们在这个系列的每一篇文章中都会提出一个具体的问题并从不同的角度讨论这种设计的优缺点、对具体实现造成的影响。如果你有想要了解的问题，可以在文章下面留言。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">记得十几年前还在用早期 Windows 系统的时候，每用一段时间系统都会变得很卡顿，这时候需要打开系统提供的下面的磁盘碎片整理程序，当碎片整理完成后会感觉到系统变得稍微流畅了一些。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.732" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="500" src="https://wechat2rss.xlab.app/img-proxy/?k=a9b020d2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bp0QRnuUP8xGFx8V0GicF78RWcxV7ic94KGMEMontLQmow93qIuRDc2rsfbLwgEghVHApwC13skj5zQ%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - Windows 磁盘碎片整理程序</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在文件系统中，碎片整理（Defragmentation）是减少文件系统中碎片的过程[^2]，该过程会将磁盘上相同文件的的内容按照顺序重新排列并利用压缩算法去除文件之间的空隙，有点类似垃圾回收中的标记压缩算法[^3]。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作者已经很多年都不使用 Windows 操作系统了，上大学之后就一直在用 macOS 到今年也有七八年的时间了，最近在研究文件系统时突然想到小时候经常见到的磁盘碎片整理程序仿佛已经消失了。不知道今天的 Windows 是否还需要磁盘整理，但是无论是 Linux 还是 macOS 上都没有类似的工具[^1]，这不禁让作者想要研究一下背后的原因。总得来说，操作系统需要碎片整理主要有以下两个原因：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">文件系统的设计使得资源被释放后出现很多碎片；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">机械硬盘的随机读写性能比顺序读写差几个数量级；</section></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>文件系统</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上古时代的 Windows 使用了非常简单的文件系统 - 文件分配表（File Allocation Table、FAT）[^4]，该文件系统的设计是造成磁盘出现碎片的根本原因，不过在分析该系统之前，我们在这里先介绍一下文件系统的历史。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">FAT 是 1977 年最开始为软盘设计的文件系统，软盘是一种非常古老的存储介质，今天的电脑基本上也都移除了软盘的驱动，当时的软盘都只能整盘的写入，所以更新软盘上的数据其实会覆盖原来的全部内容，这也就不存在所谓的磁盘碎片了。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在该文件系统被使用后不久，随着机械硬盘（Hard Disk Driver、HDD）的价格开始变得逐渐低廉并被广泛使用，微软选择在 DOS 和 Windows 9x 系列扩展 FAT 文件系统以支持更大的空间，而数据库等磁盘敏感型的应用也迅速变得非常热门。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">每次写入数据都需要重新写入整张软盘是比较低效的做法，不过因为软盘的存储空间比较小，所以这在当时也是可以接受的，但是随着存储介质的空间变得越来越大，我们需要引入随机写入提高效率，支持随机写入的 FAT 也是很简单的文件系统[^5]。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.24166666666666667" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=cefc6046&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bp0QRnuUP8xGFx8V0GicF78RVPAZZeNeFMf7sp2kgYGg8yzx8EYHxZg3FoDap3icj3hX7HLBy6NtyIA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - FAT 的写入</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如上图所示，如果我们要向一块新的硬盘写入多个文件 A、B 和 C，这些文件会在 FAT 文件系统中按照顺序存储，文件之间不存在任何碎片。然而如果在这时我们决定删除其中 B 文件并向文件系统中写入更大的文件 D，会出现比较有趣的情况。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.175" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=e766bca6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bp0QRnuUP8xGFx8V0GicF78RRdwlIqLOib5gTXET8L2YzaXo0zmSpDiaf3xWEY8ZwBw96MTJunx4kIIQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - FAT 的碎片</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">FAT 文件系统在磁盘上会先找到删除 B 后留下的两块空闲位置并在其中写入 D 文件的一部分，随后又会在 C 文件后找到另一块空闲位置并将 D 文件的剩余内容全部写到该位置。这样造成的结果是 D 文件会分散在硬盘上，当用户读取 D 文件时需要触发多次随机读取。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">FAT 是一种非常简单、原始的文件系统，它的设计和实现从今天的角度来看都很糟糕，每次写入文件时不仅不会检查空闲空间的大小、造成文件碎片，还不包含碎片管理功能，使用时间过长还需要用户手动触发磁盘的碎片整理，这其实是很糟糕的设计和用户体验。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>机械硬盘</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">机械硬盘（Hard Disk Drive、HDD）是一种基于电子的、非易失的机械数据存储设备，它使用磁性存储器存储并查找磁盘上的数据，在读取和写入数据的过程中，硬盘机械臂连接的磁头会读写磁盘表面的位[^6]。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">正是因为磁盘具有比较复杂的机械结构，所以磁盘的读取和写入都要花费很多时间，数据库的读写性能也基本都依赖于磁盘的性能，如果我们在使用机械硬盘的数据库中随机查询一条数据，这可能会触发磁盘的随机 I/O，然而将数据从磁盘读取到内存中所需要的成本是非常大的，普通磁盘（非 SSD）加载数据需要经过队列、寻道、旋转以及传输的这些过程，大概要花费 10ms 左右的时间[^7]。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.325" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=46fcb676&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bp0QRnuUP8xGFx8V0GicF78RuR3RC6HEEkVibsDBLfLRnSRPpEP00IKkFJJQpRltuE5ZM9YOHhqbblQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - 磁盘的随机 I/O</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当我们在磁盘中读取文件时，如果文件的内容散落到了磁盘上的不同位置，它可能需要执行多次随机 I/O 才能够获取该文件的全部内容，这对于机械结构的磁盘来说是很大的额外开销。如果文件的内容会存储在相同的位置，那么读取文件时仅需要执行一次随机 I/O，后续的读取都可以使用速度约为 40 MB/s 的顺序 I/O，这可以显著减少文件的读取时间。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">碎片化的文件在机械硬盘上会导致比较严重的性能问题，在理想情况下，我们希望磁盘能够达到它的读写带宽上限；但是在实际使用过程中，频繁的随机 I/O 让磁盘将大多数的时间都花在寻道和旋转上，导致其无法全力工作。与机械硬盘相比，具有电子结构的固态硬盘能够更好地耐受碎片化的文件系统，而整理碎片反而会影响它的使用寿命。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">相信很多工程师在进入这一行业之前都会使用 Windows 系统，早期的 Windows 是桌面系统近乎唯一的选择，作者对今天的这个题目有比较特殊的感情，在研究操作系统的文件系统之前一直都没有想过这个问题，直到碎片化的文件系统一词让自己想到了十多年前的疑问，这种豁然开朗的感觉在今天还是很少能够体会到的。我们简单总结一下早期的 Windows 需要碎片整理的两个原因：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">早期的 Windows 系统使用简单的 FAT 文件系统，该文件系统经过频繁的写入删除操作会导致大文件散落在磁盘的各处；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">机械硬盘在十多年前还是当时的主流设备，不过因为硬盘的机械结构，所以随机读写磁盘上的位置需要物理上的寻道和旋转，导致该过程极其缓慢；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">到最后，我们还是来看一些比较开放的相关问题，有兴趣的读者可以仔细思考一下下面的问题，与前面的问题不同，作者会在后面的文章中解答这两个问题：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">为什么 macOS 的文件系统不需要整理碎片？</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">为什么 Linux 的文件系统不需要整理碎片？</section></li></ul><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">如果对文章中的内容有疑问或者想要了解更多软件工程上一些设计决策背后的原因，可以在博客下面留言，作者会及时回复本文相关的疑问并选择其中合适的主题作为后续的内容。</p></blockquote><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>推荐阅读</h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">034 <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484691&amp;idx=1&amp;sn=673d4a95402a948b8fce9fd410510d06&amp;chksm=fe795a18c90ed30e6452ee4e29cea2da04d79c8037c51c0ae5bacb88909c702d3b91eea6dc27&amp;scene=21#wechat_redirect" textvalue="为什么 HugePages 可以提升数据库性能" data-itemshowtype="0" tab="innerlink" data-linktype="2">为什么 HugePages 可以提升数据库性能</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">032 <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484464&amp;idx=1&amp;sn=32bd106c7495207cabe97368a96697bb&amp;chksm=fe795b3bc90ed22dbd0dd0a520a74b6a1e27d4126a930ec2bba15773c38aafab166abfc42899&amp;scene=21#wechat_redirect" textvalue="为什么 CPU 访问硬盘很慢" data-itemshowtype="0" tab="innerlink" data-linktype="2">为什么 CPU 访问硬盘很慢</a></section></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>参考资料</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: Do Macs need to be defragmented? <a href="https://apple.stackexchange.com/questions/829/do-macs-need-to-be-defragmented" target="_blank">https://apple.stackexchange.com/questions/829/do-macs-need-to-be-defragmented</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^2]: Wikipedia: File system fragmentation <a href="https://en.wikipedia.org/wiki/File_system_fragmentation" target="_blank">https://en.wikipedia.org/wiki/File_system_fragmentation</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^3]: 内存管理设计精要 <a href="https://draveness.me/system-design-memory-management/" target="_blank">https://draveness.me/system-design-memory-management/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^4]: Wikipedia: Design of the FAT file system <a href="https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system" target="_blank">https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^5]: Why is it less necessary (comparing to FAT) to do disk defragmentation for NTFS hard drive? <a href="https://www.quora.com/Why-is-it-less-necessary-comparing-to-FAT-to-do-disk-defragmentation-for-NTFS-hard-drive?top_ans=69674612" target="_blank">https://www.quora.com/Why-is-it-less-necessary-comparing-to-FAT-to-do-disk-defragmentation-for-NTFS-hard-drive?top_ans=69674612</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^6]: Wikipedia: Hard disk drive <a href="https://en.wikipedia.org/wiki/Hard_disk_drive" target="_blank">https://en.wikipedia.org/wiki/Hard_disk_drive</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^7]: 为什么 CPU 访问硬盘很慢 <a href="https://draveness.me/whys-the-design-cpu-and-disk/" target="_blank">https://draveness.me/whys-the-design-cpu-and-disk/</a></p></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"/></p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p><p><br/></p>



<p><a href="https://draveness.me/whys-the-design-windows-defragmentation/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=abbd5787&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484891%26idx%3D1%26sn%3D95df2da5d622ba3f073755c7ba89d84d%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 06 Apr 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>2020 年总结 · 渐入佳境</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484882&amp;idx=1&amp;sn=1732919e077fc5b9dda9c353ca58c05a</link>
      <description>转眼间 2021 年已经过去将近三分之一了，然而作者的 2020 年年终总结还没有写，本来已经把这件事情抛在</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-04-01 09:22</span> <span style="display: inline-block;"></span>
</p>

<p>转眼间 2021 年已经过去将近三分之一了，然而作者的 2020 年年终总结还没有写，本来已经把这件事情抛在</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=2197e8e9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcGLgVv7bXQHFZO7ticrtTZrCOlWeKPHiaktAI1YcTCsEVBK3xEkZjuGAw%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">转眼间 2021 年已经过去将近三分之一了，然而作者的 2020 年年终总结还没有写，本来已经把这件事情抛在脑后不打算写了，但是竟然还有人在博客留言问 2020 年总结在哪里，而自己也总觉得不写年终总结也缺了点什么，所以在愚人佳节到来之际，准备把这件拖了几个月没有完成的事情解决了。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">2020 年是一个数字看起来很好的年份，但是实际上发生了很多事情，相信也改变了很多人的生活，这一年作者基本上全年都是在家远程工作的，这确实能够节约大量的通勤时间，个人倒是比较喜欢远程的工作模式，但是相信还是有很多人更习惯在办公室面对面的交流。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>目标和关键结果</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作者在前面的博客曾经提到过，自己一直都使用 OKR 来管理个人的目标[^1]，在 2020 年年初时也曾经定下了全年的 OKR，其中包含三个大目标和十几个关键结果，这里仅展示其中的一部分：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Objective 1：提高技术影响力</section></li><ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><span style="text-decoration:line-through;">KR1：完成 Go 语言设计与实现全书</span></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">KR2：社区做三次任意主题的技术分享；(1/3)</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">KR3：全年更新 42 篇为什么这么设计系列文章；(24/42)</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><span style="text-decoration:line-through;">KR4：全年分享 4 篇技术无关的博客；(4/4)</span></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">KR5：全年分享 4 篇系统设计精要；(1/4)</section></li></ol><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Objective 2：提高工程能力</section></li><ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><span style="text-decoration:line-through;">KR1：将 Kubernetes 作为主要的工作重点；</span></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">KR2：成为 Kubernetes 前 50 的提交者；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><span style="text-decoration:line-through;">KR3：深入研究操作系统；</span></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><span style="text-decoration:line-through;">KR4：深入研究计算机网络协议；</span></section></li></ol></ul><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.43119266055045874" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1090" src="https://wechat2rss.xlab.app/img-proxy/?k=ce030f5e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcRjho9KM5y5yX8kDTpQdutfkdwUHfCh1xvKKbvFXGxHpRZuvAx2ELgg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - OKR</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果说要给去年的 OKR 情况打个分的话，作者会给<strong>提高技术影响力</strong>这个目标打 0.7 分，给<strong>提高工程能力</strong>这个目标打 0.5 分。完成的部分这里也就不多介绍了，后面的内容会总结 2020 年的个人成长，这里简单做一下反思：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">关于社区分享：社区做三次任意主题的技术分享看起来不是一个特别困难的关键结果，期间也有很多人找到作者做一些分享，但是都因为个人觉得时间不足推掉了，所以没有实现预定的计划；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">关于为什么这么设计：年初的时候其实过于高估了自己的业余时间和执行力，全年更新 42 篇 为什么这么设计系列是个很难完成的目标，哪怕留了很多的缓冲（原本打算一年完成 52 篇），最终也面临着毅力不足和选题困难的双重考验；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">关于系统设计精要：这是一个非常耗费时间的系列文章，每个选题都需要耗费很长的时间进行学习和总结，同系列的第二篇文章已经完成了理论部分的内容，但是在实践部分拖了太久导致后续没有动力完成；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">关于 Kubernetes 社区：今年已经把自己的工作重点转移到了 Kubernetes 上，但是社区内部的工作和公司内的场景有很大的不同，再加上自己的时间不是特别充裕，所以最终没有在社区内做出太多的贡献；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在内容创作和输出方面这一年虽然也做了很多，但是仍然没有达到年初的预期，作为内容创作者有时是非常痛苦的，在后期可能会遇到很多选题的障碍，这时需要不断接收新的输入才能持续地对外输出一些内容。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">总的来说，作者在年初的时候过于乐观地估计了个人的创作能力和业余时间，因为在计划实现的细节上缺少了一些毅力和打持久战的准备，所以没有完成年初定下的计划，还是比较可惜的。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>个人成长</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">2020 年已经是作者毕业的第三个年头了，相信三年的工作经验已经使我们成为了更加成熟的工程师，对于计算机科学领域的基础知识已经有了相当的了解和积累，能够找到很多不同问题的相似性并快速得到并实现最优解。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>技术成长<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作者在这一年的时间没有阅读太多技术相关的书籍，过滤了一下完成的任务发现只读了下面这些本书籍：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Scheduling Theory, Algoritms and Systems</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">垃圾回收的算法与实现</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Linux Kernel Development</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Understanding the Linux Kernel</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Understanding Linux Network Internals</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Computer Architecture: A Quantative Approach</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">System Performance: Enterprise and the Cloud</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">...</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">其中大多数的书籍大都是为了研究相关领域学习的，如果对该领域不是特别感兴趣，也不建议花时间阅读。从这个书单可以看到作者在技术方面主要关注的领域，即操作系统和 Linux 内核，其中包括 Linux 的控制组、大名鼎鼎的 eBPF、内核调度和网络等等内容。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5268214571657326" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1249" src="https://wechat2rss.xlab.app/img-proxy/?k=343aaee9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcSicibGcPdw6d1RUdgUUzHJZxguynbk6chGIwxicxYnaibn5MoSa5ppb9Tg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - eBPF 原理</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">操作系统真的是每个合格的软件工程师必须深入理解的话题，很多现实场景中的问题都能够在操作系统中找到类似的场景，比如：资源的调度、内存的分配。哪怕是分布式系统面临的问题，我们也能在操作系统中找到对应的解决方案，如果有时间的话可以每年找一本新的书重新学习操作系统，说不定可以发现一些新的知识。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了书籍之外，作者在过去一年时间也阅读了很多系统领域很多顶会的论文，例如：OSDI、SOSP 和 NSDI 等等，主要关注的还是数据中心、云计算和分布式系统等方向。你可以看到这些年的很多论文都与机器学习关系比较密切，通过机器学习利用历史数据来提高调度决策、集群可用性等等，虽然作者对这些领域都不是特别感兴趣，但是不得不承认的是与其通过工程师的经验决定一些参数，使用机器学习在某些场景往往能够大幅提高决策的质量。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5" style="display: block;margin-right: auto;margin-left: auto;" data-type="jpeg" data-w="848" src="https://wechat2rss.xlab.app/img-proxy/?k=31ef13b6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcsX90EJuG8A947KBa2hI2gYk2VwVOGLGUwXuk0t4ibuCjQnS5eOFPGHw%2F640%3Fwx_fmt%3Djpeg"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - 机器学习</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作者在 2020 年阅读了以下几篇非常有趣的论文，感兴趣的读者可以看一下相关的文章，也可以直接阅读论文的原文：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484486&amp;idx=1&amp;sn=7608bea8f26e9d3598d1d62ad54ede80&amp;chksm=fe795b4dc90ed25bfe8ea92a9730e3dad235ca4efc0fd1e4330027ef079cb01b94ecd3596b69&amp;scene=21#wechat_redirect" textvalue="操作系统模型与乐高积木 · OSDI &#39;18" data-itemshowtype="0" tab="innerlink" data-linktype="2">操作系统模型与乐高积木 · OSDI &#39;18</a> 是 OSDI &#39;18 的最佳论文，它探索了分布式操作系统的可能性以及分布式系统带来的性能损失；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484674&amp;idx=1&amp;sn=c673699eeef92c28ae9e30a6cd7353a3&amp;chksm=fe795a09c90ed31f8eb25812c0299d98d5e7f4f7ba477b5c44771e6d347f3974bdaf4e1c40c3&amp;scene=21#wechat_redirect" textvalue="NVMe 固态硬盘与键值存储 KVell · SOSP &#39;19" data-itemshowtype="0" tab="innerlink" data-linktype="2">NVMe 固态硬盘与键值存储 KVell · SOSP &#39;19</a> 研究了在非易失性内存中如何实现键值存储，底层存储介质的改变使得我们过去的经验失效了，例如：随机 I/O 的速度远远慢于顺序 I/O，这也进而影响上层的应用程序；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然说计算机科学有时教会我们面向接口编程，不用在乎底层的实现细节，但是在性能变得逐渐重要、异构计算以及存储介质的不断演进的今天，我们也需要低头看一看手中的硬件并想一想它到底带来了哪些变化，而我们又能够做些什么。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>软技能<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作者记得两三年前在地铁上看过一本书《软技能：代码之外的生存指南》，虽然这本书的具体内容竟忘了，但是不得不说软技能对于任何人都是很重要的。没有人可以否认软技能对我们职业生涯的帮助，学习编程之外的技能不仅有益于我们的职业，也能够帮助我们更好地认识世界。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">2020 年作者阅读了很多编程之外的书籍，例如：学会提问 、 如何阅读一本书 ，这些知识可能在短期内不会对我们的生活有太多的改变，但是在长期来看，<span style="text-decoration:line-through;">我们都会死</span>，对我们的生活确实是有帮助的，尤其是当我们发现技术达到了瓶颈之后，学习一些交叉学科的知识往往会得到更高的投资回报。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了计算机科学之外，作者在 2020 年还阅读了很多哲学和经济学领域的书籍，这些知识虽然对技术的提升没有太多的帮助，但是却能够帮我们更好地思考并认识世界，在学习的过程中我们也会发现不同领域有一些惊人的相似性。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.175" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=9b3c562f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcVbokXXRSazbiajoWcKDW6mHQlKrLPOAPBaibT3XyNDExFibGyLq8X4DlA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - 三大支柱</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">计算机科学、哲学和经济学是作者目前比较感兴趣的学科，虽然作者对于计算机科学只是稍有了解，而对于后两者更是一无所知，但是也正是因为一无所知，所以才更要花费时间和精力去学习并认识这个世界，如果各位读者有推荐的读物非常欢迎在文章下面留言。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作者是因为上大学时一位学长的分享才开始写博客的，最开始的博客只是作为笔记，记得当时在学 CTMCP（Concepts, Techniques, and Models of Computer Programming），然后每天都把一些类似笔记的内容放到博客上，14 年写的很多笔记已经被删掉了，目前能够看到最早的博客还是学习 Prolog 的一些内容。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">从最开始写博客到今天已经过去七年了，作者其实能够感觉到写作对个人表达能力的提升，这种改变是异常缓慢的，只有回过头来再看过去的内容才会觉得自己确实有了很大的变化，今天的推理和论证与过去相比变得更加严谨，在回答问题和评论时也会斟酌用词并尽量给出完善的回答，而这一点的进步也是最近通过总结和反思感受到的，也让作者更加坚信<strong>分享内容并接受其他人的疑问和挑战能够让自己更快的成长</strong>。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>社区</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上面简单分享了作者过去一年的个人成长，这里又到了与各位读者分享数据的时候了，与去年一样我们这里仍然按照内容和社交网络两部分介绍作者输出的内容以及相关的数据。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>内容<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了我们在 2019 提到的的两个系列 — Go 语言设计与实现 和 <a target="_blank" href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzU5NTAzNjc3Mg==&amp;action=getalbum&amp;album_id=1319543853325664257&amp;scene=173&amp;from_msgid=2247484221&amp;from_itemidx=1&amp;count=3#wechat_redirect" textvalue="为什么这么设计" tab="innerlink" data-linktype="2">为什么这么设计</a> 之外，2020 年因为关注了一些学术界的研究，所以写了一些关于顶会论文的文章，也就是 <a target="_blank" href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzU5NTAzNjc3Mg==&amp;action=getalbum&amp;album_id=1386423615767363584&amp;scene=173&amp;from_msgid=2247484827&amp;from_itemidx=1&amp;count=3#wechat_redirect" textvalue="看看论文" tab="innerlink" data-linktype="2">看看论文</a> 系列，在这里也就简单介绍下这三部分内容的具体情况。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>Go 语言设计与实现<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">比较早关注作者博客的读者应该都知道，作者在 19 年的时候就开始写一系列关于 Go 语言实现的文章，经过整整一年的时间，在 2019 年底对这个主题已经变得不是特别感兴趣，达到了一个非常疲惫的阶段。之前虽然有编辑联系过希望能够出版，但是因为内容也没有写多少，所以一直没有答应下来，直到 2020 年，人民邮电出版社的编辑正好找到我，当时觉得需要外部的条件刺激让自己能够下定决心完成这本书的编写，在这里也对之前联系过的编辑老师们说句抱歉了🙏🙏🙏。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">经过一年时间的努力，Go 语言设计与实现 这本书目前已经交稿，估计纸质版的书籍会在 <strong>2021 年下半年</strong>正式出版，感兴趣的读者也可以关注一下，如果你对书籍的质量不放心，也可以在官网上阅读这些内容，如果觉得不适合或者写得很差，也不用花这个『冤枉钱』。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.7713697219361483" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1942" src="https://wechat2rss.xlab.app/img-proxy/?k=e0b0b5c6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOc474C2CNwNOsnUzJicdlY8QxOP9icRm6rlsOs4hftCbcFRU16klnHM6Dw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 5 - Go 语言设计与实现访问量</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今天的 Go 语言已经变得非常热门，生态环境也逐渐成熟，越来越多的公司和开发者都选择使用 Go 语言开发 Web 应用程序。根据 Google 分析的统计，过去一年共有 134,000 不同用户访问了 Go 语言设计与实现 这本书，PV 达到了 1,000,000 左右。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>为什么这么设计<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><a target="_blank" href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzU5NTAzNjc3Mg==&amp;action=getalbum&amp;album_id=1319543853325664257&amp;scene=173&amp;from_msgid=2247484221&amp;from_itemidx=1&amp;count=3#wechat_redirect" textvalue="为什么这么设计" tab="innerlink" data-linktype="2">为什么这么设计</a>（Why’s THE Design，WTD）系列文章是作者在 2019 年开的坑，2020 年又写了 24 篇与这个主题相关的内容，这个系列去年全年的访问量大约在 320,000 左右。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.9206680584551148" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1916" src="https://wechat2rss.xlab.app/img-proxy/?k=61490c2b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcQgVLAU9wTRM6ELl9EGY3wZRNSDCTgfqkgtJt8FOb42LvqfYGrPlMBA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 6 - 为什么这么设计 2020 年访问量</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作者在完成这个系列文章之前，对于网络、操作系统等领域的某些细节掌握的不是很清楚，虽然了解整个框架和原理，但是在细节上不够精准，通过研究特定领域的特定话题反而驱动作者补全细节上的一些漏洞，在分享经验和公开讨论之中完善了知识结构，而这也是作者持续写文章的动力之一。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>看看论文<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">最后介绍的 <a target="_blank" href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzU5NTAzNjc3Mg==&amp;action=getalbum&amp;album_id=1386423615767363584&amp;scene=173&amp;from_msgid=2247484827&amp;from_itemidx=1&amp;count=3#wechat_redirect" textvalue="看看论文" tab="innerlink" data-linktype="2">看看论文</a> 是作者在 2020 年下半年开始写的一系列文章，它是一系列分析计算机和软件工程领域论文的文章，我们在这个系列的每一篇文章中都会阅读一篇来自 OSDI、SOSP 等顶会中的论文，这里不会事无巨细地介绍所有的细节，而是会筛选论文中的关键内容。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作者在过去一年写了 8 篇相关的文章，大多数的文章都来自 2018、2019 年的 SOSP 和 OSDI，因为看这些论文纯粹是出于了解业界前沿科技以及个人兴趣，也不太关注究竟会有多少阅读量，所以这里也就不展示相关的数据了，<span style="text-decoration:line-through;">感觉数据应该很差</span>。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>社交网络<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这一节仍然按照惯例公开 2020 年在几个主要社交网络中的访问量和阅读量情况。首先为各位读者分享 2019 年博客面向信仰编程的情况，过去一年时间作者总共发了 48 篇博客，全年的 PV 为 1,808,949、UV 为 322,288，与去年相比同比增长 63.77% 和 48.64%。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.32567049808429116" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1566" src="https://wechat2rss.xlab.app/img-proxy/?k=c2845b1c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcx0iac9lO3xyBphgSVFdR8GHkTR6hwPOpxqnk5Pv7Ft99utlP79ibGYOQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">2020-blog-statistics</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 6 - 2020 年博客访问量</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">微信公众号的数据由于只会保留 90 天，所以很难看到全年的增长曲线，年初的订阅量大概在 5,800 左右，到 2020 年底达到了 24,155，全年总共获得了 20,000 左右的新订阅。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.4704251386321627" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="2164" src="https://wechat2rss.xlab.app/img-proxy/?k=9bb0d7c5&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcY1HfaJYHJ3G0yIRuHC6wahsXD7MKWX7hib0W20yhvFW7iaUNDgd7ZreA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 7 - 微信公众号数据</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">从数据可以看到，全年的博客访问量和微信公众号的订阅都有比较明显地增长，在这里也感谢一直以来订阅和关注作者内容的各位读者朋友。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>写在最后</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">时间是我们非常宝贵的财富，我们每一年都会选择将时间投资在不同的东西上以期望获得相应的回报，无论是投入学习获得更多的知识和经验，还是投入娱乐获得得到放松和良好的心态，这都是我们自己做出的选择和规划。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">正如每一年，2020 年对作者来说也是很重要的一年，这一年最大的变化有两点首先是尝试学习多个学科的知识，其次是更加关注软件工程领域中的理论知识、研究学术界最新的研究方向。希望未来能够逐渐扩大自己的外延，更好地了解这个世界并认识世界运行的一些底层逻辑。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: <a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484226&amp;idx=1&amp;sn=e4e187880ef81c13793858d91194198a&amp;chksm=fe795c49c90ed55f09ebea1685ca6ac19d7cca96070eca8657e3b0ccb336e8f2cbd26c440aeb&amp;token=1162789066&amp;lang=zh_CN&amp;scene=21#wechat_redirect" textvalue="如何管理自己的时间资产 " tab="innerlink" data-linktype="2">如何管理自己的时间资产 </a></p></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"/></p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p><p><br/></p>



<p><a href="https://draveness.me/2020-summary/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=841e4713&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484882%26idx%3D1%26sn%3D1732919e077fc5b9dda9c353ca58c05a%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Thu, 01 Apr 2021 09:22:00 +0800</pubDate>
    </item>
    <item>
      <title>你该如何为 Kubernetes 定制特性</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484872&amp;idx=1&amp;sn=9b2cf32f7e6de939236d25cfd05ac0de</link>
      <description>谈谈扩展 Kubernetes 的几种策略，CNI、CSI、CRI、设备插件、调度框架和准入控制等</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-03-30 08:30</span> <span style="display: inline-block;"></span>
</p>

<p>谈谈扩展 Kubernetes 的几种策略，CNI、CSI、CRI、设备插件、调度框架和准入控制等</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=71a44d04&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOc8BQBujtTVt5AaOic45VT1PoUUoNcMPqNoe8C7Nd9peyT1sRibQgCuMkA%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 是非常复杂的集群编排系统，然而哪怕包含丰富的功能和特性，因为容器的调度和管理本身就有较高的复杂性，所以它无法满足所有场景下的需求。虽然 Kubernetes 能够解决大多数场景中的常见问题，但是为了实现更加灵活的策略，我们需要使用 Kubernetes 提供的扩展能力实现特定目的。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">每个项目在不同的周期会着眼于不同的特性，我们可以将项目的演进过程简单分成三个不同的阶段：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><strong style="color: black;">最小可用</strong>：项目在早期更倾向于解决通用的、常见的问题，给出开箱即用的解决方案以吸引用户，这时代码库的规模还相对比较小，提供的功能较为有限，能够覆盖领域内 90% 的场景；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><strong style="color: black;">功能完善</strong>：随着项目得到更多的使用者和支持者，社区会不断实现相对重要的功能，社区治理和自动化工具也逐渐变得完善，能够解决覆盖内 95% 的场景；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><strong style="color: black;">扩展能力</strong>：因为项目的社区变得完善，代码库变得逐渐庞大，项目的每个变动都会影响下游的开发者，任何新功能的加入都需要社区成员的讨论和审批，这时社区会选择增强项目的扩展性，让使用者能够为自己的场景定制需求，能够解决覆盖内 99% 的场景；</section></li></ul><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.19166666666666668" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=b6c95ba6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcuKl294icKs1q8AMwojcibrCXJKV39F7Qf5Jh3hrwt0h4eKVq010rl7Ug%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - 开源项目的演进</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">从 90%、95% 到 99%，每个步骤都需要社区成员花费很多精力，但是哪怕提供了较好的扩展性也无法解决领域内的全部问题，在一些极端场景下仍然需要维护自己的分支或者另起炉灶满足业务上的需求。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">然而无论是维护自己的分支，还是另起炉灶都会带来较高的开发和维护成本，这需要结合实际需求进行抉择。但是能够利用项目提供的配置能力和扩展能力就可以明显地降低定制化的开发成本，而我们今天要梳理的就是 Kubernetes 的可扩展性。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>扩展接口</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">API 服务器是 Kubernetes 中的核心组件，它承担着集群中资源读写的重任，虽然社区提供的资源和接口可以满足大多数的日常需求，但是我们仍然会有一些场景需要扩展 API 服务器的能力，这一节简单介绍几个扩展该服务的方法。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>自定义资源<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">自定义资源（Custom Resource Definition、CRD）应该是 Kubernetes 最常见的扩展方式[^1]，它是扩展 Kubernetes API 的方式之一。Kubernetes 的 API 就是我们向集群提交的 YAML，系统中的各个组件会根据提交的 YAML 启动应用、创建网络路由规则以及运行工作负载。</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bobJaQXCyPCToSCw8kZicKOcYEtm9ZdlUOJ0oxTRDF88OOjqvpCiaFHibBES6t6ic2hH2omaNYicJ82jNw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="line-height: 26px;">apiVersion:</span> <span style="color: #d14;line-height: 26px;">v1</span><br/><span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">Pod</span><br/><span style="line-height: 26px;">metadata:</span><br/>  <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">static-web</span><br/>  <span style="line-height: 26px;">labels:</span><br/>    <span style="line-height: 26px;">role:</span> <span style="color: #d14;line-height: 26px;">myrole</span><br/><span style="line-height: 26px;">spec:</span><br/>  <span style="line-height: 26px;">containers:</span><br/>    <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">web</span><br/>      <span style="line-height: 26px;">image:</span> <span style="color: #d14;line-height: 26px;">nginx</span><br/>      <span style="line-height: 26px;">ports:</span><br/>        <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">web</span><br/>          <span style="line-height: 26px;">containerPort:</span> <span style="color: #008080;line-height: 26px;">80</span><br/>          <span style="line-height: 26px;">protocol:</span> <span style="color: #d14;line-height: 26px;">TCP</span><br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Pod</code>、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Service</code> 以及 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Ingress</code> 都是 Kubernetes 对外暴露的接口，当我们在集群中提交上述 YAML 时，Kubernetes 中的控制器会根据配置创建满足条件的容器。</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bobJaQXCyPCToSCw8kZicKOcYEtm9ZdlUOJ0oxTRDF88OOjqvpCiaFHibBES6t6ic2hH2omaNYicJ82jNw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="line-height: 26px;">apiVersion:</span> <span style="color: #d14;line-height: 26px;">apiextensions.k8s.io/v1</span><br/><span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">CustomResourceDefinition</span><br/><span style="line-height: 26px;">metadata:</span><br/>  <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">crontabs.stable.example.com</span><br/><span style="line-height: 26px;">spec:</span><br/>  <span style="line-height: 26px;">group:</span> <span style="color: #d14;line-height: 26px;">stable.example.com</span><br/>  <span style="line-height: 26px;">versions:</span><br/>    <span style="color: #990073;line-height: 26px;">-</span> <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">v1</span><br/>      <span style="line-height: 26px;">served:</span> <span style="color: #008080;line-height: 26px;">true</span><br/>      <span style="line-height: 26px;">storage:</span> <span style="color: #008080;line-height: 26px;">true</span><br/>      <span style="line-height: 26px;">schema:</span><br/>        <span style="line-height: 26px;">openAPIV3Schema:</span><br/>          <span style="line-height: 26px;">type:</span> <span style="color: #d14;line-height: 26px;">object</span><br/>          <span style="line-height: 26px;">properties:</span><br/>            <span style="line-height: 26px;">spec:</span><br/>              <span style="line-height: 26px;">type:</span> <span style="color: #d14;line-height: 26px;">object</span><br/>              <span style="line-height: 26px;">properties:</span><br/>                <span style="line-height: 26px;">cronSpec:</span><br/>                  <span style="line-height: 26px;">type:</span> <span style="color: #d14;line-height: 26px;">string</span><br/>                <span style="line-height: 26px;">image:</span><br/>                  <span style="line-height: 26px;">type:</span> <span style="color: #d14;line-height: 26px;">string</span><br/>                <span style="line-height: 26px;">replicas:</span><br/>                  <span style="line-height: 26px;">type:</span> <span style="color: #d14;line-height: 26px;">integer</span><br/>  <span style="line-height: 26px;">scope:</span> <span style="color: #d14;line-height: 26px;">Namespaced</span><br/>  <span style="line-height: 26px;">names:</span><br/>    <span style="line-height: 26px;">plural:</span> <span style="color: #d14;line-height: 26px;">crontabs</span><br/>    <span style="line-height: 26px;">singular:</span> <span style="color: #d14;line-height: 26px;">crontab</span><br/>    <span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">CronTab</span><br/>    <span style="line-height: 26px;">shortNames:</span><br/>    <span style="color: #990073;line-height: 26px;">-</span> <span style="color: #d14;line-height: 26px;">ct</span><br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了这些系统内置的 API 之外，想要实现定制的接口就需要使用 CRD，然而 CRD 仅仅是实现自定义资源的冰山一角，因为它只定义了资源中的字段，我们还需要遵循 Kubernetes 的控制器模式，实现消费 CRD 的 Operator，通过组合 Kubernetes 提供的资源实现更复杂、更高级的功能。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.35833333333333334" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=77a90b33&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcDBJ8ZffW4nMSr4gTnkGsSemv6MFJs6Wcx7pe7JpCDCJlVl80rpiaZMg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - Kubernetes API 模块化设计</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如上图所示，Kubernetes 中的控制器等组件会消费 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Deployment</code>、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">StatefulSet</code> 等资源，而用户自定义的 CRD 会由自己实现的控制器消费，这种设计极大地降低了系统之间各个模块的耦合，让不同模块可以无缝协作。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当我们想要让 Kubernetes 集群提供更加复杂的功能时，选择 CRD 和控制器是首先需要考虑的方法，这种方式与现有的功能耦合性非常低，同时也具有较强的灵活性，但是在定义接口时应该遵循社区 API 的最佳实践设计出优雅的接口[^2]。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>聚合层<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes API 聚合层是 v1.7 版本实现的功能，它的目的是将单体的 API 服务器拆分成多个聚合服务，每个开发者都能够实现聚合 API 服务暴露它们需要的接口，这个过程不需要重新编译 Kubernetes 的任何代码[^3]。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.2833333333333333" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=c49a5c99&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOct7xPAUAoqZyI9jNv9mbRN6r8JjicNicTQuRoonahzRdJzriaWk80Nrdjg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - Kubernetes API 聚合</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当我们需要在集群中加入新的 API 聚合服务时，需要提交一个 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">APIService</code> 资源，这个资源描述了接口所属的组、版本号以及处理该接口的服务，下面是 Kubernetes 社区中 metrics-server 服务对应的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">APIService</code>：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bobJaQXCyPCToSCw8kZicKOcYEtm9ZdlUOJ0oxTRDF88OOjqvpCiaFHibBES6t6ic2hH2omaNYicJ82jNw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="line-height: 26px;">apiVersion:</span> <span style="color: #d14;line-height: 26px;">apiregistration.k8s.io/v1</span><br/><span style="line-height: 26px;">kind:</span> <span style="color: #d14;line-height: 26px;">APIService</span><br/><span style="line-height: 26px;">metadata:</span><br/>  <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">v1beta1.metrics.k8s.io</span><br/><span style="line-height: 26px;">spec:</span><br/>  <span style="line-height: 26px;">service:</span><br/>    <span style="line-height: 26px;">name:</span> <span style="color: #d14;line-height: 26px;">metrics-server</span><br/>    <span style="line-height: 26px;">namespace:</span> <span style="color: #d14;line-height: 26px;">kube-system</span><br/>  <span style="line-height: 26px;">group:</span> <span style="color: #d14;line-height: 26px;">metrics.k8s.io</span><br/>  <span style="line-height: 26px;">version:</span> <span style="color: #d14;line-height: 26px;">v1beta1</span><br/>  <span style="line-height: 26px;">insecureSkipTLSVerify:</span> <span style="color: #008080;line-height: 26px;">true</span><br/>  <span style="line-height: 26px;">groupPriorityMinimum:</span> <span style="color: #008080;line-height: 26px;">100</span><br/>  <span style="line-height: 26px;">versionPriority:</span> <span style="color: #008080;line-height: 26px;">100</span><br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如果我们将上述资源提交到 Kubernetes 集群中后，用户在访问 API 服务器的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">/apis/metrics.k8s.io/v1beta1</code> 路径时，会被转发到集群中的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">metrics-server.kube-system.svc</code> 服务上。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">与应用范围很广的 CRD 相比，API 聚合机制在项目中比较少见，它的主要目的还是扩展 API 服务器，而大多数的集群都不会有类似的需求，在这里也就不过多介绍了。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>准入控制<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 的准入控制机制可以修改和验证即将被 API 服务器持久化的资源，API 服务器收到的全部写请求都会经过如下所示的阶段持久化到 etcd 中[^4]：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.275" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=c656492c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcxkta0x1sPO0eEn0jhskvbmnwjuMhmp3wU8e3CR0DZ1XF5Ua9vzUxeA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - Kubernetes 准入控制</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 的代码仓库中包含 20 多个准入控制插件[^5]，我们以 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">TaintNodesByCondition</code> 插件[^6]为例简单介绍一下它们的实现原理：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bobJaQXCyPCToSCw8kZicKOcYEtm9ZdlUOJ0oxTRDF88OOjqvpCiaFHibBES6t6ic2hH2omaNYicJ82jNw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">func</span> <span style="line-height: 26px;">(p *Plugin)</span> <span style="color: #900;font-weight: bold;line-height: 26px;">Admit</span><span style="line-height: 26px;">(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces)</span> <span style="color: #900;font-weight: bold;line-height: 26px;">error</span></span> {<br/> <span style="font-weight: bold;line-height: 26px;">if</span> a.GetResource().GroupResource() != nodeResource || a.GetSubresource() != <span style="color: #d14;line-height: 26px;">&#34;&#34;</span> {<br/>  <span style="font-weight: bold;line-height: 26px;">return</span> <span style="color: #008080;line-height: 26px;">nil</span><br/> }<br/> node, ok := a.GetObject().(*api.Node)<br/> <span style="font-weight: bold;line-height: 26px;">if</span> !ok {<br/>  <span style="font-weight: bold;line-height: 26px;">return</span> admission.NewForbidden(a, fmt.Errorf(<span style="color: #d14;line-height: 26px;">&#34;unexpected type %T&#34;</span>, a.GetObject()))<br/> }<br/> addNotReadyTaint(node)<br/> <span style="font-weight: bold;line-height: 26px;">return</span> <span style="color: #008080;line-height: 26px;">nil</span><br/>}<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">所有的准入控制插件都可以实现上述的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Admit</code> 方法修改即将提交到存储中的资源，也就是上面提到的 Mutating 修改阶段，这段代码会为所有传入节点加上 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">NotReady</code> 污点保证节点在更新期间不会有任务调度到该节点上；除了 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Admit</code> 方法之外，插件还可以实现 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Validate</code> 方法验证传入资源的合法性。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在 Kubernetes 实现自定义的准入控制器相对比较复杂，我们需要构建一个实现准入控制接口的 API 服务并将该 API 服务通过 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">MutatingWebhookConfiguration</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">ValidatingWebhookConfiguration</code> 两种资源将服务的地址和接口注册到集群中，而 Kubernetes 的 API 服务器会在修改资源时调用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">WebhookConfiguration</code> 中定义的服务修改和验证资源。Kubernetes 社区中的比较热门的服务网格 Istio 就利用该特性实现了一些功能[^7]。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>容器接口</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 作为容器编排系统，它的主要逻辑还是调度和管理集群中运行的容器，虽然它不需要从零开始实现新的容器运行时，但是因为网络和存储等模块是容器运行的必需品，所以它要与这些模块打交道。Kubernetes 选择的方式是设计网络、存储和运行时接口隔离实现细节，自己把精力放在容器编排上，让第三方社区实现这些复杂而且极具专业性的模块。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>网络插件<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">容器网络接口（Container Network Interface、CNI）包含一组用于开发插件去配置 Linux 容器中网卡的接口和框架。CNI 仅会关注容器的网络连通性并在容器删除时回收所有分配的网络资源[^8]。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.41025641025641024" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="351" src="https://wechat2rss.xlab.app/img-proxy/?k=0346442e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcTzkLEWlOGticpf2qm7CtRbdV0K7Rt7Mt5v6rafPNfrkWdGrHDY9d2Rw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 5 - 容器网络接口</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">CNI 插件虽然与 Kubernetes 有密切的关系，但是不同的容器管理系统都可以使用 CNI 插件来创建和管理网络，例如：mesos、Cloud Foundry 等。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">所有的 CNI 插件都应该实现包含 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">ADD</code>、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">DEL</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">CHECK</code> 操作的二进制可执行文件，容器管理系统会执行二进制文件来创建网络[^9]。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在 Kubernetes 中，无论使用哪种网络插件都需要遵循它的网络模型，除了每个 Pod 都需要有独立的 IP 地址之外，Kubernetes 还对网络模型做出了以下的需求：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">任意节点上的 Pod 在不使用 NAT 的情况下都访问到所有节点上的所有 Pod；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">节点上的 Kubelet 和守护进程等服务可以访问节点上的其他 Pod；</section></li></ul><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bobJaQXCyPCToSCw8kZicKOcYEtm9ZdlUOJ0oxTRDF88OOjqvpCiaFHibBES6t6ic2hH2omaNYicJ82jNw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="font-weight: bold;line-height: 26px;">type</span> CNI <span style="font-weight: bold;line-height: 26px;">interface</span> {<br/> AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)<br/> CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error<br/> DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error<br/> GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)<br/> GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]<span style="font-weight: bold;line-height: 26px;">byte</span>, *RuntimeConf, error)<br/> AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)<br/> CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error<br/> DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error<br/> GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)<br/> GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]<span style="font-weight: bold;line-height: 26px;">byte</span>, *RuntimeConf, error)<br/> ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]<span style="font-weight: bold;line-height: 26px;">string</span>, error)<br/> ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]<span style="font-weight: bold;line-height: 26px;">string</span>, error)<br/>}<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">开发 CNI 插件对于多数工程师来说都非常遥远，在正常情况下，我们只需要在一些常见的开源框架中根据需求做出选择，例如：Flannel、Calico 和 Cilium 等，当集群的规模变得非常庞大时，也自然会有网络工程师与 Kubernetes 开发者配合开发相应的插件。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>存储插件<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">容器存储接口（Container Storage Interface、CSI）是 Kubernetes 在 v1.9 引入的新特性，该特性在 v1.13 中达到稳定，目前常见的容器编排系统 Kubernetes、Cloud Foundry、Mesos 和 Nomad 都选择使用该接口扩展集群中容器的存储能力。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5206286836935167" style="display: block;margin-right: auto;margin-left: auto;" data-type="other" data-w="2545" src="https://wechat2rss.xlab.app/img-proxy/?k=f2ce1efd&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcXiaSHcHCyibKCKTyibBPJJvbo2PNLia16DHPVakuxuF05rBkuxlStSHfBQ%2F640%3Fwx_fmt%3Dother"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 6 - 容器存储接口</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">CSI 是在容器编排系统向容器化的工作负载暴露块存储和文件存储的标准，第三方的存储提供商可以通过实现 CSI 插件在 Kubernetes 集群中提供新的存储[^11]。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 的开发团队在 CSI 的文档中给出了开发和部署 CSI 插件的最佳实践[^12]，其中最主要的工作是创建实现 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Identity</code>、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Node</code> 和可选的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Controller</code> 接口的容器化应用，并通过官方的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">sanity</code> 包测试 CSI 插件的合法性，需要实现的接口都定义在 CSI 的规格文档中[^13]。</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bobJaQXCyPCToSCw8kZicKOcYEtm9ZdlUOJ0oxTRDF88OOjqvpCiaFHibBES6t6ic2hH2omaNYicJ82jNw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;">service Identity {<br/>  rpc GetPluginInfo(GetPluginInfoRequest)<br/>    returns (GetPluginInfoResponse) {}<br/>  rpc GetPluginCapabilities(GetPluginCapabilitiesRequest)<br/>    returns (GetPluginCapabilitiesResponse) {}<br/>  rpc Probe (ProbeRequest)<br/>    returns (ProbeResponse) {}<br/>}<br/>service Controller {<br/>  ...<br/>}<br/>service Node {<br/>  ...<br/>}<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">CSI 的规格文档非常复杂，除了详细地定义了不同接口的请求和响应参数。它还定义不同接口在出现相应错误时应该返回的 gRPC 错误码，开发者想要实现一个完全遵循 CSI 接口的插件还是很麻烦的。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 在较早的版本中分别接入了不同的云厂商的接口，其中包括 Google PD、AWS、Azure 以及 OpenStack，但是随着 CSI 接口的成熟，社区未来会在上游移除云厂商特定的实现，减少上游的维护成本，也能加快各个厂商自身存储的迭代和支持[^14]。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>运行时接口<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">容器运行时接口（Container Runtime Interface、CRI）是一系列用于管理容器运行时和镜像的 gRPC 接口，它是 Kubernetes 在 v1.5 中引入的新接口，Kubelet 可以通过它使用不同的容器运行时。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.30833333333333335" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=ba37a7bc&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOchx1ra4QcjS7Qog4MYHhiatrKPVjWC7PFbs9MPmXBynYnPAXWKslsSSQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 7 - CRI 和容器运行时</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">CRI 主要定义的是一组 gRPC 方法，我们能在规格文档中找到 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">RuntimeService</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">ImageService</code> 两个服务[^15]，它们的名字很好地解释了各自的作用：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bobJaQXCyPCToSCw8kZicKOcYEtm9ZdlUOJ0oxTRDF88OOjqvpCiaFHibBES6t6ic2hH2omaNYicJ82jNw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;">service RuntimeService {<br/>    rpc Version(VersionRequest) returns (VersionResponse) {}<br/>    rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {}<br/>    rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {}<br/>    rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {}<br/>    rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {}<br/>    rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {}<br/>    rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}<br/>    rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {}<br/>    rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {}<br/>    rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {}<br/>    rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {}<br/>    rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {}<br/>    rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {}<br/>    rpc ReopenContainerLog(ReopenContainerLogRequest) returns (ReopenContainerLogResponse) {}<br/>    rpc ExecSync(ExecSyncRequest) returns (ExecSyncResponse) {}<br/>    rpc Exec(ExecRequest) returns (ExecResponse) {}<br/>    rpc Attach(AttachRequest) returns (AttachResponse) {}<br/>    rpc PortForward(PortForwardRequest) returns (PortForwardResponse) {}<br/>    ...<br/>}<br/>service ImageService {<br/>    rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}<br/>    rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {}<br/>    rpc PullImage(PullImageRequest) returns (PullImageResponse) {}<br/>    rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {}<br/>    rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {}<br/>}<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">容器运行时的接口相对比较简单，上面的这些接口不仅暴露了 Pod 沙箱管理、容器管理以及命令执行和端口转发等功能，还包含用于管理镜像的多个接口，容器运行时只要实现上面的二三十个方法可以为 Kubelet 提供服务。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>设备插件</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">CPU、内存、磁盘是主机上常见的资源，然而随着大数据、机器学习和硬件的发展，部分场景可能需要异构的计算资源，例如：GPU、FPGA 等设备。异构资源的出现不仅需要节点代理 Kubelet 的支持，还需要调度器的配合，为了良好的兼容后出现的不同计算设备，Kubernetes 社区在上游引入了设备插件（Device Plugin）用于支持多种类型资源的调度和分配[^16]。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.7399770904925544" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="873" src="https://wechat2rss.xlab.app/img-proxy/?k=c4959139&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcCAZicOM70Ox7tQibdCQwNchuzsmc11BbOqHDpzJWH4L1icVw5XxWjFktg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 8 - 设备插件概述</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">设备插件是独立在 Kubelet 之外单独运行的服务，它通过 Kubelet 暴露的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Registration</code> 服务注册自己的相关信息并实现 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">DevicePlugin</code> 服务用于订阅和分配自定义的设备[^17]。</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bobJaQXCyPCToSCw8kZicKOcYEtm9ZdlUOJ0oxTRDF88OOjqvpCiaFHibBES6t6ic2hH2omaNYicJ82jNw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;">service Registration {<br/>	rpc Register(RegisterRequest) returns (Empty) {}<br/>}<br/>service DevicePlugin {<br/>      rpc GetDevicePluginOptions(Empty) returns (DevicePluginOptions) {}<br/>      rpc ListAndWatch(Empty) returns (stream ListAndWatchResponse) {}<br/>      rpc Allocate(AllocateRequest) returns (AllocateResponse) {}<br/>      rpc GetPreferredAllocation(PreferredAllocationRequest) returns (PreferredAllocationResponse) {}<br/>      rpc PreStartContainer(PreStartContainerRequest) returns (PreStartContainerResponse) {}<br/>}<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当设备插件刚刚启动时，它会调用 Kubelet 的注册接口传入自己的版本号、Unix 套接字和资源名，例如：<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">nvidia.com/gpu</code>；Kubelet 会通过 Unix 套接字与设备插件通信，它会通过 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">ListAndWatch</code> 接口持续获得设备中资源的最新状态，并在 Pod 申请资源时通过 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">Allocate</code> 接口分配资源。设备插件的实现逻辑相对比较简单，感兴趣的读者可以研究 Nvidia GPU 插件的实现原理[^18]。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>调度框架</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">调度器是 Kubernetes 中的核心组件之一，它的主要作用是在 Kubernetes 集群中的一组节点中为工作负载做出最优的调度决策，不同场景下的调度需求往往都是很复杂的，然而调度器在 Kubernetes 项目早期并不支持易用的扩展能力，仅支持调度器扩展（Extender）这种比较难用的方法。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 从 v1.15 引入的调度框架才是今天比较主流的调度器扩展技术，通过在 Kubernetes 调度器的内部抽象出关键的扩展点（Extension Point）并通过插件的方式在扩展点上改变调度器做出的调度决策[^19]。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.44875549048316254" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1366" src="https://wechat2rss.xlab.app/img-proxy/?k=4cbea37d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bobJaQXCyPCToSCw8kZicKOcEj8E2W1wqEpzGECYjiaZz1HibVvrIEd64RQU5oTgCul1iaxjqtrHAPbLQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 9 - 调度框架扩展点</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">目前的调度框架总共支持 11 个不同的扩展点，每个扩展点都对应 Kubernetes 调度器中定义的接口，这里仅展示 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">FilterPlugin</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">ScorePlugin</code> 两个常见接口中的方法[^20]：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bobJaQXCyPCToSCw8kZicKOcYEtm9ZdlUOJ0oxTRDF88OOjqvpCiaFHibBES6t6ic2hH2omaNYicJ82jNw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="font-weight: bold;line-height: 26px;">type</span> FilterPlugin <span style="font-weight: bold;line-height: 26px;">interface</span> {<br/> Plugin<br/> Filter(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *NodeInfo) *Status<br/>}<br/><span style="font-weight: bold;line-height: 26px;">type</span> ScoreExtensions <span style="font-weight: bold;line-height: 26px;">interface</span> {<br/> NormalizeScore(ctx context.Context, state *CycleState, p *v1.Pod, scores NodeScoreList) *Status<br/>}<br/><span style="font-weight: bold;line-height: 26px;">type</span> ScorePlugin <span style="font-weight: bold;line-height: 26px;">interface</span> {<br/> Plugin<br/> Score(ctx context.Context, state *CycleState, p *v1.Pod, nodeName <span style="font-weight: bold;line-height: 26px;">string</span>) (<span style="font-weight: bold;line-height: 26px;">int64</span>, *Status)<br/> ScoreExtensions() ScoreExtensions<br/>}<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">调度框架的出现让实现复杂的调度策略和调度算法变得更加容易，社区通过调度框架替代更早的谓词和优先级并实现了协作式调度、基于容量调度等功能更强大的插件[^21]。虽然今天的调度框架已经变得非常灵活，但是串行的调度器可能无法满足大集群的调度需求，而 Kubernetes 目前也很难实现多调度器，不知道未来是否会提供更灵活的接口。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 从 2014 年发布至今已经过去将近 7 年了，从一个最小可用的编排系统到今天的庞然大物，社区的每个代码贡献者和成员都有<span style="text-decoration:line-through;">责任</span>。从这篇文章中，我们可以看到随着 Kubernetes 项目的演进方向，社区越来越关注系统的可扩展性，通过设计接口、移除第三方代码降低社区成员的负担，让 Kubernetes 能够更专注于容器的编排和调度。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: Custom Resources · Kubernetes <a href="https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/" target="_blank">https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^2]: API Conventions · Kubernetes Community <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md" target="_blank">https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^3]: Aggregated API Servers · Kubernetes Community <a href="https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/aggregated-api-servers.md" target="_blank">https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/aggregated-api-servers.md</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^4]: Using Admission Controllers <a href="https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/" target="_blank">https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^5]: kubernetes/plugin/pkg/admission/ · Kubernetes <a href="https://github.com/kubernetes/kubernetes/tree/master/plugin/pkg/admission" target="_blank">https://github.com/kubernetes/kubernetes/tree/master/plugin/pkg/admission</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^6]: kubernetes/plugin/pkg/admission/nodetaint/admission.go · Kubernetes <a href="https://github.com/kubernetes/kubernetes/blob/master/plugin/pkg/admission/nodetaint/admission.go" target="_blank">https://github.com/kubernetes/kubernetes/blob/master/plugin/pkg/admission/nodetaint/admission.go</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^7]: Dynamic Admission Webhooks Overview <a href="https://istio.io/latest/docs/ops/configuration/mesh/webhook/" target="_blank">https://istio.io/latest/docs/ops/configuration/mesh/webhook/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^8]: CNI - the Container Network Interface cni <a href="https://github.com/containernetworking/cni" target="_blank">https://github.com/containernetworking/cni</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^9]: Container Network Interface Specification <a href="https://github.com/containernetworking/cni/blob/master/SPEC.md" target="_blank">https://github.com/containernetworking/cni/blob/master/SPEC.md</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^10]: cni/libcni/api.go · containernetworking/cni <a href="https://github.com/containernetworking/cni/blob/master/libcni/api.go" target="_blank">https://github.com/containernetworking/cni/blob/master/libcni/api.go</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^11]: Kubernetes Container Storage Interface (CSI) Documentation <a href="https://kubernetes-csi.github.io/docs/#kubernetes-container-storage-interface-csi-documentation" target="_blank">https://kubernetes-csi.github.io/docs/#kubernetes-container-storage-interface-csi-documentation</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^12]: Recommended Mechanism (for Developing and Deploying a CSI driver for Kubernetes) <a href="https://kubernetes-csi.github.io/docs/#recommended-mechanism-for-developing-and-deploying-a-csi-driver-for-kubernetes" target="_blank">https://kubernetes-csi.github.io/docs/#recommended-mechanism-for-developing-and-deploying-a-csi-driver-for-kubernetes</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^13]: RPC Interface · Container Storage Interface (CSI) <a href="https://github.com/container-storage-interface/spec/blob/master/spec.md#rpc-interface" target="_blank">https://github.com/container-storage-interface/spec/blob/master/spec.md#rpc-interface</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^14]: In-tree Storage Plugin to CSI Migration Design Doc <a href="https://github.com/kubernetes/community/blob/master/contributors/design-proposals/storage/csi-migration.md" target="_blank">https://github.com/kubernetes/community/blob/master/contributors/design-proposals/storage/csi-migration.md</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^15]: Container Runtime Interface (CRI) – a plugin interface which enables kubelet to use a wide variety of container runtimes. <a href="https://github.com/kubernetes/cri-api/blob/master/pkg/apis/runtime/v1/api.proto" target="_blank">https://github.com/kubernetes/cri-api/blob/master/pkg/apis/runtime/v1/api.proto</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^16]: Device Plugins <a href="https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/" target="_blank">https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/device-plugins/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^17]: API Specification · Device Manager Proposal <a href="https://github.com/kubernetes/community/blob/master/contributors/design-proposals/resource-management/device-plugin.md#api-specification" target="_blank">https://github.com/kubernetes/community/blob/master/contributors/design-proposals/resource-management/device-plugin.md#api-specification</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^18]: k8s-device-plugin <a href="https://github.com/NVIDIA/k8s-device-plugin" target="_blank">https://github.com/NVIDIA/k8s-device-plugin</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^19]: Scheduling Framework <a href="https://github.com/kubernetes/enhancements/tree/master/keps/sig-scheduling/624-scheduling-framework" target="_blank">https://github.com/kubernetes/enhancements/tree/master/keps/sig-scheduling/624-scheduling-framework</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^20]: kubernetes/pkg/scheduler/framework/interface.go <a href="https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/framework/interface.go" target="_blank">https://github.com/kubernetes/kubernetes/blob/master/pkg/scheduler/framework/interface.go</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^21]: Repository for out-of-tree scheduler plugins based on scheduler framework. <a href="https://github.com/kubernetes-sigs/scheduler-plugins" target="_blank">https://github.com/kubernetes-sigs/scheduler-plugins</a></p></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"/></p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p>



<p><a href="https://draveness.me/whys-the-design-kubernetes-deprecate-docker/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=b5f60cd2&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484872%26idx%3D1%26sn%3D9b2cf32f7e6de939236d25cfd05ac0de%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 30 Mar 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>Google 数据中心的电力超售 · OSDI &#39;20</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484827&amp;idx=1&amp;sn=42b136a12bb3669de01510a7196e651a</link>
      <description>Google 如何通过电力超售在数据中心部署更多的机器</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-03-25 08:33</span> <span style="display: inline-block;"></span>
</p>

<p>Google 如何通过电力超售在数据中心部署更多的机器</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=a9dc3042&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD3TyEvrTWdRzULA69jWllEUxzKtFmvLZrh8ib9eDI03DSWt9dvQmYVUvQ%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">『看看论文』是一系列分析计算机和软件工程领域论文的文章，我们在这个系列的每一篇文章中都会阅读一篇来自 OSDI、SOSP 等顶会中的论文，这里不会事无巨细地介绍所有的细节，而是会筛选论文中的关键内容，如果你对相关的论文非常感兴趣，可以直接点击链接阅读原文。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">本文要介绍的是 2020 年 OSDI 期刊中的论文 —— Thunderbolt: Throughput-Optimized, Quality-of-Service-Aware Power Capping at Scale[^1]，该论文实现的 Thunderbolt 可以在数据中心实现电力资源的超售，电子资源的超售可以使同一个数据中心运行更多的服务器，从而提高数据中心的整体性能并减少日常的维护开销、降低成本。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">超售系统的目的都是降低成本并提高利用率，但是也都面临着资源过度使用的潜在可能，这也就需要引入限制和保护系统在影响系统之前或者之时及时止损，正如航空公司会超售机票一样，一旦超售的航班全满，就会采用升舱、换飞机或者赔偿等方式解决问题，避免事态的进一步恶化。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.325" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=9097433c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD3sRJATyF3synbsafbGMSvBJm6kHr3U1nokhVJcc77vMvDhEuQqwvQYA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - 资源超售</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">现代的计算集群都会同时运行延时敏感型的在线服务和面向吞吐量的批处理工作负载，在运行期间一旦发生突发事件，我们会通过 Linux 内核的 CPU 限制等机制优先保证前者的延时，并在没有突发情况的机器上使用后者提高集群的资源利用率。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">延迟敏感型的在线服务往往可以容忍宕机，但是它们需要保证程序执行期间有稳定的性能和资源，这些服务在遇到性能问题时更倾向于调度到其他资源充足的机器上；而面向吞吐量的批处理任务其实可以容忍资源的不稳定，但是一旦调度到其他机器上，可能需要重新计算，这就浪费了大量的计算和网络资源。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.30833333333333335" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=ec4a65d1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD3LuTg40PUKzJia8ZUSCxFvPO2aKPFeaEn9zzQ6ouyMdzft5VsUxonpNQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - 电力超售和混合部署</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">从调度的角度上来看，论文中 Thunderbolt 要解决的问题和混合部署、计算资源超售的场景非常相似，它们两者都希望保证集群中的两种类型服务的延迟和吞吐量，只是 Thunderbolt 会将电力资源作为约束条件，而后者的目的是更高的资源利用率，这也是作者对这篇论文感兴趣的原因。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Thunderbolt 使用如下所示的架构设计，图中的四个蓝色方块就是该系统的几个核心组件，我们能从图中清晰地看到该系统的数据流向：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.5080645161290323" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1116" src="https://wechat2rss.xlab.app/img-proxy/?k=40e9dd7e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD3zI93WS7Oa9nU7Hf31d4HdWbFM2z5icPrumQuGoGzUZNaw9vYFqZunPw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - Thunderbolt 架构</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上述架构中包含两个不同的电力限制路径，分别是根据实时数据决策的反应式限制（Reactive Capping）和被称为故障转移机制的主动式限制（Proactive Capping），这两种限制策略会在不同情况下触发。</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">反应式限制：会监控电表发出的实时电力信号，当集群占用的电力功率高于特定数值时就会限制 CPU 的使用；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">主动式限制：当电力信号变得不可用时，主动式限制会接管整个系统，根据历史的数据评估机房断路器切断电源的风险，如果评估的风险过高，也会限制集群的 CPU 使用；</section></li></ul><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>反应式限制<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为 CPU 的利用率能够很好地指示当前任务使用的 CPU 功率，所以我们可以通过限制 CPU 来降低整个集群中服务器消耗的电力。Thunderbolt 使用 Linux 内核的完全公平调度器（Completely Fair Scheduler）精确的控制节点中进程的 CPU 占用，以此来控制节点占用的资源：</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">独立的任务会运行在它们自己的 Linux 控制组（Cgroups）中，调度器提供了两个用于控制 CPU 使用量的参数，即 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">quota</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">period</code>，其中 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">quota</code> 是当前工作负载在每个 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">period</code> 能够得到的执行时间，它们的单位都是微秒。如上图所示，当 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">quota</code> 为 20,000、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">period</code> 为 100,000 时，当前控制组中的任务在每 100ms 的时间周期中都会获得 20ms 的执行时间。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">同一个节点上的不同任务会分别属于不同的控制组，我们可以通过调节它们的参数为不同的任务设置不同的服务质量。而 Thunderbolt 会利用上面这点将延迟敏感型的任务设置成高优先级，将面向吞吐量的的任务设置成低优先级，在电力资源不足时限制后者的 CPU 使用。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.6490438695163104" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="889" src="https://wechat2rss.xlab.app/img-proxy/?k=eb97c7f7&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD3ZuszCmbibSNibCQdqlbRLucrRUhjic9EicgibmmWrsHRyDjjiaIJuG6p3rgQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - 流量塑形</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然我们会使用 CFS 限制工作负载的 CPU 使用，但是仍然需要上面的流量塑形控制策略决定应该何时限制以及如何限制 CPU 的使用量来控制集群的电力用量。在上述折线图中，包含两个不同的限制区域：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">硬限制区域：当节点的电力用量进入硬限制区域时，会将 CPU 的使用量减少 99% 防止过度使用资源，同时会立刻创建一段软限制区域；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">软限制区域：当节点的电力用量进入软限制区域时，会将 CPU 的使用量在一定时间内减少 25%，随后电力使用会在软限制区域区域下来回震荡，直到走出软限制区域；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">软限制区域仅会在机器的能源使用进入硬限制区域时才会触发，同时会在一段时间后消失，减少对当前节点的电力资源的使用限制。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>主动式限制<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">主动式的限制策略更像是一种故障转移的保护策略，当集群中的电表变得无法工作时，我们仍然需要保证集群的稳定。但是因为实时电力资源的缺失，我们没有办法使用上面的反应式策略，系统需要根据历史数据和服务质量决定每个任务分配的 CPU 资源。在这种策略下，使用 Linux 的 CFS 为工作负载动态分配资源是很难的，我们会直接将逻辑 CPU 从任务的 CPU 亲和（Affinity）中移除以减少资源的消耗。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">与反应式策略仅会限制面向吞吐量的工作负载不同，当电表不能提供实时可用的数据时，CPU 的禁用会应用到集群中的全部的负载上，在这时我们只能牺牲在线服务的性能保证整个集群的电力安全，避免断电带来的大型事故。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作为普通的软件工程师，在看这篇论文之前从来没有考虑过电力供应对数据中心带来的影响，也没有想过可以通过超售电力资源降低数据中心的成本。但是虽然我们没有遇到过这个问题，但是我们也可以从类似的问题中寻找答案，例如：集群中的计算资源超售和混合部署与论文中的场景有相似的解决方案。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这篇论文其实从不一样的角度开阔了我们的视野，让我们能从集群之外的物理世界考虑如何降低成本并提高能效，试想除了电力资源、计算资源的超售，我们还可以超售哪些资源呢，相信从这个角度出发，可以套用相似的逻辑解决不同的问题。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>推荐阅读</h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484502&amp;idx=1&amp;sn=8e4e2dd48c9ca18bc8eb4941d886190d&amp;chksm=fe795b5dc90ed24b977950948b23f8684bd349c1dfa3396df61885de902a4dc60db3050ece9f&amp;scene=21#wechat_redirect" textvalue="数据布局服务与局部性管理 · OSDI &#39;18" data-itemshowtype="0" tab="innerlink" data-linktype="2">数据布局服务与局部性管理 · OSDI &#39;18</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484620&amp;idx=1&amp;sn=323c44f5d13b722102f36c0bcca79abb&amp;chksm=fe795bc7c90ed2d153e79a2897cf92f84d18d4d355ec775c4acd29b3d2a2a7f6a27edfe3c938&amp;scene=21#wechat_redirect" textvalue="流量管理与数据中心故障缓解 · OSDI &#39;18" data-itemshowtype="0" tab="innerlink" data-linktype="2">流量管理与数据中心故障缓解 · OSDI &#39;18</a></section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: Li S, Wang X, Zhang X, et al. Thunderbolt: Throughput-Optimized, Quality-of-Service-Aware Power Capping at Scale[C]//14th {USENIX} Symposium on Operating Systems Design and Implementation ({OSDI} 20). 2020: 1241-1255.</p></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"/></p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p><p style="max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"/></p><p><br/></p>



<p><a href="https://draveness.me/papers-thunderbolt/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=47e37090&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484827%26idx%3D1%26sn%3D42b136a12bb3669de01510a7196e651a%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Thu, 25 Mar 2021 08:33:00 +0800</pubDate>
    </item>
    <item>
      <title>集群管理系统 Mesos 的设计原理 · NSDI &#39;11</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484818&amp;idx=1&amp;sn=de6d95f1a89f1c017d7201819cfe0a81</link>
      <description></description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-03-23 08:30</span> <span style="display: inline-block;"></span>
</p>

<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=9ff9032e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD3UDy31XGZ0CfOSh3MBfwwx5enIQ8SfZ4wmmK5pic7CnC3UL0fiaUEcevw%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">『看看论文』是一系列分析计算机和软件工程领域论文的文章，我们在这个系列的每一篇文章中都会阅读一篇来自 OSDI、SOSP 等顶会中的论文，这里不会事无巨细地介绍所有的细节，而是会筛选论文中的关键内容，如果你对相关的论文非常感兴趣，可以直接点击链接阅读原文。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">本文要介绍的是 2011 年 NSDI 期刊中的论文 —— Mesos: A Platform for Fine-Grained Resource Sharing in the Data Center[^1]，该论文实现的 Mesos 能够在集群中管理不同的计算框架，例如 Hadoop 和 MPI 等。虽然 Mesos 集群管理系统是 10 多年前发布的技术，今天已经逐渐被更主流、更通用的容器编排系统 Kubernetes 取代，但是它确实可以解决集群管理上的部分问题。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Apache Mesos 和 Kubernetes 都是优秀的开源框架，也都支持大规模的集群管理，但是它们两个管理的集群规模仍然差一个数量级，单个 Mesos 集群可以管理 50,000 节点，而 Kubernetes 集群却只能管理 5,000 节点，需要做很多优化和限制，才能达到相同的数量级。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.24166666666666667" src="https://wechat2rss.xlab.app/img-proxy/?k=d2e57734&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD3tB1SOx82A9x5qfLWdYVPII1fibLibuYDUFh3X4HDLJCdPw1F512j1Z9Q%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - Kubernetes 和 Mesos 集群</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然 Kubernetes 是今天集群管理的主流技术，但是 Mesos 在刚刚出现时也是很先进的集群管理系统，它想要取代的是当时更为常见的静态分片集群。静态分片集群虽然可以同时运行属于不同框架的工作负载（例如：Hadoop、MPI），但是因为框架的异构性，使用静态分片技术会将集群中的机器预先分配给不同的框架，再由这些框架分配和管理资源。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.25" src="https://wechat2rss.xlab.app/img-proxy/?k=e5fffcad&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD3oBsmFGXRZMzjl2GFW0yj5icn0FCBRFibfsUNQbMH5akfickq4sUCqOTcw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - 静态分片</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Mesos 在最初设计时并不会直接管理和调度开发者提交的工作负载，而是提供一组接口暴露集群的资源，并通过这组轻量级的接口同时对接 Hadoop、MPI 等框架。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>架构</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如下图所示的 Mesos 集群同时运行了 Hadoop 和 Mesos 两个框架，如果忽略图中与 Hadoop、MPI 框架的相关模块，我们会发现架构会变得非常简单，它仅由 Zookeeper 集群、Mesos 主节点和工作节点组成。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="856" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.6682242990654206" src="https://wechat2rss.xlab.app/img-proxy/?k=538f3699&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD3JwGF5uStHoKyMpicQRy2iauP3jen25tmwU1mDBDFDGohibFwQibVCrOPWQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - Mesos 架构图</strong><br/></figcaption></figure><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Zookeeper 集群提供了高可用的数据存储和选举等功能；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Mesos 主节点收集工作节点上报的数据并向框架的调度器提供资源；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Mesos 工作节点上报数据并通过框架的执行者在本地启动任务；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">每个 Mesos 集群中运行的框架都由调度器和执行者两部分组成，调度器会处理主节点提供的资源，与 Kubernetes 的调度器有着相同的作用，当调度器接受主节点提供的资源后，它会返回待运行任务的相关信息；而执行者会在工作节点上运行框架创建的任务。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Mesos 为了保证更好的可扩展性，它定义了一套能够满足资源共享的最小接口，将任务调度和执行的控制权都通过如下所示的接口交给框架，其本身仅保留较粗粒度的调度和资源管理功能。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1020" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.49019607843137253" src="https://wechat2rss.xlab.app/img-proxy/?k=fa857c12&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD35Gj7KP0T4cd3dZnbN1SEZy5KZvswwicGulVqicoLmwq0RSHt7J0uLh2Q%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - Mesos 接口</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为 Mesos 中的任务调度是分布式的过程，所以为了保证该过程的效率和可靠性，它引入了下面的这三种机制：</p><ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">节点过滤器：框架使用过滤器剔除集群中不满足自身调度条件的节点；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">资源主动分配：为了提高框架的调度速度，会将预先提供给框架的资源计入框架的总分配资源，直到框架完成调度，这能激励框架实现更快的调度器；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">资源撤回：如果框架在一段时间内没有处理主节点提供的资源，Mesos 会撤回资源并提供给其他框架；</section></li></ol><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了提供良好的扩展性和性能之外，作为集群调度管理系统，Mesos 也面临着隔离不同任务资源的问题。在 Mesos 刚刚发布时，容器技术还没有像今天这么普及，但是它也利用操作系统的容器隔离不同工作负载的影响[^2]，并利用可插拔式的隔离模块支持多种隔离机制。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>调度模型</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们在文章开篇就已经介绍过 Mesos 和 Kubernetes 能够管理的集群规模有数量级的差距，这里简单对比分析下两者在调度器上的差异，这能帮助我们理解 Kubernetes 调度器在设计时做出的决策，以及这些决策是如何影响它的可扩展性。</p><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">需要注意的是，提升系统可扩展性往往都是复杂的问题，而在 Kubernetes 这样庞大的系统中会显得更加复杂，Kubernetes 的调度器不是影响其可扩展性的唯一因素，想要提升单个集群的规模要从多个方面入手。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Mesos 的调度器选择了两层的调度设计，其中顶层调度器仅会根据底层框架调度器的需求<strong>粗粒度地过滤集群中的节点</strong>，而框架调度器会<strong>执行真正的任务调度</strong>，将任务绑定到相应的节点上。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.30833333333333335" src="https://wechat2rss.xlab.app/img-proxy/?k=c127e635&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD31M1CBIRqeIteXoPt74ZwskaNC74KIKmglFUZiaKwKYOuHwRJNJHmk8w%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 5 - 两层调度器</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这种两层的调度器设计看起来虽然很复杂，但是实际上它能够降低 Mesos 调度器的复杂度并提高了它的可扩展性：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">降低复杂度：顶层调度器不需要处理真正的调度过程，它仅通过资源提供（Resource Offer）机制将一组节点交给底层调度器控制；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">提高可扩展性：两层调度器设计可以更方便地接入新的框架调度器，兼容不同复杂的调度策略，不同框架调度器内部可以串行为任务选择节点，提高整体调度的吞吐量；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然 Mesos 通过两层调度器设计提供了很强的扩展性，但是它却不能为调度决策提供<strong>全局最优解</strong>。这是因为所有的调度决策都是在整个集群中的一部分节点中做出的，所有的调度决策都只是局部最优的，而这也是多调度器中的常见问题[^3]。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在调度系统中，想要实现更好的扩展性就一定面临着分片，分片必然导致调度器无法提供全局最优解并且显著地增加系统的复杂性。我们从 Linux、Go 语言等 CPU 调度器的演进可以观察到这点，最初的调度器大多数都是单线程的，为了提高调度器的性能，会使用多调度器并引入工作窃取机制处理多调度器中待调度任务队列的不平衡。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes v1.21 版本的内置调度器仍然是单线程的，它为了在全局 5,000 个节点中做出最优的调度决策，需要使用不同的插件遍历这 5,000 个节点并排序，而这也是影响其扩展性的重要原因之一。全局最优解听起来是非常美好的设计，但是在调度这种比较复杂的场景中，局部最优解往往也都可以满足需求，在业务上不需要保证该约束时，就可以通过多调度器来提升性能了。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当对比 Mesos 和静态分片集群的资源利用率时，我们会发现 Mesos 在 CPU 和内存的集群资源利用率上都明显高于使用静态分片的集群，而这个结果也不会造成太多的意外，因为动态的资源分配策略一般都能够提高集群的资源利用率。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1040" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.6653846153846154" src="https://wechat2rss.xlab.app/img-proxy/?k=cf048e6f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brwIF5PAVXq0E3oSyMNWrD3wLrtLjjnCfJtKRl9aj5NC7GBoicz0Zpmmuyb2bwIZibdc25wxaHg5ib7g%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 6 - Mesos 和静态集群的资源利用率对比</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">了解 Mesos 出现时解决的问题以及它的设计可以让我们更好地理解今天面临的挑战，Mesos 在刚刚出现时是非常新颖的技术，与同期的其他产品来讲确实提供了很强的灵活性，但是随着 Yarn、Kubernetes 等技术的出现，它的很多场景也都被新技术取代，而这也是技术发展的必然趋势。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>推荐阅读</h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484797&amp;idx=1&amp;sn=fd73cc9bd2e56742a5627432aa5a4166&amp;chksm=fe795a76c90ed36031fffd9c4c588d3a0993634478afca3f0c4f0f1d3b79a772a72e15653f2e&amp;scene=21#wechat_redirect" textvalue="Facebook 集群调度管理系统 · OSDI &#39;20" data-itemshowtype="0" tab="innerlink" data-linktype="2">Facebook 集群调度管理系统 · OSDI &#39;20</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484620&amp;idx=1&amp;sn=323c44f5d13b722102f36c0bcca79abb&amp;chksm=fe795bc7c90ed2d153e79a2897cf92f84d18d4d355ec775c4acd29b3d2a2a7f6a27edfe3c938&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">流量管理与数据中心故障缓解 · OSDI &#39;18</a><br/></section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: Benjamin Hindman, Andy Konwinski, Matei Zaharia, Ali Ghodsi, Anthony D. Joseph, Randy Katz, Scott Shenker, and Ion Stoica. 2011. Mesos: a platform for fine-grained resource sharing in the data center. In Proceedings of the 8th USENIX conference on Networked systems design and implementation (NSDI&#39;11). USENIX Association, USA, 295–308.</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^2]: What&#39;s LXC? <a href="https://linuxcontainers.org/lxc/introduction/" target="_blank">https://linuxcontainers.org/lxc/introduction/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^3]: 调度系统设计精要 <a href="https://draveness.me/system-design-scheduler/" target="_blank">https://draveness.me/system-design-scheduler/</a></p></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"/></p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p><p><br/></p>



<p><a href="https://draveness.me/papers-mesos/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=da855add&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484818%26idx%3D1%26sn%3Dde6d95f1a89f1c017d7201819cfe0a81%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 23 Mar 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>为什么 Kubernetes 要替换 Docker</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484807&amp;idx=1&amp;sn=db8619f1c0e9273842cdf3803f299eab</link>
      <description></description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-03-16 08:30</span> <span style="display: inline-block;"></span>
</p>

<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=1d457b67&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7brG5PAqvicfe0DGRia04ibMD77IyXnVKB8tPrmX80n552Su8RlymISuJVYEYYRH5fMyzoe4BMeHiaPBEQ%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">为什么这么设计（Why’s THE Design）是一系列关于计算机领域中程序设计决策的文章，我们在这个系列的每一篇文章中都会提出一个具体的问题并从不同的角度讨论这种设计的优缺点、对具体实现造成的影响。如果你有想要了解的问题，可以在文章下面留言。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 是今天容器编排领域的事实标准，而 Docker 从诞生之日到今天都在容器中扮演着举足轻重的地位，也都是 Kubernetes 中的默认容器引擎。然而在 2020 年 12 月，Kubernetes 社区决定着手移除仓库中 Dockershim 相关代码[^1]，这对于 Kubernetes 和 Docker 两个社区来说都意义重大。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.30833333333333335" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=a74cc801&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brG5PAqvicfe0DGRia04ibMD77lAyqYQ9cNmfhU0EhdImibErPESdVEhuTMMVxnUTV0aLJrZzybYLp7JQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">kubelet-and-containers</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 1 - Dockershim</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">相信大多数的开发者都听说过 Kubernetes 和 Docker，也知道我们可以使用 Kubernetes 管理 Docker 容器，但是可能没有听说过 Dockershim，即 Docker 垫片。如上图所示，Kubernetes 中的节点代理 Kubelet 为了访问 Docker 提供的服务需要先经过社区维护的  Dockershim，Dockershim 会将请求转发给管理容器的 Docker 服务。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">其实从上面的架构图中，我们就能猜测出 Kubernetes 社区从代码仓库移除 Dockershim 的原因：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Kubernetes 引入容器运行时接口（Container Runtime Interface、CRI）隔离不同容器运行时的实现机制，容器编排系统不应该依赖于某个具体的运行时实现；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Docker 没有支持也不打算支持 Kubernetes 的 CRI 接口，需要 Kubernetes 社区在仓库中维护 Dockershim；</section></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>可扩展性</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 通过引入新的容器运行时接口将容器管理与具体的运行时解耦，不再依赖于某个具体的运行时实现。很多开源项目在早期为了降低用户的使用成本，都会提供开箱即用的体验，而随着用户群体的扩大，为了满足更多定制化的需求、提供更强的可扩展性，会引入更多的接口。Kubernetes 通过下面的一系列接口为不同模块提供了扩展性：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.4" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=49191bac&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brG5PAqvicfe0DGRia04ibMD77eh8D0hjOAqE7TNrd5VdmxQTecJ2Oww8Bl65MzD3QWWJod19MWR14Eg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">kubernetes-extensions</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 2 - Kubernetes 接口和可扩展性</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 在较早期的版本中就引入了 CRD、CNI、CRI 和 CSI 等接口，只有用于扩展调度器的调度框架是 Kubernetes 中比较新的特性。我们在这里就不展开分析其他的接口和扩展了，简单介绍一下容器运行时接口。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 早在 1.3 就在代码仓库中同时支持了 rkt 和 Docker 两种运行时，但是这些代码为 Kubelet 组件的维护带来了很大的困难，不仅需要维护不同的运行时，接入新的运行时也很困难；容器运行时接口（Container Runtime Interface、CRI）是 Kubernetes 在 1.5 中引入的新接口，Kubelet 可以通过这个新接口使用各种各样的容器运行时。其实 CRI 的发布就意味着 Kubernetes 一定会将 Dockershim 的代码从仓库中移除。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">CRI 是一系列用于管理容器运行时和镜像的 gRPC 接口，我们能在它的定义中找到 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">RuntimeService</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">ImageService</code> 两个服务[^2]，它们的名字很好地解释了各自的作用：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7brG5PAqvicfe0DGRia04ibMD77rMdNibkohEjjduxRK66dpJ4RAAYuLmCPF3fIibIs4vByPFmPqZOy6dkg/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;">service RuntimeService {<br/>    rpc Version(VersionRequest) returns (VersionResponse) {}<br/>    rpc RunPodSandbox(RunPodSandboxRequest) returns (RunPodSandboxResponse) {}<br/>    rpc StopPodSandbox(StopPodSandboxRequest) returns (StopPodSandboxResponse) {}<br/>    rpc RemovePodSandbox(RemovePodSandboxRequest) returns (RemovePodSandboxResponse) {}<br/>    rpc PodSandboxStatus(PodSandboxStatusRequest) returns (PodSandboxStatusResponse) {}<br/>    rpc ListPodSandbox(ListPodSandboxRequest) returns (ListPodSandboxResponse) {}<br/>    rpc CreateContainer(CreateContainerRequest) returns (CreateContainerResponse) {}<br/>    rpc StartContainer(StartContainerRequest) returns (StartContainerResponse) {}<br/>    rpc StopContainer(StopContainerRequest) returns (StopContainerResponse) {}<br/>    rpc RemoveContainer(RemoveContainerRequest) returns (RemoveContainerResponse) {}<br/>    rpc ListContainers(ListContainersRequest) returns (ListContainersResponse) {}<br/>    rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {}<br/>    rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {}<br/>    rpc ReopenContainerLog(ReopenContainerLogRequest) returns (ReopenContainerLogResponse) {}<br/>    ...<br/>}<br/>service ImageService {<br/>    rpc ListImages(ListImagesRequest) returns (ListImagesResponse) {}<br/>    rpc ImageStatus(ImageStatusRequest) returns (ImageStatusResponse) {}<br/>    rpc PullImage(PullImageRequest) returns (PullImageResponse) {}<br/>    rpc RemoveImage(RemoveImageRequest) returns (RemoveImageResponse) {}<br/>    rpc ImageFsInfo(ImageFsInfoRequest) returns (ImageFsInfoResponse) {}<br/>}<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">对 Kubernetes 稍有了解的人都能从上面的定义中找到一些熟悉的方法，它们都是容器运行时需要暴露给 Kubelet 的接口。Kubernetes 将 CRI 垫片实现成 gRPC 服务器与 Kubelet 中的客户端通信，所有的请求都会被转发给容器运行时处理。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.30833333333333335" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=0c97fd0e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brG5PAqvicfe0DGRia04ibMD772ZMPCyfAuHeoz4CmfchIVlB1qTUaI3RLLqoNrmuevs7CbnmhnbB2Bw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">cri-and-container-runtimes</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 3 - Kubernetes 和 CRI</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 中的声明式接口非常常见，作为声明式接口的拥趸，CRI 没有使用声明式的接口是一件听起来『非常怪异』的事情[^3]。不过 Kubernetes 社区考虑过让容器运行时重用 Pod 资源，这样容器运行时可以实现不同的控制逻辑来管理容器，能够极大地简化 Kubelet 和容器运行时之间的接口，但是社区出于以下两点考虑，最终没有选择声明式的接口：</p><ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">所有的运行时都需要重新实现相同的逻辑支持很多 Pod 级别的功能和机制；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Pod 的定义在 CRI 设计时演进地非常快，初始化容器等功能都需要运行时的配合；</section></li></ol><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然社区最终为 CRI 选择了命令式的接口，但是 Kubelet 仍然会保证 Pod 的状态会不断地向期望状态迁移。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>不兼容接口</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">与容器运行时相比，Docker 更像是一个复杂的开发者工具，它提供了从构建到运行的全套功能。开发者可以很快地上手 Docker 并在本地运行并管理一些 Docker 容器，然而在集群中运行的容器运行时往往不需要这么复杂的功能，Kubernetes 需要的只是 CRI 中定义的那些接口。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.375" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=caace873&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7brG5PAqvicfe0DGRia04ibMD77hF8iaBd8OLKnHhLXFbJj2hxZDZ5bosbIL2ryoZefBhmCdBSpQcJ4jcA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">docker-and-cri</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 4 - Docker &amp; CRI</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Docker 的官方文档加起来可能有一本书的厚度，相信没有任何开发者可以熟练运用 Docker 提供的全部功能。而作为开发者工具，虽然 Docker 中包含 CRI 需要的所有功能，但是都需要实现一层包装以兼容 CRI。除此之外，社区提出的很多新功能都没有办法在 Dockershim 中实现，例如 cgroups v2 以及用户命名空间。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 作为比较松散的开源社区，每个成员尤其是各个 SIG 的成员都只会在开源社区上花费有限的时间，而维护 Kubelet 的 sig-node 又尤其繁忙，很多新的功能都因为维护者没有足够的精力而被搁置，所以既然 Docker 社区看起来没有打算支持 Kubernetes 的 CRI 接口，维护 Dockershim 又需要花费很多精力，那么我们就能理解为什么 Kubernetes 会移除 Dockershim 了。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">今天的 Kubernetes 已经是非常成熟的项目，它的关注点也逐渐从提供更完善的功能转变到提供更好的扩展性，这样才能满足不同场景和不同公司定制化的业务需求。Kubernetes 在过去因为 Docker 的热门而选择 Docker，而在今天又因为高昂的维护成本而放弃 Docker，我们能够从这个过程中体会到容器领域的发展和进步。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">移除 Docker 的种子其实从 CRI 发布时就种下了，Dockershim 一直都是 Kubernetes 为了兼容 Docker 获得市场采取的临时决定，对于今天已经统治市场的 Kubernetes 来说，Docker 的支持显得非常鸡肋，移除代码也就顺理成章了。我们在这里重新回顾一下 Kubernetes 在仓库中移除 Docker 支持的两个原因：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Kubernetes 在早期版本中引入 CRI 摆脱依赖某个具体的容器运行时依赖，屏蔽底层的诸多实现细节，让 Kubernetes 能够更关注容器的编排；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Docker 本身不兼容 CRI 接口，而且官方并没有实现 CRI 的打算，同时也不支持容器的一些新需求，所以 Dockershim 的维护成为了社区的想要摆脱负担；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">到最后，我们还是来看一些比较开放的相关问题，有兴趣的读者可以仔细思考一下下面的问题：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Kubernetes 中还有哪些模块提供良好的扩展性？</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">除了文中提到的 CRI-O、Containerd，还有哪些支持 CRI 的容器运行时？</section></li></ul><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">如果对文章中的内容有疑问或者想要了解更多软件工程上一些设计决策背后的原因，可以在博客下面留言，作者会及时回复本文相关的疑问并选择其中合适的主题作为后续的内容。</p></blockquote><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>参考资料</h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Dockershim Deprecation FAQ <a href="https://kubernetes.io/blog/2020/12/02/dockershim-faq/" target="_blank">https://kubernetes.io/blog/2020/12/02/dockershim-faq/</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Don&#39;t Panic: Kubernetes and Docker <a href="https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/" target="_blank">https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/</a></section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: Removing dockershim from kubelet #1985 <a href="https://github.com/kubernetes/enhancements/pull/1985" target="_blank">https://github.com/kubernetes/enhancements/pull/1985</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^3]: Introducing Container Runtime Interface (CRI) in Kubernetes <a href="https://kubernetes.io/blog/2016/12/container-runtime-interface-cri-in-kubernetes/" target="_blank">https://kubernetes.io/blog/2016/12/container-runtime-interface-cri-in-kubernetes/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^2]: Container Runtime Interface (CRI) – a plugin interface which enables kubelet to use a wide variety of container runtimes. <a href="https://github.com/kubernetes/cri-api/blob/master/pkg/apis/runtime/v1/api.proto" target="_blank">https://github.com/kubernetes/cri-api/blob/master/pkg/apis/runtime/v1/api.proto</a></p></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">推荐阅读</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484410&amp;idx=1&amp;sn=a3e1b698bb35068250e36a062efa7b8b&amp;chksm=fe795cf1c90ed5e75bc01e42f14b3f3b9fc608d7e17ed9fd16c72cddced040f140456a7327fa&amp;scene=21#wechat_redirect" textvalue="为什么数据库不应该使用外键" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></a><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484343&amp;idx=1&amp;sn=9209f33fbd85afdbed88662b2e9e8e69&amp;chksm=fe795cbcc90ed5aad26e1244de90d9668a9f2598dbf120fc72705b415792170b2293f0c33176&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">Kubernetes 贡献指南</a></section></li><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484054&amp;idx=1&amp;sn=aaa67860a51d83cc62c20045c96a2e09&amp;chksm=fe795d9dc90ed48b54371cdbd0024317aa71f9d1d8cbec817bdb4d7fe24b36d042a652c770b5&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">Docker 核心技术与实现原理</a><br/></section></li></ul><blockquote data-tool="mdnice编辑器" style="margin-top: 20px;margin-bottom: 20px;padding: 10px 10px 10px 20px;border-left-color: rgba(0, 0, 0, 0.4);color: rgb(106, 115, 125);font-size: 0.9em;max-width: 100%;border-top: none;border-right: none;border-bottom: none;overflow: auto;background: rgba(0, 0, 0, 0.05);box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="padding-top: 8px;padding-bottom: 8px;max-width: 100%;min-height: 1em;font-size: 16px;color: black;line-height: 26px;box-sizing: border-box !important;overflow-wrap: break-word !important;">如果对文章中的内容有疑问或者想要了解更多软件工程上一些设计决策背后的原因，可以在博客下面留言，作者会及时回复本文相关的疑问并选择其中合适的主题作为后续的内容。</p></blockquote></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"/></p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p><p><br/></p>



<p><a href="https://draveness.me/whys-the-design-kubernetes-deprecate-docker/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=ed94b933&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484807%26idx%3D1%26sn%3Ddb8619f1c0e9273842cdf3803f299eab%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 16 Mar 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>Facebook 集群调度管理系统 · OSDI 2020</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484797&amp;idx=1&amp;sn=fd73cc9bd2e56742a5627432aa5a4166</link>
      <description></description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-03-09 08:30</span> <span style="display: inline-block;"></span>
</p>

<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=9db1ab9f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7boH4SPwNgTaNr7rlz0opV2QzmvibdOejwMOFcTeZicpCYKutxVNHENgKxIWnzdxDIFfvPicsHLm7VVIg%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">『看看论文』是一系列分析计算机和软件工程领域论文的文章，我们在这个系列的每一篇文章中都会阅读一篇来自 OSDI、SOSP 等顶会中的论文，这里不会事无巨细地介绍所有的细节，而是会筛选论文中的关键内容，如果你对相关的论文非常感兴趣，可以直接点击链接阅读原文。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">本文要介绍的是 2020 年 OSDI 期刊中的论文 —— Twine: A Unified Cluster Management System for Shared Infrastructure[^1]，该论文实现的 Twine 是 Facebook 过去十年生产环境中的集群管理系统。在该系统出现之前，Facebook 的集群由为业务定制的独立资源池组成，因为这些资源池中的机器可能有独立的版本或者配置，所以无法与其他业务共享。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Twine 的出现解决了不同资源池中机器配置不同的问题，提供了动态配置机器的功能，这样可以合并原本独立的资源池，提高资源整体的利用率，在业务申请资源时可以根据需要配置机器，例如：改变内核版本、启用 HugePages 以及 CPU Turbo 等特性。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.30833333333333335" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=e0c5ef72&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boH4SPwNgTaNr7rlz0opV2QaCu3miaEb5qJlX3CaJTueDLoOnPrvR9c5sxjyKUbH9RjPx8rPTTLfnw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - Twine 设计决策</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 是今天十分热门的集群管理方案，不过 Facebook 的方案 Twine 却做出了与 Kubernetes 相反的决策，实现了截然不同的解决方案。需要注意的是使用 Kubernetes 并不一定意味着要使用静态集群、私有节点池和大容量机器，我们仍然可以通过引入其他模块实现动态集群等特性，只是 Kubernetes 本身不支持这些设计。我们在这篇文章中仅会讨论上述三大决策的前两个以及 Twine 如何实现水平扩容、管理大规模的集群。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>架构设计</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">作为可以管理上百万机器、支撑 Facebook 业务的核心调度管理系统，Twine 的生态系统非常复杂，我们在这里简单介绍该系统中的一些核心组件：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.410466067048242" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1223" src="https://wechat2rss.xlab.app/img-proxy/?k=0602dc94&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boH4SPwNgTaNr7rlz0opV2QV6Dg2fxqxfZfz4MTr2bOsyy9JssylPqnNlOsYkvfMuVsLVYa98ricMA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - Twine 生态系统</strong><br/></figcaption></figure><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">分配器（Allocator）：对应 Kubernetes 中的调度器，负责为工作负载分配机器，它在内存中维护了所有机器的索引和属性并使用多线程处理资源的调度分配；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">调度器（Scheduler）：对应 Kubernetes 中的控制器，它负责管理工作负载的生命周期，当集群出现硬件故障、日常维护等情况时会推动系统做出响应；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">应用程序调度器（Application-Level Schedulers）：对应 Kubernetes 中的 Operator，如果我们想使用特殊的逻辑管理有状态服务，需要实现自定义的调度器；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">分配器、调度器和应用程序调度器是 Twine 系统中的核心组件，然而除了这些组件之外，生态中还包含前端界面、优化集群工作负载的平衡器和指定特定业务容量的服务。在了解这些具体组件之后，这里我们围绕文章开头提出的动态集群和自定义配置展开讨论 Twine 的设计。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>动态集群<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Twine 的动态集群建立在其抽象出的权利（Entitlement）上，每个权利集群都包含一组动态分配的机器、属于特定业务的伪集群。数据中心中的机器和任务之间建立其的这层抽象使机器的分配变得更加动态：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.3333333333333333" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=030f4dd6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boH4SPwNgTaNr7rlz0opV2QCuQDnkmYibq3QDWiaddkPn2sTtmvskpVBRPk5eJfJ7YFGupONQUrk8aA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - 任务、权利和数据中心</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">分配器不仅会将机器分配给权利集群，还会把同一个权利集群中的工作负载调度到特定的机器上。</p><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">需要注意的是，我们在这里简化了 Twine 中的模型，Facebook 的数据中心会由几十个主配电板（Main Switchboard、MSB）组成，它们具有独立的电力供应和网络隔离，配电板上的机器可以看做属于同一个集群。</p></blockquote><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>自定义配置<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">私有的节点池很不利于机器的共享，但是确实有很多业务对机器的内核版本和配置有要求，例如：很多机器学习或者数据统计的任务都需要使用 Linux 的 HugePages 优化性能，但是 HugePages 可能会损害在线服务的性能。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.35833333333333334" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=573f7cf8&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boH4SPwNgTaNr7rlz0opV2QhE9jK7eyz4R7MtlQXo17FVLxmxAnoYys9c6VUYKRtJwUicZXicibttwYA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - 主机配置</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Twine 由此引入了主机配置的概念，为每个权利集群绑定独立的主机配置，当数据中心的机器被分配到某个伪集群时，会根据集群的配置更新机器，为工作负载提供最符合需求的运行环境，这在 Facebook 内将 Web 层的服务性能提高了 11%，也是目前的 Kuberentes 无法满足的。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>集群规模</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Facebook 的集群规模也是目前世界领先的，虽然目前的集群规模还没有突破百万级，但是随着业务的快速发展，Twine 很快就需要支持百万级别的物理机管理，它会通过下面两个原则支撑这个数量级的节点：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">通过按照权利集群分片的方式水平扩容；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">通过分离关注点减少调度系统的工作量；</section></li></ul><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>分片<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">分片是集群或者系统想要实现水平扩容的最常见方式，Twine 为了支持水平扩容就以权利集群的维度分片；作为虚拟集群，Twine 可以在分片之间迁移权利集群，不需要重启机器上的任务，然而跨权利集群的迁移就需要滚动更新的支持了。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.25" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=9ef67195&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boH4SPwNgTaNr7rlz0opV2QYkPIibvo3IOsUWhO11d8VEWAw5d0ZtyahQ3PbuPld5Qic2tomCCOBT4g%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 5 - 调度器分片</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">通过分片，集群管理系统的水平扩容就变得非简单，而 Twine 最大的分片中管理了 170,000 台机器，这与 Kubernetes 能够支持 5,000 节点相比有将近两个数量级的差距。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了分片之外，联邦也是解决集群管理规模的有效手段，Kubernetes 社区的联邦可以让同一个任务在多个独立集群运行，可以支持多地区、混合云甚至多云的部署，但是因为需要跨集群同步信息，所以实现相对比较复杂；Twine 的调度器可以在分片中的机器不足时动态迁移新的机器，所以可以使用单个调度器管理一个服务的所有副本。这里就不讨论两种方案的优劣了，各位读者可以自行思考，不过作者还是倾向于通过的联邦管理多个集群。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>分离关注<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 是一种中心化的架构，所有的组件都会从集群中的 API 服务器读取或者写入信息，所有的数据都存储在独立的持久存储系统中，而中心化的架构和存储系统也成为了 Kubernetes 集群管理的瓶颈。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Twine 在设计上尽量避免了中心化的存储系统并分离原本属于单个组件的职责，拆分到了调度器、分配器、资源代理、健康检查服务和主机配置服务中，每个服务也有独立的存储系统，这就能够避免单存储系统带来的扩容问题。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在 Kubernetes 大行其道的今天，能够看到 Facebook 分享其内部集群管理系统的不同设计还是有很大意义的，这让我们重新思考 Kubernetes 中设计带来的潜在问题，例如：中心化的 etcd 存储，很多使用 Kubernetes 的大公司为了让其能够管理更多节点，都会选择修改 etcd 的源代码或者替换存储系统。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kubernetes 对于集群规模较小的公司还是有很大好处的，而其本身确实能够解决集群管理中 95% 的问题，Kubernetes 也不是银弹，它没法做到解决场景内的全部问题。在应用 Kubernetes 时，中小规模的公司可以全盘接收 Kubernetes 的架构和设定，而大公司可以在 Kubernetes 的基础上做一些定制，甚至参与到标准的制定中增加技术影响力、提高话语权并且帮助支撑公司业务成长。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>推荐阅读</h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484502&amp;idx=1&amp;sn=8e4e2dd48c9ca18bc8eb4941d886190d&amp;chksm=fe795b5dc90ed24b977950948b23f8684bd349c1dfa3396df61885de902a4dc60db3050ece9f&amp;scene=21#wechat_redirect" textvalue="数据布局服务与局部性管理 · OSDI 2018" data-itemshowtype="0" tab="innerlink" data-linktype="2">数据布局服务与局部性管理 · OSDI 2018</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484620&amp;idx=1&amp;sn=323c44f5d13b722102f36c0bcca79abb&amp;chksm=fe795bc7c90ed2d153e79a2897cf92f84d18d4d355ec775c4acd29b3d2a2a7f6a27edfe3c938&amp;scene=21#wechat_redirect" textvalue="流量管理与数据中心故障缓解 · OSDI 2018" data-itemshowtype="0" tab="innerlink" data-linktype="2">流量管理与数据中心故障缓解 · OSDI 2018</a></section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: Tang C, Yu K, Veeraraghavan K, et al. Twine: A Unified Cluster Management System for Shared Infrastructure[C]//14th {USENIX} Symposium on Operating Systems Design and Implementation ({OSDI} 20). 2020: 787-803.</p></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"/></p><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p>



<p><a href="https://draveness.me/papers-twine/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=9f38db16&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484797%26idx%3D1%26sn%3Dfd73cc9bd2e56742a5627432aa5a4166%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 09 Mar 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>内存管理设计精要</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484786&amp;idx=1&amp;sn=cbc7790b6a90e380cd6e0f39431a2ca5</link>
      <description>从基础知识、内存分配、垃圾回收和高级垃圾回收四个部分介绍内存管理技术</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-03-02 08:30</span> <span style="display: inline-block;"></span>
</p>

<p>从基础知识、内存分配、垃圾回收和高级垃圾回收四个部分介绍内存管理技术</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=df4c9d81&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8bBEVGBPvuz26fSODx1HnmMrnr7BjGdAib3ibTTVGia4mUiafDFpJklH15A%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">系统设计精要是一系列深入研究系统设计方法的系列文章，文中不仅会分析系统设计的理论，还会分析多个实际场景下的具体实现。这是一个季更或者半年更的系列，如果你有想要了解的问题，可以在文章下面留言。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">持久存储的磁盘在今天已经不是稀缺的资源了，但是 CPU 和内存仍然是相对比较昂贵的资源，作者在 <a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484172&amp;idx=1&amp;sn=f4094370ae5e56160dd9a57451daea29&amp;chksm=fe795c07c90ed5118d46e2ffff6357e4bc78b97f69676f1b7eb6ae624bae9a9ed9e7667800fa&amp;scene=21#wechat_redirect" textvalue="调度系统设计精要" data-itemshowtype="0" tab="innerlink" data-linktype="2">调度系统设计精要</a> 中曾经介绍操作系统和编程语言对 CPU 资源的调度策略和原理，本文将会介绍计算机中常见的另一个稀缺资源 — 内存，是如何管理的。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.2" src="https://wechat2rss.xlab.app/img-proxy/?k=14ed9f38&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8E4NrcDnYEY5Bdh9HdaGhPZ1xzForfcMlwfAh2VUkV7TxCQOBGfrGaw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - 内存系统设计精要</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">内存管理系统和模块在操作系统以及编程语言中都占有着重要的地位，任何资源的使用都离不开申请和释放两个动作，内存管理中的两个重要过程就是内存分配和垃圾回收，内存管理系统如何利用有限的内存资源为尽可能多的程序或者模块提供服务是它的核心目标。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.41574074074074074" src="https://wechat2rss.xlab.app/img-proxy/?k=8fed6999&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8TLxYJrjh9kIuIce6UhvH3Cqe9ibycVRMn5vKBEicuHCDN5OS3h23qQeA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - 文章脉络和内容</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然多数系统都会将内存管理拆分成多个复杂的模块并引入一些中间层提供缓存和转换的功能，但是内存管理系统实际上都可以简化成两个模块，即内存分配器（Allocator）、垃圾收集器（Collector）。当然除了这两个模块之外，在研究内存管理时都会引入第三个模块 — 用户程序（Mutator），帮助我们理解整个系统的工作流程。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.5166666666666667" src="https://wechat2rss.xlab.app/img-proxy/?k=3689036e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8ibr0bQVicHHFjWFzRIYM9XZ6eYmoGh9X1hN2yQCuFxdS0S91UMsKabSA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - 内存管理系统模块</strong><br/></figcaption></figure><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">用户程序（Mutator）- 可以通过分配器创建对象或者更新对象持有的指针；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">内存分配器（Allocator）— 处理用户程序的的内存分配请求；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">垃圾收集器（Collector）- 标记内存中的对象并回收不需要的内存；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上述的三个模块是内存管理系统中的核心，它们在应用程序运行期间可以维护管理内存达到相对平衡的状态，我们在介绍内存管理时也会围绕这三个不同的组件，本节将从基本概念、内存分配和垃圾回收三个方面详细介绍内存管理的相关理论。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>基本概念</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">基本概念这一节将介绍内存管理中的基本问题，我们会简单介绍应用程序的内存布局、内存管理中的设计的常见概念以及广义上的几种不同内存管理方式，这里会帮助各位读者从顶层了解内存管理。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>内存布局<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">操作系统会为在其上运行的应用程序分配一片巨大的虚拟内存，需要注意的是，与操作系统的主存和物理内存不一样，虚拟内存并不是在物理上真正存在的概念，它是操作系统构建的逻辑概念。应用程序的内存一般会分成以下几个不同的区域：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="500" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.95" src="https://wechat2rss.xlab.app/img-proxy/?k=42ac219b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8osgfgKnOiarVlOY8PxxXZmFIT2cicbByuZ856MHzEIfVT2O2yDnDn0Aw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - 内存布局</strong><br/></figcaption></figure><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">栈区（Stack）— 存储程序执行期间的本地变量和函数的参数，从高地址向低地址生长；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">堆区（Heap）— 动态内存分配区域，通过 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">malloc</code>、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">new</code>、<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">free</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">delete</code> 等函数管理；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">未初始化变量区（BSS）— 存储未被初始化的全局变量和静态变量；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">数据区（Data）— 存储在源代码中有预定义值的全局变量和静态变量；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">代码区（Text）— 存储只读的程序执行代码，即机器指令；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上述五种不同段虽然存储着不同的数据，但是我们可以将它们分成三种不同的内存分配类型，也就是静态内存、栈内存和堆内存。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>静态内存<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">静态内存可以最早追溯到 1960 年的 ALGOL 语言[^1]，静态变量的生命周期可以贯穿整个程序。所有静态内存的布局都是在编译期间确认的，运行期间也不会分配新的静态内存，因为所有的静态内存都是在编译期间确认的，所以会为这些变量申请固定大小的内存空间，这些固定的内存空间也会导致静态内存无法支持函数的递归调用：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="500" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.62" src="https://wechat2rss.xlab.app/img-proxy/?k=82d9716a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8422NF3XATXV4KicvLKTNVo6iaDriaj3euNEJ94vs4oBJrrWlVqCIWknnQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 5 - 静态内存的特性</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为编译器可以确定静态变量的地址，所以它们是程序中唯一可以使用绝对地址寻址的变量。当程序被加载到内存中时，静态变量会直接存储在程序的 BSS 区或者数据区，这些变量也会在程序退出时被销毁，正是因为静态内存的这些特性，我们并不需要在程序运行时引入静态内存的管理机制。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>栈内存<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">栈是应用程序中常见的内存空间，它遵循后进先出的规则管理存储的数据[^2]。当应用程序调用函数时，它会将函数的参数加入栈顶，当函数返回时，它会将当前函数使用的栈全部销毁。栈内存管理的指令也都是由编译器生成的，我们会使用 BP 和 SP 这两个寄存器存储当前栈的相关信息，完全不需要工程师的参与，不过我们也只能在栈上分配大块固定的数据结构。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="500" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.62" src="https://wechat2rss.xlab.app/img-proxy/?k=b660a888&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8tmict9ebQf1ahTN6q5ug1pXLakow4t0ibWaSq5h5wJrpxricqp27fk0Yg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 6 - 栈内存的特性</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为栈内存的释放是动态的并且是线性的，所以它可以支持函数的递归调用，不过运行时动态栈分配策略的引入也会导致程序栈内存的溢出，如果我们在编程语言中使用的递归函数超出了程序内存的上限，会造成栈溢出错误。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>堆内存<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">堆内存也是应用程序中的常见内存，与超过函数作用域会自动回收的栈内存相比，它能够让函数的被调用方向调用方返回内存并在内存的分配提供更大的灵活性，不过它提供的灵活性也带来了内存泄漏和悬挂指针等内存安全问题。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="500" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.78" src="https://wechat2rss.xlab.app/img-proxy/?k=f877e67e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8G0pPiavTeEu9jeJrFbJYyicg03uxPMibfHVaRBA6sx72I9HBiaz78pSkjg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 7 - 堆内存的特性</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为堆上的内存是工程师手动申请的，所以需要在使用结束时释放，一旦用过的内存没有释放，就会造成内存泄漏，占用更多的系统内存；如果在使用结束前释放，会导致危险的悬挂指针，其他对象指向的内存已经被系统回收或者重新使用。虽然进程的内存可以划分成很多区域，但是当我们在谈内存管理时，一般指的都是堆内存的管理，也就是如何解决内存泄漏和悬挂指针的问题。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>管理方式<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们可以将内存管理简单地分成手动管理和自动管理两种方式，手动管理内存一般是指由工程师在需要时通过 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">malloc</code> 等函数手动申请内存并在不需要时调用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">free</code> 等函数释放内存；自动管理内存由编程语言的内存管理系统自动管理，在大多数情况下不需要工程师的参与，能够自动释放不再使用的内存。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="300" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="1.0333333333333334" src="https://wechat2rss.xlab.app/img-proxy/?k=4dd56215&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG88xfO6RrP5AwTxz5yMEWAYYemDH8zxttusib732X6ic2sibagqRicbCmNUA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 8 - 手动管理和自动管理</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">手动管理和自动管理只是内存管理的两种不同方式，本节将分别介绍两种内存管理的方式以及不同编程语言做出的不同选择。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>手动管理<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">手动管理内存是一种比较传统的内存管理方式，C/C++ 这类系统级的编程语言不包含<strong>狭义上的</strong>自动内存管理机制，工程师需要主动申请或者释放内存。如果存在理想的工程师能够精准地确定内存的分配和释放时机，人肉的内存管理策略只要做到足够精准，使用手动管理内存的方式可以提高程序的运行性能，也不会造成内存安全问题，</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">但是这种理想的工程师往往不存在于现实中，人类因素（Human Factor）总会带来一些错误，内存泄漏和悬挂指针基本是 C/C++ 这类语言中最常出现的错误，手动的内存管理也会占用工程师的大量精力，很多时候都需要思考对象应该分配到栈上还是堆上以及堆上的内存应该何时释放，维护成本相对来说还是比较高的，这也是必然要做的权衡。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>自动管理<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">自动管理内存基本是现代编程语言的标配，因为内存管理模块的功能非常确定，所以我们可以在编程语言的编译期或者运行时中引入自动的内存管理方式，最常见的自动内存管理机制就是垃圾回收，不过除了垃圾回收之外，一些编程语言也会使用自动引用计数辅助内存的管理。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">自动的内存管理机制可以帮助工程师节省大量的与内存打交道的时间，让工程师将全部的精力都放在核心的业务逻辑上，提高开发的效率；在一般情况下，这种自动的内存管理机制都可以很好地解决内存泄漏和悬挂指针的问题，但是这也会带来额外开销并影响语言的运行时性能。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>对象头<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">对象头是实现自动内存管理的关键元信息，内存分配器和垃圾收集器都会访问对象头以获取相关的信息。当我们通过 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">malloc</code> 等函数申请内存时，往往都需要将内存按照指针的大小对齐（32 位架构上为 4 字节，64 位架构上为 8 字节），除了用于对齐的内存之外，每一个堆上的对象也都需要对应的对象头：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="630" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.4444444444444444" src="https://wechat2rss.xlab.app/img-proxy/?k=a3902d7f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8mljMmG9dcibnxrIuUI11b6H2x2iaAQmk3Qynhquwic033he8oBjdFQjFg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 9 - 对象头与对象</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">不同的自动内存管理机制会在对象头中存储不同的信息，使用垃圾回收的编程语言会存储标记位 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">MarkBit</code>/<code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">MarkWord</code>，例如：Java 和 Go 语言；使用自动引用计数的会在对象头中存储引用计数 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">RefCount</code>，例如：Objective-C。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">编程语言会选择将对象头与对象存储在一起，不过因为对象头的存储可能影响数据访问的局部性，所以有些编程语言可能会单独开辟一片内存空间来存储对象头并通过内存地址建立两者之间的隐式联系。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>内存分配</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">内存分配器是内存管理系统中的重要组件，它的主要职责是处理用户程序的内存申请。虽然内存分配器的职责非常重要，但是<strong>内存的分配和使用其是一个增加系统中熵的过程</strong>，所以内存分配器的设计与工作原理相对比较简单，我们在这里介绍内存分配器的两种类型。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">内存分配器只包含线性内存分配器（Sequential Allocator）和空闲链表内存分配器（Free-list Allocator）两种，内存管理机制中的所有内存分配器其实都是上述两种不同分配器的变种，它们的设计思路完全不同，同时也有着截然不同的应用场景和特性，我们在这里依次介绍这两种内存分配器的原理。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>线性分配器<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">线性分配（Bump Allocator）是一种高效的内存分配方法，但是有较大的局限性。当我们在编程语言中使用线性分配器，我们只需要在内存中维护一个指向内存特定位置的指针，当用户程序申请内存时，分配器只需要检查剩余的空闲内存、返回分配的内存区域并修改指针在内存中的位置，即移动下图中的指针：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.24166666666666667" src="https://wechat2rss.xlab.app/img-proxy/?k=efa6757c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8ff7unQ1jHz6eWsoDwicAticbIlFyuGcO0T4GcYcO12w2fv1xPiaAPNtqg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 10 - 线性分配器</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">根据线性分配器的原理，我们可以推测它有较快的执行速度，以及较低的实现复杂度；但是线性分配器无法在内存被释放时重用内存。如下图所示，如果已经分配的内存被回收，线性分配器是无法重新利用红色的这部分内存的：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.24166666666666667" src="https://wechat2rss.xlab.app/img-proxy/?k=807c8ebe&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8qLkdZR7ibQLncRicCK05moBDZnjrHrmvjGxLm3zF4FxmvWrhdzicP4CZg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 11 - 线性分配器回收内存</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">正是因为线性分配器的这种特性，我们需要合适的垃圾回收算法配合使用。标记压缩（Mark-Compact）、复制回收（Copying GC）和分代回收（Generational GC）等算法可以通过拷贝的方式整理存活对象的碎片，将空闲内存定期合并，这样就能利用线性分配器的效率提升内存分配器的性能了。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为线性分配器的使用需要配合具有拷贝特性的垃圾回收算法，所以 C 和 C++ 等需要直接对外暴露指针的语言就无法使用该策略，我们会在下一节详细介绍常见垃圾回收算法的设计原理。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>空闲链表分配器<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">空闲链表分配器（Free-List Allocator）可以重用已经被释放的内存，它在内部会维护一个类似链表的数据结构。当用户程序申请内存时，空闲链表分配器会依次遍历空闲的内存块，找到足够大的内存，然后申请新的资源并修改链表：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.26666666666666666" src="https://wechat2rss.xlab.app/img-proxy/?k=556e7cfa&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8IZE3vyJxOt5r9uVFwNmpsN1bA8tA5tGtKbhYicj7bmSRqva8J1K34icg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 12 - 空闲链表分配器</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为不同的内存块以链表的方式连接，所以使用这种方式分配内存的分配器可以重新利用回收的资源，但是因为分配内存时需要遍历链表，所以它的时间复杂度就是 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">O(n)</code>。空闲链表分配器可以选择不同的策略在链表中的内存块中进行选择，最常见的就是以下四种方式：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">首次适应（First-Fit）— 从链表头开始遍历，选择第一个大小大于申请内存的内存块；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">循环首次适应（Next-Fit）— 从上次遍历的结束位置开始遍历，选择第一个大小大于申请内存的内存块；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">最优适应（Best-Fit）— 从链表头遍历整个链表，选择最合适的内存块；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">隔离适应（Segregated-Fit）— 将内存分割成多个链表，每个链表中的内存块大小相同，申请内存时先找到满足条件的链表，再从链表中选择合适的内存块；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上述四种策略的前三种就不过多介绍了，Go 语言使用的内存分配策略与第四种策略有些相似，我们通过下图了解一下该策略的原理：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.49166666666666664" src="https://wechat2rss.xlab.app/img-proxy/?k=9b016ffa&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8ZyGkTIF9232Dm9eSLUd01x7sBuzkSpvvsgUHbrt2IwxZe3icfj8vhtw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 13 - 隔离适应策略</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如上图所示，该策略会将内存分割成由 4、8、16、32 字节的内存块组成的链表，当我们向内存分配器申请 8 字节的内存时，我们会在上图中的第二个链表找到空闲的内存块并返回。隔离适应的分配策略减少了需要遍历的内存块数量，提高了内存分配的效率。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>垃圾回收</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">垃圾回收是一种自动的内存管理形式[^3]，垃圾收集器是内存管理系统的重要组件，内存分配器会负责在堆上申请内存，而垃圾收集器会释放不再被用户程序使用的对象。谈到垃圾回收，很多人的第一反应可能都是暂停程序（stop-the-world、STW）和垃圾回收暂停（GC Pause），垃圾回收确实会带来 STW，但是这不是垃圾回收的全部，本节将详细介绍垃圾回收以及垃圾收集器的相关概念和理论。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>什么是垃圾<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在深入分析垃圾回收之前，我们需要先明确垃圾回收中垃圾的定义，明确定义能够帮助我们更精确地理解垃圾回收解决的问题以及它的职责。计算机科学中的垃圾包括对象、数据和计算机系统中的其他的内存区域，这些数据不会在未来的计算中使用，因为内存资源是有限的，所以我们需要将这些垃圾占用的内存交还回堆并在未来复用[^4]。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="300" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="1.0333333333333334" src="https://wechat2rss.xlab.app/img-proxy/?k=2ddc7776&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8g0lhmBG4oukUs2TwA4KW6IOd5BjDvp44P6XWibA8PQib5b6D63VmNwzQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 14 - 语义和语法垃圾</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">垃圾可以分成语义垃圾和语法垃圾两种，*语义垃圾（Semantic Garbage）*是计算机程序中永远不会被程序访问到的对象或者数据；*语法垃圾（Syntactic Garbage）*是计算机程序内存空间中从根对象无法达到（Unreachable）的对象或者数据。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">语义垃圾是<strong>不会被使用的</strong>的对象，可能包括废弃的内存、不使用的变量，垃圾收集器无法解决程序中语义垃圾的问题，我们需要通过编译器来一部分语义垃圾。语法垃圾是在对象图中<strong>不能从根节点达到的</strong>对象，所以语法垃圾在一般情况下都是语义垃圾：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="630" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.5238095238095238" src="https://wechat2rss.xlab.app/img-proxy/?k=f7d2caae&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG88P6LePhVjSzrItNdHgOfTpBPKtoJ6gLHFWkm5S2BVvamLmK2PpGKicw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 15 - 无法达到的语法垃圾</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">垃圾收集器能够发现并回收的就是对象图中无法达到的语法垃圾，通过分析对象之间的引用关系，我们可以得到图中根节点不可达的对象，这些不可达的对象会在垃圾收集器的清理阶段被回收。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>收集器性能<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">吞吐量（Throughput）和最大暂停时间（Pause time）是两个衡量垃圾收集器的主要指标，除了这两个指标之外，堆内存的使用效率和访问的局部性也是垃圾收集的常用指标，我们简单介绍以下这些指标对垃圾收集器的影响。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>吞吐量<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">垃圾收集器的吞吐量其实有两种解释，一种解释是垃圾收集器在执行阶段的速度，也就是单位时间的标记和清理内存的能力，我们可以用堆内存除以 GC 使用的总时间来计算。</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bppvQaK0RXusGusxGmOJsG8wPFciafzSjLB7uu8kk6jjJYlJzWAkBOPayNHnQcGoKE1ictKg6zicUqOw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;">HEAP_SIZE / TOTAL_GC_TIME<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">另一种吞吐量计算方法是使用程序运行的总时间除以所有 GC 循环运行的总时间，GC 的时间对于整个应用程序来说是额外开销，这个指标能看出额外开销占用资源的百分比，从这一点，我们也能看出 GC 的执行效率。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>最大暂停时间<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">由于在垃圾回收的某些阶段会触发 STW，所以用户程序是不能执行的，最长的 STW 时间会严重影响程序处理请求或者提供服务的尾延迟，所以这一点也是我们在测量垃圾收集器性能时需要考虑的指标。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="630" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.42857142857142855" src="https://wechat2rss.xlab.app/img-proxy/?k=406a2dff&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8WklZv4aKd5oyKQMFWdib8A5gwGaxyAh99qRkmL0HWhPtrlahjr5kjPg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 16 - 最大暂停时间</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">使用 STW 垃圾收集器的编程语言，用户程序在垃圾回收的全部阶段都不能执行。并发标记清除的垃圾收集器将可以与用户程序并发执行的工作全部并发执行，能够减少最大程序暂停时间，</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>堆使用效率<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">堆的使用效率也是衡量垃圾收集器的重要指标。为了能够标识垃圾，我们需要在内存空间中引入包含特定信息的对象头，这些对象头都是垃圾收集器带来的额外开销，正如网络带宽可能不是最终的下载速度，协议头和校验码的传输会占用网络带宽，对象头的大小最终也会影响堆内存的使用效率；除了对象头之外，堆在使用过程中出现的碎片也会影响内存的使用效率，为了保证内存的对齐，我们会在内存中留下很多缝隙，这些缝隙也是内存管理带来的开销。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>访问局部性<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">访问的局部性是我们在讨论内存管理时不得不谈的话题，空间的局部性是指处理器在短时间内总会重复地访问同一片或者相邻的内存区域，操作系统会以内存页为单位管理内存空间，在理想情况下，合理的内存布局可以使得垃圾收集器和应用程序都能充分地利用空间局部性提高程序的执行效率。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>收集器类型<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">垃圾收集器的类型在总体上可以分成直接（Direct）垃圾收集器和跟踪（Tracing）垃圾收集器。直接垃圾收集器包括引用计数（Refernce-Counting），跟踪垃圾收集器包含标记清理、标记压缩、复制垃圾回收等策略，而引用计数收集器却不是特别常见，少数编程语言会使用这种方式管理内存。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="760" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.631578947368421" src="https://wechat2rss.xlab.app/img-proxy/?k=cdc8c302&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8b9qibFicic0JEqAFxbo6qEv8ttBtQcOricrmciavD6PMBapFyHCvu09e4icQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 17 - 垃圾收集器类型</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了直接和跟踪垃圾收集器这些相对常见的垃圾回收方法之外，也有使用所有权或者手动的方式管理内存，我们在本节中会介绍引用计数、标记清除、标记压缩和复制垃圾回收四种不同类型垃圾收集器的设计原理以及它们的优缺点。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>引用计数<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">基于引用计数的垃圾收集器是直接垃圾收集器，当我们改变对象之间的引用关系时会修改对象之间的引用计数，每个对象的引用计数都记录了当前有多少个对象指向了该对象，当对象的引用计数归零时，当前对象就会被自动释放。在使用引用计数的编程语言中，垃圾收集是在用户程序运行期间实时发生的，所以在理论上也就不存在 STW 或者明显地垃圾回收暂停。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="630" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.49206349206349204" src="https://wechat2rss.xlab.app/img-proxy/?k=41aa8f93&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8ZfPkXBiaw5tZ0OyAw4bBZxw1gs0wzREVXvA73oPAJz0YrHE02C9yOhA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 18 - 对象的引用计数</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如上图所示，基于引用计数的垃圾收集器需要应用程序在对象头中存储引用计数，引用计数就是该类型的收集器在内存中引入的额外开销。我们在这里举一个例子介绍引用计数的工作原理，如果在使用引用计数回收器的编程语言中使用如下所示赋值语句时：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bppvQaK0RXusGusxGmOJsG8wPFciafzSjLB7uu8kk6jjJYlJzWAkBOPayNHnQcGoKE1ictKg6zicUqOw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;">obj.field = new_ref;<br/></code></pre><ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">对象 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">obj</code> 原来引用的对象 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">old_ref</code> 的引用计数会<strong style="color: black;">减一</strong>；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">对象 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">obj</code> 引用的新对象 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">new_ref</code> 的引用计数会<strong style="color: black;">加一</strong>；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">如果 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">old_ref</code> 对象的引用计数归零，我们会释放该对象回收它的内存；</section></li></ol><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这种类型的垃圾收集器会带来两个比较常见的问题，分别是递归的对象回收和循环引用：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">递归回收 — 每当对象的引用关系发生改变时，我们都需要计算对象的新引用计数，一旦对象被释放，我们就需要递归地访问所有该对象的引用并将被引用对象的计数器减一，一旦涉及到较多的对象就可能会造成 GC 暂停；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">循环引用 — 对象的相互引用在对象图中也非常常见，如果对象之间的引用都是强引用，循环引用会导致多个对象的计数器都不会归零，最终会造成内存泄漏；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">递归回收是使用引用计数时不得不面对的问题，我们很难在工程上解决该问题；不过使用引用计数的编程语言却可以利用弱引用来解决循环引用的问题，弱引用也是对象之间的引用关系，<strong>建立和销毁弱引用关系都不会修改双方的引用计数</strong>，这就能避免对象之间的弱引用关系，不过这也需要工程师对引用关系作出额外的并且正确的判断。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="630" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.4603174603174603" src="https://wechat2rss.xlab.app/img-proxy/?k=d5541941&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8ckOxWJ7jTmianouj10G3aeYoFOupfI28f8gpgjibVv2EiclOB9t18UMjQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 19 - 强引用与弱引用</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">除了弱引用之外，一些编程语言也会在引用计数的基础上加入标记清除技术，通过遍历和标记堆中不再被使用的对象解决循环引用的问题。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">引用计数垃圾收集器是一种非移动（Non-moving）的垃圾回收策略，它在回收内存的过程中不会移动已有的对象，很多编程语言都会对工程师直接暴露内存的指针，所以 C、C++ 以及 Objective-C 等编程语言其实都可以使用引用计数来解决内存管理的问题。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>标记清除<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">标记清除（Mark-Sweep）是最简单也最常见的垃圾收集策略，它的执行过程可以分成<strong>标记</strong>和<strong>清除</strong>两个阶段，标记阶段会使用深度优先或者广度优先算法扫描堆中的存活对象，而清除阶段会回收内存中的垃圾。当我们使用该策略回收垃圾时，它会首先从根节点出发沿着对象的引用遍历堆中的全部对象，能够被访问到的对象是存活的对象，不能被访问到的对象就是内存中的垃圾。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如下图所示，内存空间中包含多个对象，我们从根对象出发依次遍历对象的子对象并将从根节点可达的对象都标记成存活状态，即 A、C 和 D 三个对象，剩余的 B、E 和 F 三个对象因为从根节点不可达，所以会被当做垃圾：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.24166666666666667" src="https://wechat2rss.xlab.app/img-proxy/?k=40b9d55a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8mX0ynq459coLt0pyxTygm1cqxutZyPBdq24icFf9ibM0vy2iahmJSbB8A%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 20 - 标记清除的标记阶段</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">标记阶段结束后会进入清除阶段，在该阶段中收集器会依次遍历堆中的所有对象，释放其中没有被标记的 B、E 和 F 三个对象并将新的空闲内存空间以链表的结构串联起来，方便内存分配器的使用。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.24166666666666667" src="https://wechat2rss.xlab.app/img-proxy/?k=e2c2dda9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8zIFcjibEHRV5cNTh8Dx48J2RmXlFg2AOqg58shia5PibPm8DVPMricuWXQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 21 - 标记清除的收集阶段</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">使用标记清除算法的编程语言需要在对象头中加入表示对象存活的标记位（Mark Bit），标记位与操作系统的写时复制不兼容，因为即使内存页中的对象没有被修改，垃圾收集器也会修改内存页中对象相邻的标记位导致内存页的复制。我们可以使用位图（Bitmap）标记避免这种情况，表示对象存活的标记与对象分别存储，清理对象时也只需要遍历位图，能够降低清理过程的额外开销。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如上图所示，使用标记清除算法的垃圾收集器一般会使用基于空闲链表的分配器，因为对象在不被使用时会被就地回收，所以长时间运行的程序会出现很多内存碎片，这会降低内存分配器的分配效率，在实现上我们可以将空闲链表按照对象大小分成不同的区以减少内存中的碎片。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">标记清除策略是一种实现简单的垃圾收集策略，但是它的内存碎片化问题也比较严重，简单的内存回收策略也增加了内存分配的开销和复杂度，当用户程序申请内存时，我们也需要在内存中找到足够大的块分配内存。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>标记压缩<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">标记压缩（Mark-Compact）也是比较常见的垃圾收集算法，与标记清除算法类似，标记压缩的执行过程可以分成<strong>标记</strong>和<strong>压缩</strong>两个阶段。该算法在标记阶段也会从根节点遍历对象，查找并标记所有存活的对象；在压缩阶段，我们会将所有存活的对象紧密排列，『挤出』存活对象之间的缝隙：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.425" src="https://wechat2rss.xlab.app/img-proxy/?k=31a245f1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8nRibOibvamc4oZSlA4TpuGZoLWy1ib7b6qgGEAl9boo7Hjhu3a3WVVnicw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 22 - 标记压缩算法</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为在压缩阶段我们需要移动存活的对象，所以这一种 moving 收集器，如果编程语言支持使用指针访问对象，那么我们就无法使用该算法。标记的过程相对比较简单，我们在这里以 Lisp 2 压缩算法为例重点介绍该算法的压缩阶段：</p><ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">计算当前对象迁移后的最终位置并将位置存储在转发地址（Forwarding Address）中；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">根据当前对象子对象的转发地址，将引用指向新的位置；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">将所有存活的对象移动到对象头中转发地址的位置；</section></li></ol><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">从上述过程我们可以看出，使用标记压缩算法的编程语言不仅要在对象头中存储标记位，还需要存储当前对象的转发地址，这增加了对象在内存中的额外开销。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">标记压缩算法的实现比较复杂，在执行的过程中需要遍历三次堆中的对象，作为 moving 的垃圾收集器，它不适用于 C、C++ 等编程语言；压缩算法的引入可以减少程序中的内存碎片，我们可以直接使用最简单的线性分配器为用户程序快速分配内存。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>复制垃圾回收<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">复制垃圾回收（Copying GC）也是跟踪垃圾收集器的一种，它会将应用程序的堆分成两个大小相等的区域，如下图所示，其中左侧区域负责为用户程序分配内存空间，而右侧区域用于垃圾回收。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.25" src="https://wechat2rss.xlab.app/img-proxy/?k=04631700&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG87Fr7RBUezoichrXPdyB5uKK8oGruPTplrARh08bX0glCn0O3vVH8icFQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 23 - 复制垃圾回收</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当用户程序使用的内存超过上图中的左侧区域就会出现内存不足（Out-of memory、OOM），垃圾收集器在这时会开启新的垃圾收集循环，复制垃圾回收的执行过程可以非常以下的四个阶段：</p><ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">复制阶段 — 从 GC 根节点出发遍历内存中的对象，将发现的存活对象迁移到右侧的内存中；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">转发阶段 — 在原始对象的对象头或者在原位置设置新对象的转发地址（Forwarding Address），如果其他对象引用了该对象可以从转发地址转到新的地址；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">修复指针 — 遍历当前对象持有的引用，如果引用指向了左侧堆中的对象，回到第一步迁移发现的新对象；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">交换阶段 — 当内存中不存在需要迁移的对象之后，交换左右两侧的内存区域；</section></li></ol><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.425" src="https://wechat2rss.xlab.app/img-proxy/?k=4722ffac&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8jM3EiaXWbwRFD6bBibzDw4k2ZPb9IES4LYkA54YNMrZJkyF5Sia4Mm4Ug%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 24 - 复制垃圾回收的复制阶段</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如上图所示，当我们把 A 对象复制到右侧的区域后，会将原始的 A 对象指向新的 A 对象，这样其他引用 A 的对象可以快速找到它的新地址；因为 A 对象的复制是『像素级复制』，所以 A 对象仍然会指向左侧内存的 C 对象，这时需要将 C 对象复制到新的内存区域并修改 A 对象的指针。在最后，当不存在需要拷贝的对象时，我们可以直接交换两个内存区域的指针。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">复制垃圾回收与标记压缩算法一样都会拷贝对象，能够减少程序中的内存碎片，我们可以使用线性的分配器快速为用户程序分配内存。因为只需要扫描一半的堆，遍历堆的次数也会减少，所以可以减少垃圾回收的时间，但是这也会降低内存的利用率。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>高级垃圾回收</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">内存管理是一个相对比较大的话题，我们在上一小节介绍了垃圾回收的一些基本概念，其中包括常见的垃圾回收算法：引用计数、标记清除、标记压缩和复制垃圾回收，这些算法都是比较基本的垃圾回收算法，我们在这一节中将详细介绍一些高级的垃圾回收算法，它们会利用基本的垃圾回收算法和新的数据结构构建更复杂的收集器。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>分代垃圾收集器<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">分代垃圾回收（Generational garbage collection）是在生产环境中比较常见的垃圾收集算法，该算法主要建立在弱分代假设（Weak Generational Hypothesis）上 —— 大多数的对象会在生成后马上变成垃圾，只有极少数的对象可以存活很久[^5]。根据该经验，分代垃圾回收会把堆中的对象分成多个代，不同代垃圾回收的触发条件和算法都完全不同。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.25" src="https://wechat2rss.xlab.app/img-proxy/?k=6ad16ca6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8Caz0e5Q84PA1OsKQdmNibkQetzCAPL2skfCRYbzP36hhMiczxLqSnd0w%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 25 - 青年代和老年代</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">常见的分代垃圾回收会将堆分成青年代（Young、Eden）和老年代（Old、Tenured），所有的对象在刚刚初始化时都会进入青年代，而青年代触发 GC 的频率也更高；而老年代的对象 GC 频率相对比较低，只有青年代的对象经过多轮 GC 没有被释放才可能被晋升（Promotion）到老年代，晋升的过程与复制垃圾回收算法的执行过程相差无几。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">青年代的垃圾回收被称作是 Minor GC 循环，而老年代的垃圾回收被称作 Major GC 循环，Full GC 循环一般是指整个堆的垃圾回收，需要注意的是很多时候我们都会混淆 Major GC 循环和 Full GC 循环，在讨论时一定要先搞清楚双方对这些名词的理解是否一致。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">青年代的垃圾回收只会扫描整个堆的一部分，这能够减少一次垃圾回收需要的扫描的堆大小和程序的暂停时间，提高垃圾回收的吞吐量。然而分代也为垃圾回收引入了复杂度，其中最常见的问题是<em>跨代引用（Intergenerational Pointer）</em>，即老年代引用了青年代的对象，如果堆中存在跨代引用，那么在 Minor GC 循环中我们不仅应该遍历垃圾回收的根对象，还需要从包含跨代引用的对象出发标记青年代中的对象。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.25" src="https://wechat2rss.xlab.app/img-proxy/?k=b0c365b5&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8fnlly1JtWkh9LEljic2I0jIepGVOVGG7dsjdHYuSgtUCLFlcwPics0tQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 26 - 跨代引用</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">为了处理分代垃圾回收的跨代引用，我们需要解决两个问题，分别是如何<strong>识别</strong>堆中的跨代引用以及如何<strong>存储</strong>识别的跨代引用，在通常情况下我们会使用*写屏障（Write Barrier）<em>识别跨代引用并使用</em>卡表（Card Table）*存储相关的数据。</p><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">注意：卡表只是标记或者存储跨代引用的一种方式，除了卡表我们也可以使用记录集（Record Set）存储跨代引用的老年代对象或者使用页面标记按照操作系统内存页的维度标记老年代的对象。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">写屏障是当对象之间的指针发生改变时调用的代码片段，这段代码会判断该指针是不是从老年代对象指向青年代对象的跨代引用。如果该指针是跨代引用，我们会在如下所示的卡表中标记老年代对象所在的区域：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.275" src="https://wechat2rss.xlab.app/img-proxy/?k=372803d2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8iaz0ZS9m3twFqE1B43mDib5icqUWvtow6bicz2weom2TbWH1UZcmj770KQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 27 - 卡表</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">卡表与位图比较相似，它也由一系列的比特位组成，其中每一个比特位都对应着老年区中的一块内存，如果该内存中的对象存在指向青年代对象的指针，那么这块内存在卡表中就会被标记，当触发 Minor GC 循环时，除了从根对象遍历青年代堆之外，我们还会从卡表标记区域内的全部老年代对象开始遍历青年代。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">分代垃圾回收基于弱分代假说，结合了复制垃圾回收、写屏障以及卡表等技术，将内存中的堆区分割成了青年代和老年代等区域，为不同的代使用不同的内存分配和垃圾回收算法，可以有效地减少 GC 循环遍历的堆大小和处理时间，但是写屏障技术也会带了额外开销，移动收集器的特性也使它无法在 C、C++ 等编程语言中使用，在部分场景下弱分代假说不一定会成立，如果大多数的对象都会活得很久，那么使用分代垃圾回收可能会起到反效果。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>标记区域收集器<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">标记区域收集器（Mark-Region Garbage Collector）是 2008 年提出的垃圾收集算法[^6]，这个算法也被称作混合垃圾回收（Immix GC），它结合了标记清除和复制垃圾回收算法，我们使用前者来追踪堆中的存活对象，使用后者减少内存中存在的碎片。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.39166666666666666" src="https://wechat2rss.xlab.app/img-proxy/?k=a272a2cb&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8fcOALAus2ZLFMz4iaa9uN0NdN0gQ3kicQyDBKx8GSGSqUoOgNWBiciabhA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 28 - 标记区域收集器</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Immix 垃圾回收算法包含两个组件，分别是用于标记区域的收集器和去碎片化机制[^7]。标记区域收集器与标记清除收集器比较类似，它将堆内存拆分成特定大小的内存块，再将所有的内存块拆分成特定大小的线。当用户程序申请内存时，它会在上述内存块中查找空闲的线并使用线性分配器快速分配内存；通过引入粗粒度的内存块和细粒度的线，可以更好地控制内存的分配和释放。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.4666666666666667" src="https://wechat2rss.xlab.app/img-proxy/?k=4b551e54&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8tagYDCticUmpJ4a7TlARxBmU7ajRCDTxsKWs9vMX5Kl2l8gstbybNPg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 29 - 线性分配器的光标</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">标记区域收集器与标记清除收集器比较类似，因为它们不会移动对象，所以都会面临内存碎片化的问题。如下图所示，标记区域收集器在回收内存时都是以块和线为单位进行回收的，所以只要当前内存线中包含存活对象，收集器就会保留该片内存区域，这会带来我们在上面提到的内存碎片。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Immix 引入的机会转移（Opportunistic Evacuation）机制能够有效地减少程序中的碎片化，当收集器在内存块中遇到可以被转移的对象，它就会使用复制垃圾回收算法将当前块中的存活对象移动到新的块中并释放原块中的内存。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">标记区域收集器将堆内存分成了粗粒度的内存块和细粒度的内存线，结合了标记清除算法和复制垃圾回收几种基本垃圾收集器的特性，既能够提升垃圾收集器的吞吐量，还能够利用线性分配器提高内存的分配速度，但是该收集器的实现相对比较复杂。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>增量并发收集器<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">相信很多人对垃圾收集器的印象都是暂停程序（Stop the world，STW），随着用户程序申请越来越多的内存，系统中的垃圾也逐渐增多；当程序的内存占用达到一定阈值时，整个应用程序就会全部暂停，垃圾收集器会扫描已经分配的所有对象并回收不再使用的内存空间，当这个过程结束后，用户程序才可以继续执行。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">传统的垃圾收集算法会在垃圾收集的执行期间暂停应用程序，一旦触发垃圾收集，垃圾收集器就会抢占 CPU 的使用权占据大量的计算资源以完成标记和清除工作，然而很多追求实时的应用程序无法接受长时间的 STW。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.30833333333333335" src="https://wechat2rss.xlab.app/img-proxy/?k=617cc376&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8ruYkuGibCmyRmUZSChT5oKDEPY1KfoOx6eYcX81XzDxzaqTYEkucibvw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 30 - 垃圾收集与暂停程序</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">远古时代的计算资源还没有今天这么丰富，今天的计算机往往都是多核的处理器，垃圾收集器一旦开始执行就会浪费大量的计算资源，为了减少应用程序暂停的最长时间和垃圾收集的总暂停时间，我们会使用下面的策略优化现代的垃圾收集器：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">增量垃圾收集 — 增量地标记和清除垃圾，降低应用程序暂停的最长时间；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">并发垃圾收集 — 利用多核的计算资源，在用户程序执行时并发标记和清除垃圾；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为增量和并发两种方式都可以与用户程序交替运行，所以我们需要<strong>使用屏障技术</strong>保证垃圾收集的正确性；与此同时，应用程序也不能等到内存溢出时触发垃圾收集，因为当内存不足时，应用程序已经无法分配内存，这与直接暂停程序没有什么区别，增量和并发的垃圾收集需要提前触发并在内存不足前完成整个循环，避免程序的长时间暂停。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">增量式（Incremental）的垃圾收集是减少程序最长暂停时间的一种方案，它可以将原本时间较长的暂停时间切分成多个更小的 GC 时间片，虽然从垃圾收集开始到结束的时间更长了，但是这也减少了应用程序暂停的最大时间：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.30833333333333335" src="https://wechat2rss.xlab.app/img-proxy/?k=f472bee1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8pgmaZXezJ4IwoDSFMeiavib6Xj1pLroBzWolKQRQbMq0o2cQl7uymXrw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 31 - 增量垃圾收集器</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">需要注意的是，增量式的垃圾收集需要与三色标记法一起使用，为了保证垃圾收集的正确性，我们需要在垃圾收集开始前打开写屏障，这样用户程序对内存的修改都会先经过写屏障的处理，保证了堆内存中对象关系的强三色不变性或者弱三色不变性。虽然增量式的垃圾收集能够减少最大的程序暂停时间，但是增量式收集也会增加一次 GC 循环的总时间，在垃圾收集期间，因为写屏障的影响用户程序也需要承担额外的计算开销，所以增量式的垃圾收集也不是只有优点的。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">并发（Concurrent）的垃圾收集不仅能够减少程序的最长暂停时间，还能减少整个垃圾收集阶段的时间，通过开启读写屏障、<strong>利用多核优势与用户程序并行执行</strong>，并发垃圾收集器确实能够减少垃圾收集对应用程序的影响：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.30833333333333335" src="https://wechat2rss.xlab.app/img-proxy/?k=c8aa99ca&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8bqTqP9ouA3UtmSYzz1Ur7oeLJxRInTpUricXDl9xuSCKIe8I4ujHc0A%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 32 - 并发垃圾收集器</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然并发收集器能够与用户程序一起运行，但是并不是所有阶段都可以与用户程序一起运行，部分阶段还是需要暂停用户程序的，不过与传统的算法相比，并发的垃圾收集可以将能够并发执行的工作尽量并发执行；当然，因为读写屏障的引入，并发的垃圾收集器也一定会带来额外开销，不仅会增加垃圾收集的总时间，还会影响用户程序，这是我们在设计垃圾收集策略时必须要注意的。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">但是因为增量并发收集器的并发标记阶段会与用户程序一同或者交替运行，所以可能出现<strong>标记为垃圾的对象被用户程序中的其他对象重新引用</strong>，当垃圾回收的标记阶段结束后，被错误标记为垃圾的对象会被直接回收，这就会带来非常严重的问题，想要解决增量并发收集器的这个问题，我们需要了解三色抽象和屏障技术。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>三色抽象<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">为了解决原始标记清除算法带来的长时间 STW，多数现代的追踪式垃圾收集器都会实现三色标记算法的变种以缩短 STW 的时间。三色标记算法将程序中的对象分成白色、黑色和灰色三类[^8]：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">白色对象 — 潜在的垃圾，其内存可能会被垃圾收集器回收；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">黑色对象 — 活跃的对象，包括不存在任何引用外部指针的对象以及从根对象可达的对象；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">灰色对象 — 活跃的对象，因为存在指向白色对象的外部指针，垃圾收集器会扫描这些对象的子对象；</section></li></ul><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.23333333333333334" src="https://wechat2rss.xlab.app/img-proxy/?k=d25a0793&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8GXaK7vF8GiaudEJl11bHJbkMLqQGrUTkU0o3QHA68Rv3QlUvgV8rRzA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 33 - 三色的对象</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在垃圾收集器开始工作时，程序中不存在任何的黑色对象，垃圾收集的根对象会被标记成灰色，垃圾收集器只会从灰色对象集合中取出对象开始扫描，当灰色集合中不存在任何对象时，标记阶段就会结束。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.7833333333333333" src="https://wechat2rss.xlab.app/img-proxy/?k=aab70852&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8fMrSUhku7HqOBWZfnFicM5ZIXeBvjgt3qJgicGz5v72Z8OIusibkVxHFQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 34 - 三色标记垃圾收集器的执行过程</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">三色标记垃圾收集器的工作原理很简单，我们可以将其归纳成以下几个步骤：</p><ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">从灰色对象的集合中选择一个灰色对象并将其标记成黑色；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">将黑色对象指向的所有对象都标记成灰色，保证该对象和被该对象引用的对象都不会被回收；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">重复上述两个步骤直到对象图中不存在灰色对象；</section></li></ol><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当三色的标记清除的标记阶段结束之后，应用程序的堆中就不存在任何的灰色对象，我们只能看到黑色的存活对象以及白色的垃圾对象，垃圾收集器可以回收这些白色的垃圾，下面是使用三色标记垃圾收集器执行标记后的堆内存，堆中只有对象 D 为待回收的垃圾：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.43333333333333335" src="https://wechat2rss.xlab.app/img-proxy/?k=56568a68&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8dYxjWP3g9x3yjn8f6dal7AGvkyWGw4rbUPVW1Fow7ZmXibxIcjFAqEA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 35 - 三色标记后的堆</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为用户程序可能在标记执行的过程中修改对象的指针，所以三色标记清除算法本身是不可以并发或者增量执行的，它仍然需要 STW，在如下所示的三色标记过程中，用户程序建立了从 A 对象到 D 对象的引用，但是因为程序中已经不存在灰色对象了，所以 D 对象会被垃圾收集器错误地回收。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.43333333333333335" src="https://wechat2rss.xlab.app/img-proxy/?k=5dd8f6da&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8tuicMAAFQE8Kw8qY40aryUDjGpuicmHraIIFvSYlncpiaxiaM9ANf9a7Cg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 36 - 三色标记与用户程序</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">本来不应该被回收的对象却被回收了，这在内存管理中是非常严重的错误，我们将这种错误成为悬挂指针，即指针没有指向特定类型的合法对象，影响了内存的安全性[^9]，想要并发或者增量地标记对象还是需要使用屏障技术。</p><h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>垃圾回收屏障<span style="display: none;"></span></h4><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">内存屏障技术是一种屏障指令，它可以让 CPU 或者编译器在执行内存相关操作时遵循特定的约束，目前的多数的现代处理器都会乱序执行指令以最大化性能，但是该技术能够保证代码对内存操作的顺序性，在内存屏障前执行的操作一定会先于内存屏障后执行的操作[^10]。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">想要在并发或者增量的标记算法中保证正确性，我们需要达成以下两种三色不变性（Tri-color invariant）中的任意一种：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">强三色不变性 — 黑色对象不会指向白色对象，只会指向灰色对象或者黑色对象；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">弱三色不变性 — 黑色对象指向的白色对象必须包含一条从灰色对象经由多个白色对象的可达路径[^11]；</section></li></ul><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.4583333333333333" src="https://wechat2rss.xlab.app/img-proxy/?k=cdc6a808&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8kmjRaQHyich19LRQ35r4d6XRiacJVXOnYfEJiatj9jnY7iaHoJlJRicL6jQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 37 - 三色不变性</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上图分别展示了遵循强三色不变性和弱三色不变性的堆内存，遵循上述两个不变性中的任意一个，我们都能保证垃圾收集算法的正确性，而屏障技术就是在并发或者增量标记过程中保证三色不变性的重要技术。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">垃圾收集中的屏障技术更像是一个钩子方法，它是在用户程序读取对象、创建新对象以及更新对象指针时执行的一段代码，根据操作类型的不同，我们可以将它们分成读屏障（Read barrier）和写屏障（Write barrier）两种，因为读屏障需要在读操作中加入代码片段，对用户程序的性能影响很大，所以编程语言往往都会采用写屏障保证三色不变性。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们在这里想要介绍的是以下几种写屏障技术，分别是 Dijkstra 提出的插入写屏障[^12]和 Yuasa 提出的删除写屏障[^13]，这里会分析它们如何保证三色不变性和垃圾收集器的正确性。</p><h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;"><span style="display: none;"></span>插入写屏障<span style="display: none;"></span></h5><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Dijkstra 在 1978 年提出了插入写屏障，通过如下所示的写屏障，用户程序和垃圾收集器可以在交替工作的情况下保证程序执行的正确性：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bppvQaK0RXusGusxGmOJsG8wPFciafzSjLB7uu8kk6jjJYlJzWAkBOPayNHnQcGoKE1ictKg6zicUqOw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;">writePointer(slot, ptr):<br/>    shade(ptr)<br/>    *field = ptr<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上述插入写屏障的伪代码非常好理解，每当我们执行类似 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">*slot = ptr</code> 的表达式时，我们会执行上述写屏障通过 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">shade</code> 函数尝试改变指针的颜色。如果 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">ptr</code> 指针是白色的，那么该函数会将该对象设置成灰色，其他情况则保持不变。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.375" src="https://wechat2rss.xlab.app/img-proxy/?k=be8cfaf3&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG82e8OYaQdibKNgFR5TNjFM9tN7z6vJSy7FibbOBAD7ErH2NWf2SDmEkkg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 38 - Dijkstra 插入写屏障</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">假设我们在应用程序中使用 Dijkstra 提出的插入写屏障，在一个垃圾收集器和用户程序交替运行的场景中会出现如上图所示的标记过程：</p><ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">垃圾收集器将根对象指向 A 对象标记成黑色并将 A 对象指向的对象 B 标记成灰色；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">用户程序修改 A 对象的指针，将原本指向 B 对象的指针指向 C 对象，这时触发写屏障将 C 对象标记成灰色；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">垃圾收集器依次遍历程序中的其他灰色对象，将它们分别标记成黑色；</section></li></ol><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Dijkstra 的插入写屏障是一种相对保守的屏障技术，它会将<strong>有存活可能的对象都标记成灰色</strong>以满足强三色不变性。在如上所示的垃圾收集过程中，实际上不再存活的 B 对象最后没有被回收；而如果我们在第二和第三步之间将指向 C 对象的指针改回指向 B，垃圾收集器仍然认为 C 对象是存活的，这些被错误标记的垃圾对象只有在下一个循环才会被回收。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">插入式的 Dijkstra 写屏障虽然实现非常简单并且也能保证强三色不变性，但是它也有很明显的缺点。因为栈上的对象在垃圾收集中也会被认为是根对象，所以为了保证内存的安全，Dijkstra 必须为栈上的对象增加写屏障或者在标记阶段完成重新对栈上的对象对象进行扫描，这两种方法各有各的缺点，前者会大幅度增加写入指针的额外开销，后者重新扫描栈对象时需要暂停程序，垃圾收集算法的设计者需要在这两者之前做出权衡。</p><h5 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;"><span style="display: none;"></span>删除写屏障<span style="display: none;"></span></h5><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Yuasa 在 1990 年的论文 Real-time garbage collection on general-purpose machines 中提出了删除写屏障，因为一旦该写屏障开始工作，它就会保证开启写屏障时堆上所有对象的可达，所以也被称作快照垃圾收集（Snapshot GC）[^14]：</p><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">This guarantees that no objects will become unreachable to the garbage collector traversal all objects which are live at the beginning of garbage collection will be reached even if the pointers to them are overwritten.</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">该算法会使用如下所示的写屏障保证增量或者并发执行垃圾收集时程序的正确性：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bppvQaK0RXusGusxGmOJsG8wPFciafzSjLB7uu8kk6jjJYlJzWAkBOPayNHnQcGoKE1ictKg6zicUqOw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;">writePointer(slot, ptr)<br/>    shade(*slot)<br/>    *slot = ptr<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上述代码会在老对象的引用被删除时，将白色的老对象涂成灰色，这样删除写屏障就可以保证弱三色不变性，老对象引用的下游对象一定可以被灰色对象引用。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.375" src="https://wechat2rss.xlab.app/img-proxy/?k=9d0c09d9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bppvQaK0RXusGusxGmOJsG8AZiabGZGnz8hDexaGy1oBGwQ4BWLvhxfdRCqPMb39VEKpE04A7pNVKA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 39 - Yuasa 删除写屏障</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">假设我们在应用程序中使用 Yuasa 提出的删除写屏障，在一个垃圾收集器和用户程序交替运行的场景中会出现如上图所示的标记过程：</p><ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">垃圾收集器将根对象指向 A 对象标记成黑色并将 A 对象指向的对象 B 标记成灰色；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">用户程序将 A 对象原本指向 B 的指针指向 C，触发删除写屏障，但是因为 B 对象已经是灰色的，所以不做改变；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><strong style="color: black;">用户程序将 B 对象原本指向 C 的指针删除，触发删除写屏障，白色的 C 对象被涂成灰色</strong>；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">垃圾收集器依次遍历程序中的其他灰色对象，将它们分别标记成黑色；</section></li></ol><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上述过程中的第三步触发了 Yuasa 删除写屏障的着色，因为用户程序删除了 B 指向 C 对象的指针，所以 C 和 D 两个对象会分别违反强三色不变性和弱三色不变性：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">强三色不变性 — 黑色的 A 对象直接指向白色的 C 对象；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">弱三色不变性 — 垃圾收集器无法从某个灰色对象出发，经过几个连续的白色对象访问白色的 C 和 D 两个对象；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Yuasa 删除写屏障通过对 C 对象的着色，保证了 C 对象和下游的 D 对象能够在这一次垃圾收集的循环中存活，避免发生悬挂指针以保证用户程序的正确性。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">内存管理在今天仍然是十分重要的话题，当我们在讨论编程语言的性能和便利程度时，内存管理机制都是绕不开的。编程语言在设计内存管理机制时，往往需要在手动管理和自动管理之间进行抉择，现代的大多数编程语言为了减少工程师的负担，多数都会选择使用垃圾回收的方式自动管理内存，但是也有少数编程语言通过手动管理追求极致的性能。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">想要在一篇文章中详尽展示内存管理的方方面面是不可能的，我们可能需要一本书或者几本书的厚度才能详细地展示内存管理的相关技术，这里更多侧重的还是垃圾回收，Rust 的所有权、生命周期以及 C++ 的智能指针等机制在文章中都没有提及，感兴趣的读者可以自行了解。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: Wikipedia: Static variable <a href="https://en.wikipedia.org/wiki/Static_variable" target="_blank">https://en.wikipedia.org/wiki/Static_variable</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^2]: Wikipedia: Stack-based memory allocation <a href="https://en.wikipedia.org/wiki/Stack-based_memory_allocation" target="_blank">https://en.wikipedia.org/wiki/Stack-based_memory_allocation</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^3]: Wikipedia: Garbage collection (computer science) <a href="https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)" target="_blank">https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^4]: Wikipedia: Garbage (computer science) <a href="https://en.wikipedia.org/wiki/Garbage_(computer_science)" target="_blank">https://en.wikipedia.org/wiki/Garbage_(computer_science)</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^5]: Garbage Collection in Java (1) - Heap Overview <a href="http://insightfullogic.com/2013/Feb/20/garbage-collection-java-1/" target="_blank">http://insightfullogic.com/2013/Feb/20/garbage-collection-java-1/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^6]: Immix: A Mark-Region Garbage Collector with Space Efficiency, Fast Collection, and Mutator Performance. Stephen M. Blackburn. Kathryn S. McKinley. 2008. <a href="http://www.cs.utexas.edu/users/speedway/DaCapo/papers/immix-pldi-2008.pdf" target="_blank">http://www.cs.utexas.edu/users/speedway/DaCapo/papers/immix-pldi-2008.pdf</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^7]: The CS 6120 Course Blog. Siqiu Yao. 2019. <a href="https://www.cs.cornell.edu/courses/cs6120/2019fa/blog/immix/" target="_blank">https://www.cs.cornell.edu/courses/cs6120/2019fa/blog/immix/</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^8]: &#34;Tri-color marking&#34; <a href="https://en.wikipedia.org/wiki/Tracing_garbage_collection#Tri-color_marking" target="_blank">https://en.wikipedia.org/wiki/Tracing_garbage_collection#Tri-color_marking</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^9]: &#34;Dangling pointer&#34; <a href="https://en.wikipedia.org/wiki/Dangling_pointer" target="_blank">https://en.wikipedia.org/wiki/Dangling_pointer</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^10]: &#34;Wikpedia: Memory barrier&#34; <a href="https://en.wikipedia.org/wiki/Memory_barrier" target="_blank">https://en.wikipedia.org/wiki/Memory_barrier</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^11]: P. P. Pirinen. Barrier techniques for incremental tracing. In ACM SIGPLAN Notices, 34(3), 20–25, October 1998. <a href="https://dl.acm.org/doi/10.1145/301589.286863" target="_blank">https://dl.acm.org/doi/10.1145/301589.286863</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^12]: E. W. Dijkstra, L. Lamport, A. J. Martin, C. S. Scholten, and E. F. Steffens. On-the-fly garbage collection: An exercise in cooperation. Communications of the ACM, 21(11), 966–975, 1978. <a href="https://www.cs.utexas.edu/users/EWD/transcriptions/EWD05xx/EWD520.html" target="_blank">https://www.cs.utexas.edu/users/EWD/transcriptions/EWD05xx/EWD520.html</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^13]: T. Yuasa. Real-time garbage collection on general-purpose machines. Journal of Systems and Software, 11(3):181–198, 1990. <a href="https://www.sciencedirect.com/science/article/pii/016412129090084Y" target="_blank">https://www.sciencedirect.com/science/article/pii/016412129090084Y</a></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^14]: Paul R Wilson. &#34;Uniprocessor Garbage Collection Techniques&#34; <a href="https://www.cs.cmu.edu/~fp/courses/15411-f14/misc/wilson94-gc.pdf" target="_blank">https://www.cs.cmu.edu/~fp/courses/15411-f14/misc/wilson94-gc.pdf</a></p></section><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;">推荐阅读</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484172&amp;idx=1&amp;sn=f4094370ae5e56160dd9a57451daea29&amp;chksm=fe795c07c90ed5118d46e2ffff6357e4bc78b97f69676f1b7eb6ae624bae9a9ed9e7667800fa&amp;scene=21#wechat_redirect" data-itemshowtype="0" tab="innerlink" data-linktype="2">调度系统设计精要</a><br/></section></li></ul><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;letter-spacing: 0px;text-align: left;background-color: rgb(255, 255, 255);line-height: 1.6;word-break: break-word;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;color: rgb(0, 0, 0);font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p><p><br/></p>



<p><a href="https://draveness.me/system-design-memory-management/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=ac0ddbf4&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484786%26idx%3D1%26sn%3Dcbc7790b6a90e380cd6e0f39431a2ca5%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 02 Mar 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>如何保证数据中心服务器的时间一致 · OSDI 2020</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484737&amp;idx=1&amp;sn=f19f53c5652b5956398fb028651a1427</link>
      <description></description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-02-23 08:30</span> <span style="display: inline-block;"></span>
</p>

<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=f7685d96&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bo6CpvhJF65qBH0YB8k3ibOicR9LmFwqBbicf4oyyAJ89Ycox8WWod8Zice6PQDueTEWyKtgibMmwXOJzg%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">『看看论文』是一系列分析计算机和软件工程领域论文的文章，我们在这个系列的每一篇文章中都会阅读一篇来自 OSDI、SOSP 等顶会中的论文，这里不会事无巨细地介绍所有的细节，而是会筛选论文中的关键内容，如果你对相关的论文非常感兴趣，可以直接点击链接阅读原文。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">本文要介绍的是 2020 年 OSDI 期刊中的论文 —— Sundial: Fault-tolerant Clock Synchronization for Datacenters[^1]，该论文实现的 Sundial 可以在数据中心提供高精度的、容错的对时机制。在数据中心发生故障时，它也能够保证不同服务器的绝对时间差小于 ~100ns，比行业内的其他的系统好一到两个数量级，这里的 ~100ns 也被称为时间不确定性上限（Time-uncertainty Bound）。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们在这里会先介绍论文中提到的时间不确定性上限，在最后会介绍 Sundial 的系统架构，包含对时频率和错误恢复两方面的设计。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>时间不确定性上限</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">时间不确定性上限是个非常有趣的概念，假设我们有以下两台服务器，两台服务器本地的绝对时间包含一定的差异，其中服务器 A 在 X 时间从数据库中使用时间戳 T 读取数据，而服务器 B 在随后的 Y 时间使用时间戳 T-1 向数据库写入数据：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.2916666666666667" src="https://wechat2rss.xlab.app/img-proxy/?k=d6d6b8eb&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bo6CpvhJF65qBH0YB8k3ibOicSaIzSSTSQaVaaY4cXjiawicIicJKlEFias2HAEP0syxRLbiaYEicg407iaZvA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - 时间不确定性上限</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">从整体上看，因为不同服务器绝对时间上的微弱差异，后发生的时间反而使用了更早的时间戳，这就带来了潜在的问题。但是如果我们能够确定不同服务器之间绝对时间的最大差值，让服务器在读取数据时等待一段时间，就可以保证读写操作时序的正确性，这里的等待时间就是上面提到的时间不确定性上限，该值越小，系统的响应速度也就越快。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">很多系统都对时间不确定性上限有要求，例如：分布式的事务数据库、一致性快照、网络遥测、单向延迟和分布式日志等，它的值越小，系统就能提供更好的性能、更快的响应速度和更强的一致性。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>系统架构设计</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Sundial 使用软硬件结合的方式设计，其中包含如下图所示的三个主要组件，它会在硬件中完成同步时间信息、检测系统中错误等重要功能，软件负责计算同步生成树并故障时触发错误恢复机制：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="586" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.6501706484641638" src="https://wechat2rss.xlab.app/img-proxy/?k=b3c00f6e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bo6CpvhJF65qBH0YB8k3ibOicsTj6fAXqScuShWAmlTSU0rxzapHbqkiaNrs5cpYFcDldvZxsr2OI9MQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - Sundial 框架</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Sundial 论文从软件和硬件的维度介绍它的设计与实现原理，不过这里从时钟同步和错误恢复的功能维度介绍该系统的设计，</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>时钟同步<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Sundial 中的时钟同步机制与 SDN 的数据平面与控制平面很相似，其中作为控制平面的控制器会在初始化时计算所有服务器的之间的时钟同步关系以及备选的同步路径，而作为数据面板的服务器会负责同步时间。如下所示，在节点正常工作时，根节点 0 的时间会同步给节点 4 和节点 6，而节点 1 会从节点 4 获取最新的时间：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.3416666666666667" src="https://wechat2rss.xlab.app/img-proxy/?k=05f39539&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bo6CpvhJF65qBH0YB8k3ibOicCPvykibTosVIXibOT6W30BIu56QayXVHdYFWGfk3BpUTJcW1dj54qvicA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - 同步生成树</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为服务器本地用于计时的石英晶体谐振器（Crystal Oscillator）会受数据中心温度、电压变化的影响会变得不再准确，不同时钟的差异会变得越来越大，所以硬件需要每隔 100µs 触发时间同步，同步消息会沿着控制器计算的生成树同步向下传递，一旦收到时间同步消息就会发送给其他节点。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然同步的频率异常频繁并且该数据包的优先级非常高，但是因为每个数据包的大小只有 100 字节，所以这只会占用网络整体带宽的 0.01%，最多只会为其他数据包增加 10ns 的网络延迟。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>错误恢复<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">为了发现同步生成树中的节点错误，Sundial 需要软件和硬件的一同配合，其中硬件会使用超时时间检测上游发送的同步数据包，一旦发生超时或者检测到上游发出的时间不在预期的范围之内，硬件就会触发软件的错误恢复处理机制。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-w="1080" data-type="png" style="display: block;margin-right: auto;margin-left: auto;" data-ratio="0.3416666666666667" src="https://wechat2rss.xlab.app/img-proxy/?k=e8c8cf59&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bo6CpvhJF65qBH0YB8k3ibOictRmiaoKIiaiaBrciaEHPXy8NpgJ3A0eiavaY6SP6ZRnQyBfVWInPhBCrw8g%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">sundial-spanning-tree-and-fault-tolerance</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 4 - 错误恢复</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">正如我们在上一节中提到的，中心化的控制器会为非根节点提供用于错误恢复的备选路径，在计算主要同步路径和备选同步路径时需要遵循多个条件以提高备选路径的可用性，这里就不展开介绍了，熟悉图算法并且感兴趣的读者可以直接阅读论文了解相关内容。上面所有的备选路径都是由中心控制器计算并且存储在硬件本地的，这样在发生故障时可以迅速从本地错误中恢复。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">精确的绝对时间虽然看起来距离我们很远，但是实际上有非常大的作用，Sundial 论文提出的技术能够为数据中心的服务器提供更准确的绝对时间，更准确的事件和更低的时间不确定性上限让 Spanner 数据库的提交等待延迟降低了 70% ~ 80%，中位数从 211µs 降低到了 49µs，99 分位数也降低了 ~550µs：</p><section data-tool="mdnice编辑器" style="overflow-x: auto;"><table><thead><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><th style="border-top-width: 1px;border-color: rgb(204, 204, 204);text-align: left;background-color: rgb(240, 240, 240);min-width: 85px;"><br/></th><th style="border-top-width: 1px;border-color: rgb(204, 204, 204);text-align: left;background-color: rgb(240, 240, 240);min-width: 85px;">Baseline</th><th style="border-top-width: 1px;border-color: rgb(204, 204, 204);text-align: left;background-color: rgb(240, 240, 240);min-width: 85px;">With Sundial</th></tr></thead><tbody style="border-width: 0px;border-style: initial;border-color: initial;"><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">中位数</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;">211µs</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;">49µs</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: rgb(248, 248, 248);"><td style="border-color: rgb(204, 204, 204);min-width: 85px;">99 分位数</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;">784µs</td><td style="border-color: rgb(204, 204, 204);min-width: 85px;">238µs</td></tr></tbody></table></section><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>表 1 - Spanner 提交等待延迟性能提升</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">大多数系统中并不需要用到如此复杂的系统同步时间，我们只需要使用 RFC5905 中的 NTP 协议就可以获得毫秒级精度的时间[^2]，虽然这与纳秒级别的时间有几个数量级的差距，但是在多数场景下都可以满足需求了。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>推荐阅读</h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484604&amp;idx=1&amp;sn=b8a820ba9f227e0a5c82f4d48e71ac0c&amp;chksm=fe795bb7c90ed2a1d2074b744736ecf1b1ef3d127e92d1545e62be0feb763ef1db3466e92526&amp;scene=21#wechat_redirect" textvalue="微服务架构的分布式容错 · SOSP 2019" data-itemshowtype="0" tab="innerlink" data-linktype="2">微服务架构的分布式容错 · SOSP 2019</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484432&amp;idx=1&amp;sn=1a557f9e571e1156524e1f81c6e3b134&amp;chksm=fe795b1bc90ed20d17b9d3e7b427d6be3e7f94f50c1ca6e72ef703eab8cb9a629e9df48136a4&amp;scene=21#wechat_redirect" textvalue="处理器感知线程管理系统 · OSDI 2018" data-itemshowtype="0" tab="innerlink" data-linktype="2">处理器感知线程管理系统 · OSDI 2018</a></section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^1]: Yuliang Li, Gautam Kumar, Hema Hariharan, Hassan Wassel, Peter Hochschild, Dave Platt, Simon Sabato, Minlan Yu, Nandita Dukkipati, Prashant Chandra, Amin Vahdat. 2020. Sundial: Fault-tolerant Clock Synchronization for Datacenters. In Proceedings of the 13th USENIX conference on Operating Systems Design and Implementation (OSDI&#39;20). USENIX Association, USA, 1171-1186. </p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">[^2]: RFC 5905 - Network Time Protocol Version 4 - IETF Tools <a href="https://tools.ietf.org/html/rfc5905" target="_blank">https://tools.ietf.org/html/rfc5905</a></p></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;letter-spacing: 0px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);line-height: 1.6;word-break: break-word;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;color: rgb(0, 0, 0);font-size: 16px;text-align: left;white-space: normal;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p>



<p><a href="https://draveness.me/papers-sundial/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=8f40071e&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484737%26idx%3D1%26sn%3Df19f53c5652b5956398fb028651a1427%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 23 Feb 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>为什么 OLAP 需要列式存储</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484728&amp;idx=1&amp;sn=4628e9a1a23720b6771111c14ef77f76</link>
      <description>ClickHouse、Hive 和 HBase 等用于在线分析处理（OLAP）场景的数据存储往往都会使用列式存储，我们来分析下背后的原因</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-02-08 08:30</span> <span style="display: inline-block;"></span>
</p>

<p>ClickHouse、Hive 和 HBase 等用于在线分析处理（OLAP）场景的数据存储往往都会使用列式存储，我们来分析下背后的原因</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=19ceb393&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bpWAbBTueINkwyUtOA0uTM58CeGyEMtko6KAEVcxsXVsuibLLGgK49flt80sylf1REPEicu1yUMDibxQ%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">为什么这么设计（Why’s THE Design）是一系列关于计算机领域中程序设计决策的文章，我们在这个系列的每一篇文章中都会提出一个具体的问题并从不同的角度讨论这种设计的优缺点、对具体实现造成的影响。如果你有想要了解的问题，可以在文章下面留言。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">ClickHouse 是最近比较热门的用于在线分析处理的（OLAP）[^1]数据存储，与我们常见的 MySQL、PostgreSQL 等传统的关系型数据库相比，ClickHouse、Hive 和 HBase 等用于在线分析处理（OLAP）场景的数据存储往往都会使用列式存储。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.24166666666666667" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=a790cd44&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bpWAbBTueINkwyUtOA0uTM5jK425R7o656znJhCI24CBficH2e4kgm6uagIPnWUqicwqu2ia5CceImcA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;">olap-oltp-databases</figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 1 - OLAP 和 OLTP</strong></p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">对数据库稍有了解的读者都知道，在线事务处理（Online Transaction Processing、OLTP）[^2]和在线分析处理（Online Analytical Processing、OLAP）是数据库最常见的两种场景，这两种场景不是唯二的两种，从中衍生出来的还有混合事务分析处理（Hybrid Transactional/Analytical Processing、HTAP）[^3]等概念。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在线事务处理是最常见的场景，在线服务需要为用户实时提供服务，提供服务的过程中可能要查询或者创建一些记录；而在线分析处理的场景需要批量处理用户数据，数据分析师会根据用户产生的数据分析用户行为和画像、产出报表和模型。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">标题中提到的列式存储与传统关系型数据库的行式存储相对应，如下图所示，其中行式存储以数据行或者实体为逻辑单元管理数据，数据行的存储都是连续的，而列式存储以数据列为逻辑单元管理数据，相邻的数据都是具有相同类型的数据。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.35833333333333334" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=b7722654&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bpWAbBTueINkwyUtOA0uTM5yvDH0SSKrEQ0XFiaboO4vAeO0cm5qFmlhyVkicM3DX4Htpc96PaXMxwA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - 行式存储和列式存储</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">既然我们已经了解了标题中提到的两个概念：OLAP 和列式存储，那么接下来将从以下两个方面分析为什么列式存储更适合 OLAP 的场景。</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">列式存储可以满足快速读取特定列的需求，在线分析处理往往需要在上百列的宽表中读取指定列分析；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">列式存储就近存储同一列的数据，使用压缩算法可以得到更高的压缩率，减少存储占用的磁盘空间；</section></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>按需读取</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在线服务需要应对用户发起的增删改查需求，虽然查询的需求往往都是写入请求的几倍、甚至几十倍，但是写操作带来的负责一致性问题成为了在线服务数据存储不得不解决的问题，MySQL 和 PostgreSQL 等使用关系型数据库提供的事务可以提供很好的方案。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">正是因为 OLTP 场景中大多数的操作都是以记录作为单位的，所以将经常被同时使用的数据相邻存储也是很符合逻辑的，但是如果我们将 MySQL 等数据库用于 OLAP 场景，最常见的查询也可能需要遍历整张表中的全部数据。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.275" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=92718c5d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bpWAbBTueINkwyUtOA0uTM5ZHbEVHHR8ibA9PBiaGxeyQBLDJicB84EHe6uUeicLee5dMv1UIEry4IMcw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - 在行式存储获取特定列</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如上图所示，当我们仅需要获取上表中年龄的分布时，也仍然需要读取表中的全部数据并在内存中丢弃不需要的数据行，其中黄色部分都是我们不关心的数据，这浪费了大量的 I/O 和内存资源。虽然我们可以使用辅助索引解决这些问题，但是对于 OLAP 中常见的几十列甚至上百列的宽表就捉襟见肘了。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">列式存储会按列存储数据，这也意味着在读取数据表中的特定列时，我们只需要找到相应内存空间的起始位置，然后读取这片连续的内存空间就可以获得关心的全部数据。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.325" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=25ee0fdf&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bpWAbBTueINkwyUtOA0uTM5WUwn22icB7kC70jMAn380zHv4hCmA4SMmAhMjt1F6pFDiaXrhdviaC8LQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - 在列式存储获取特定列</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">哪怕在几百列的大表中找到几个特定列也不需要遍历整张表，只需要找到列的起始位置就可以快速获取相关的数据，减少了 I/O 和内存资源的浪费，这也是为什么面向列的存储系统更适合在 OLAP 的场景中使用。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>数据压缩</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">因为列式存储将同一列的数据存储在一起，所以使用压缩算法可以得到更高的压缩率，减少存储占用的磁盘空间。压缩算法的基本原理其实很简单，它使用基于特定规则的数据表示原数据，如下所示的字符串中包含连续的相同字符，我们使用最符合直觉的压缩算法就可以减少字符串的长度：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.24166666666666667" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=80a94ac3&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bpWAbBTueINkwyUtOA0uTM5BeCDK2hiaIcX5zJRdpQMDCqnq7jspPVEHpGPtVe4CXOQ4C9r25F0EgQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 5 - 简单的压缩算法</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">上图中所有的黄色方块表示前面字符串的重复次数，这种简单的压缩策略可以在保证无损的情况下将字符串的长度压缩 33%，然而压缩率是由压缩算法和数据的特性共同决定的。与面向行的数据存储相比，面向列的数据存储会将相同类型的数据就近存储，这也给压缩算法的提供了更多发挥的空间。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然压缩算法实际上是一种使用 CPU 时间换取 I/O 时间和空间的策略，但是在多数情况下，这种生意都是稳赚不赔的。压缩算法通过减少数据的大小、减少磁盘的寻道时间提高 I/O 的性能、减少数据的传输时间并提高缓冲区的命中率，节省的 I/O 时间可以轻易补偿它带来的 CPU 额外开销[^4]。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在线分析处理的场景虽然一直都存在，不过随着数字化浪潮的演进，我们也只是在最近才采集到了海量的用户数据。因为过去的系统无法满足今天海量数据的分析和处理需求，所以才出现了为细分场景设计的系统，面向列的存储系统也因为它的以下特性在 OLAP 的场景中焕发了光彩：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">列式存储可以满足快速读取特定列的需求，在线分析处理往往需要在上百列的宽表中读取指定列分析，而传统的行式存储在分析数据时往往需要使用索引或者遍历整张表，带来了非常大的额外开销；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">列式存储就近存储同一列的数据，使用压缩算法可以得到更高的压缩率，减少存储占用的磁盘空间，虽然带来了 CPU 时间的额外开销，但是节省的 I/O 时间比带来的额外开销更多；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">列式存储在 OLAP 的场景中有着种种优势，不过它也不是数据存储中的银弹，仍然有很多缺点，不过在这里就不做讨论了。到最后，我们还是来看一些比较开放的相关问题，有兴趣的读者可以仔细思考一下下面的问题：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">列式存储在 OLTP 的场景中有哪些优点？</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">HTAP 的场景会使用哪种方式存储数据？</section></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>推荐阅读</h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484081&amp;idx=1&amp;sn=2d9e26268b7961203cdfb5f446107d0f&amp;chksm=fe795dbac90ed4ac4a521cd2b00466ae8dbf7095a0108189f6e4d6d1e56ba87fa73709198c48&amp;scene=21#wechat_redirect" textvalue="为什么 MySQL 使用 B+ 树" data-itemshowtype="0" tab="innerlink" data-linktype="2">为什么 MySQL 使用 B+ 树</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484410&amp;idx=1&amp;sn=a3e1b698bb35068250e36a062efa7b8b&amp;chksm=fe795cf1c90ed5e75bc01e42f14b3f3b9fc608d7e17ed9fd16c72cddced040f140456a7327fa&amp;scene=21#wechat_redirect" textvalue="为什么数据库不应该使用外键" data-itemshowtype="0" tab="innerlink" data-linktype="2">为什么数据库不应该使用外键</a></section></li></ul><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">如果对文章中的内容有疑问或者想要了解更多软件工程上一些设计决策背后的原因，可以在博客下面留言，作者会及时回复本文相关的疑问并选择其中合适的主题作为后续的内容。</p></blockquote></section><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p><br/></p><p style="max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p>



<p><a href="https://draveness.me/whys-the-design-olap-column-oriented/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=96cebb0b&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484728%26idx%3D1%26sn%3D4628e9a1a23720b6771111c14ef77f76%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Mon, 08 Feb 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>纳秒级高性能日志系统 · ATC 2018</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484716&amp;idx=1&amp;sn=bdb00764de73ad9745d82a4944271b0f</link>
      <description>打印日志没有那么简单，我们看看如何在纳秒时间尺度下实现日志系统。</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2021-02-02 08:30</span> <span style="display: inline-block;"></span>
</p>

<p>打印日志没有那么简单，我们看看如何在纳秒时间尺度下实现日志系统。</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=54c74cd2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7bpicVEibS8VlARHDgnUCVJicZk0J5Ejmo5DIUL7Y9ibYmPuZo0h6AYPvUw3Xc9m85xpzjftJfrRWcwcwQ%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">『看看论文』是一系列分析计算机和软件工程领域论文的文章，我们在这个系列的每一篇文章中都会阅读一篇来自 OSDI、SOSP 等顶会中的论文，这里不会事无巨细地介绍所有的细节，而是会筛选论文中的关键内容，如果你对相关的论文非常感兴趣，可以直接点击链接阅读原文。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">本文要介绍的是 2018 年 ATC 期刊中的论文 —— NanoLog: A Nanosecond Scale Logging System[^1]，该论文实现的 NanoLog 是高性能的日志系统，与 C++ 社区中的其他日志系统，例如：spdlog、glog 和 Boost Log 相比，它的性能可以高出 1 ~ 2 个数量级，我们在这篇文章中来简要分析 NanoLog 的设计与实现原理。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">日志是系统可观测性的重要一环，相信很多工程师都有线上出问题临时加日志查问题的经历，作者刚刚又重新经历了这一过程，稍有经验的开发者都会在系统中加入很多日志方便生产环境的问题排查，更有经验的开发者会谨慎地在系统中（尤其是低延迟的实时系统）添加日志，因为打印日志这件看起来简单的事情实际上会带来很大的额外开销。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">打印日志是一件简单的事情，几乎所有的工程师在踏上编程之路的第一天起就学会了如何使用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">printf</code> 等函数向标准输出打印字符串，在 99% 的程序中使用这种做法都不会带来性能问题，只是在生产环境中我们会使用结构化的、容易解析的格式替代 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">printf</code>，例如：在日志中加入时间戳、文件名和行数等信息。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.275" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=8f8163d9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bpicVEibS8VlARHDgnUCVJicZkiavxv3XxxEU3CpMBQeomEWspkVibDH9lhHzjicASO7r22jE5J3TUjPXKg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - 常见的日志系统</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">但是剩下 1% 的程序要求超低延迟的系统，它们对响应时间的要求可能是微秒甚至纳秒级别的。在这种场景下，如果仍然需要日志系统提供可观测性，我们需要深入研究向标准输出或者文件写日志的细节，例如：使用缓冲区、异步写入文件以及减少反射等动态特性的使用，除此之外，我们还要保证日志输出的顺序、避免消息丢失或者截断。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">spdlog、glog 和 Boost Log 已经能够满足绝大多数应用程序的需求，但是对于这些延迟机器敏感的应用程序，打印日志需要的几微秒可能会显著地增加请求的处理时间影响程序的性能，而今天分享的 NanoLog 是一个可以在纳秒尺度打印日志的系统。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>架构设计</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">NanoLog 能在毫秒尺度打印日志是因为它能够在编译期间提取静态日志消息并在离线阶段处理日志的格式化等问题。其核心优化都建立在以下两个条件上：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">开发者可直接阅读的格式化日志不一定要在应用程序运行时直接生成，我们可以在运行期间记录日志的动态参数并在运行后按需生成；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">日志消息中的绝大部分信息都是静态冗余的，只有少数参数会发生变化，我们可以在编译期间获取日志的静态内容并仅在后处理器中打印一次；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">正是因为大多数日志都遵循上述特性，所以 NanoLog 可以在它们的基础上实现纳秒尺度的日志打印。NanoLog 的不同设计方式决定了它与传统日志模块会在架构上有很大的差异，它由以下三个组件构成：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.22655794991263833" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1717" src="https://wechat2rss.xlab.app/img-proxy/?k=36c70ac0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bpicVEibS8VlARHDgnUCVJicZkdYPPoIlia96icYLBn4H4daB8PvKAUWygjy5W6OXcmj2fiahqMHYCuMoyA%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - NanoLog 系统架构</strong><br/></figcaption></figure><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">预处理器：在编译期间从源代码提取日志消息、将原始日志语句替换成优化过的代码并为每条日志消息生成压缩、字典辅助函数；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">运行时库：在缓冲区中缓存多个线程打印的日志并使用预处理阶段生成的辅助函数输出压缩后的二进制日志；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">解码器：利用预处理阶段生成的包含静态信息的字典解码日志，获取可以被人类阅读的日志；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">传统的日志系统都仅包含运行时，我们只需要在开发时引入头文件、链接静态库或者动态库并在代码中指定的位置输出日志，日志系统会程序运行期间将日志打印到标准输出或者指定文件。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">但是 NanoLog 打破了上述这种传统的设计，为了减少运行时的开销，我们将一部分操作迁移到了编译期间和运行后，这也遵循<em>工作质量守恒定律</em>：工作不会凭空消失，它只会从运行时转移到程序生命周期的其他阶段，NanoLog 就使用了下面的操作降低了日志系统的开销：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">在编译时重写了日志语句移除静态信息并将昂贵的格式化操作推迟到代码运行后的阶段，这能够在运行时减少大量的计算和 I/O 带宽需求；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">为每个日志消息编译了特定代码，能够高效地处理动态参数，避免在运行时解析日志消息并编码参数类型；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">使用轻量级的压缩算法以及不保序的日志减少运行时的 I/O 和处理时间；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">使用后处理组合压缩的日志数据和编译时提取的静态信息生成可读的日志；</section></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>实现原理</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">这一节我们将简要分析 NanoLog 系统中三大组件，即预处理、运行时和后处理的具体实现原理。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>预处理<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">NanoLog 使用 Python 来实现预处理器，程序的入口在 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">processor/parser</code> 中，它会扫描用户的源文件、生成元数据文件和修改后的源代码，接下来会将这些修改后的代码编译到 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">.so</code> 或者 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">.a</code> 文件中，而不是编译初始的代码。除此之外，预处理器还会读取生成的所有元数据文件、生成 C++ 源代码并编译到 NanoLog 的运行时库和最后的用户程序中：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.325" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=c5bd72ea&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bpicVEibS8VlARHDgnUCVJicZkoDuKAYUk1YjgsfCrKaNgQBM8cljOQIPLT2Lh7uF2AzBbMFHUInCIzQ%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - 预处理器</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">它会为源代码中的每个 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">NANO_LOG</code> 生成两条语句，即 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">record</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">compress</code>，其中前者记录日志中的动态参数，后者压缩记录的数据减少程序的 I/O 时间。下面是预处理器生成的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">record</code> 函数：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bpicVEibS8VlARHDgnUCVJicZkdfHuO9z7I2ytzmwRYiaic2MvibECzQbic0ZZCOhiaAqL1LvWpyf4RGDo3Dg/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="font-weight: bold;line-height: 26px;">inline</span> <span style="font-weight: bold;line-height: 26px;">void</span> __syang0__fl__E32374s3237424642lf__s46cc__100__(NanoLog::LogLevel level, <span style="font-weight: bold;line-height: 26px;">const</span> <span style="font-weight: bold;line-height: 26px;">char</span>* fmtStr , <span style="font-weight: bold;line-height: 26px;">const</span> <span style="font-weight: bold;line-height: 26px;">char</span>* arg0, <span style="font-weight: bold;line-height: 26px;">int</span> arg1, <span style="font-weight: bold;line-height: 26px;">int</span> arg2, <span style="font-weight: bold;line-height: 26px;">double</span> arg3) {<br/>    <span style="font-weight: bold;line-height: 26px;">extern</span> <span style="font-weight: bold;line-height: 26px;">const</span> <span style="font-weight: bold;line-height: 26px;">uint32_t</span> __fmtId__E32374s3237424642lf__s46cc__100__;<br/>    <span style="font-weight: bold;line-height: 26px;">if</span> (level &gt; NanoLog::getLogLevel())<br/>        <span style="font-weight: bold;line-height: 26px;">return</span>;<br/>    <span style="font-weight: bold;line-height: 26px;">uint64_t</span> timestamp = PerfUtils::Cycles::rdtsc();<br/>    <span style="font-weight: bold;line-height: 26px;">size_t</span> str0Len = <span style="color: #008080;line-height: 26px;">1</span> + <span style="color: #0086b3;line-height: 26px;">strlen</span>(arg0);;<br/>    <span style="font-weight: bold;line-height: 26px;">size_t</span> allocSize = <span style="font-weight: bold;line-height: 26px;">sizeof</span>(arg1) + <span style="font-weight: bold;line-height: 26px;">sizeof</span>(arg2) + <span style="font-weight: bold;line-height: 26px;">sizeof</span>(arg3) +  str0Len +  <span style="font-weight: bold;line-height: 26px;">sizeof</span>(NanoLogInternal::Log::UncompressedEntry);<br/>    NanoLogInternal::Log::UncompressedEntry *re = <span style="font-weight: bold;line-height: 26px;">reinterpret_cast</span>&lt;NanoLogInternal::Log::UncompressedEntry*&gt;(NanoLogInternal::RuntimeLogger::reserveAlloc(allocSize));<br/>    re-&gt;fmtId = __fmtId__E32374s3237424642lf__s46cc__100__;<br/>    re-&gt;timestamp = timestamp;<br/>    re-&gt;entrySize = <span style="font-weight: bold;line-height: 26px;">static_cast</span>&lt;<span style="font-weight: bold;line-height: 26px;">uint32_t</span>&gt;(allocSize);<br/>    <span style="font-weight: bold;line-height: 26px;">char</span> *buffer = re-&gt;argData;<br/>    <span style="color: #998;font-style: italic;line-height: 26px;">// Record the non-string arguments</span><br/>    NanoLogInternal::Log::recordPrimitive(buffer, arg1);<br/> NanoLogInternal::Log::recordPrimitive(buffer, arg2);<br/> NanoLogInternal::Log::recordPrimitive(buffer, arg3);<br/>    <span style="color: #998;font-style: italic;line-height: 26px;">// Record the strings (if any) at the end of the entry</span><br/>    <span style="color: #0086b3;line-height: 26px;">memcpy</span>(buffer, arg0, str0Len); buffer += str0Len;*(<span style="font-weight: bold;line-height: 26px;">reinterpret_cast</span>&lt;<span style="color: #0086b3;line-height: 26px;">std</span>::remove_const&lt;<span style="font-weight: bold;line-height: 26px;">typename</span> <span style="color: #0086b3;line-height: 26px;">std</span>::remove_pointer&lt;<span style="font-weight: bold;line-height: 26px;">decltype</span>(arg0)&gt;::type&gt;::type*&gt;(buffer) - <span style="color: #008080;line-height: 26px;">1</span>) = <span style="color: #d14;line-height: 26px;">L&#39;\0&#39;</span>;<br/>    <span style="color: #998;font-style: italic;line-height: 26px;">// Make the entry visible</span><br/>    NanoLogInternal::RuntimeLogger::finishAlloc(allocSize);<br/>}<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">每个函数都包含特定的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">fmtId</code>，日志的压缩和解压也都会用到这里生成的标识符，上述函数还会为参数分配内存空间并按照顺序调用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">recordPrimitive</code> 将所有参数记录到缓冲区中。压缩使用的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">compress</code> 也遵循类似的逻辑：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bpicVEibS8VlARHDgnUCVJicZkdfHuO9z7I2ytzmwRYiaic2MvibECzQbic0ZZCOhiaAqL1LvWpyf4RGDo3Dg/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">inline</span> <span style="font-weight: bold;line-height: 26px;">ssize_t</span><br/><span style="color: #900;font-weight: bold;line-height: 26px;">compressArgs__E32374s3237424642lf__s46cc__100__</span><span style="line-height: 26px;">(NanoLogInternal::Log::UncompressedEntry *re, <span style="font-weight: bold;line-height: 26px;">char</span>* out)</span> </span>{<br/>    <span style="font-weight: bold;line-height: 26px;">char</span> *originalOutPtr = out;<br/>    <span style="color: #998;font-style: italic;line-height: 26px;">// Allocate nibbles</span><br/>    BufferUtils::TwoNibbles *nib = <span style="font-weight: bold;line-height: 26px;">reinterpret_cast</span>&lt;BufferUtils::TwoNibbles*&gt;(out);<br/>    out += <span style="color: #008080;line-height: 26px;">2</span>;<br/>    <span style="font-weight: bold;line-height: 26px;">char</span> *args = re-&gt;argData;<br/>    <span style="color: #998;font-style: italic;line-height: 26px;">// Read back all the primitives</span><br/>    <span style="font-weight: bold;line-height: 26px;">int</span> arg1; <span style="color: #0086b3;line-height: 26px;">std</span>::<span style="color: #0086b3;line-height: 26px;">memcpy</span>(&amp;arg1, args, <span style="font-weight: bold;line-height: 26px;">sizeof</span>(<span style="font-weight: bold;line-height: 26px;">int</span>)); args +=<span style="font-weight: bold;line-height: 26px;">sizeof</span>(<span style="font-weight: bold;line-height: 26px;">int</span>);<br/> <span style="font-weight: bold;line-height: 26px;">int</span> arg2; <span style="color: #0086b3;line-height: 26px;">std</span>::<span style="color: #0086b3;line-height: 26px;">memcpy</span>(&amp;arg2, args, <span style="font-weight: bold;line-height: 26px;">sizeof</span>(<span style="font-weight: bold;line-height: 26px;">int</span>)); args +=<span style="font-weight: bold;line-height: 26px;">sizeof</span>(<span style="font-weight: bold;line-height: 26px;">int</span>);<br/> <span style="font-weight: bold;line-height: 26px;">double</span> arg3; <span style="color: #0086b3;line-height: 26px;">std</span>::<span style="color: #0086b3;line-height: 26px;">memcpy</span>(&amp;arg3, args, <span style="font-weight: bold;line-height: 26px;">sizeof</span>(<span style="font-weight: bold;line-height: 26px;">double</span>)); args +=<span style="font-weight: bold;line-height: 26px;">sizeof</span>(<span style="font-weight: bold;line-height: 26px;">double</span>);<br/>    <span style="color: #998;font-style: italic;line-height: 26px;">// Pack all the primitives</span><br/>    nib[<span style="color: #008080;line-height: 26px;">0</span>].first = <span style="color: #008080;line-height: 26px;">0x0f</span> &amp; <span style="font-weight: bold;line-height: 26px;">static_cast</span>&lt;<span style="font-weight: bold;line-height: 26px;">uint8_t</span>&gt;(BufferUtils::pack(&amp;out, arg1));<br/> nib[<span style="color: #008080;line-height: 26px;">0</span>].second = <span style="color: #008080;line-height: 26px;">0x0f</span> &amp; <span style="font-weight: bold;line-height: 26px;">static_cast</span>&lt;<span style="font-weight: bold;line-height: 26px;">uint8_t</span>&gt;(BufferUtils::pack(&amp;out, arg2));<br/> nib[<span style="color: #008080;line-height: 26px;">1</span>].first = <span style="color: #008080;line-height: 26px;">0x0f</span> &amp; <span style="font-weight: bold;line-height: 26px;">static_cast</span>&lt;<span style="font-weight: bold;line-height: 26px;">uint8_t</span>&gt;(BufferUtils::pack(&amp;out, arg3));<br/>    <span style="font-weight: bold;line-height: 26px;">if</span> (<span style="color: #008080;line-height: 26px;">true</span>) {<br/>        <span style="color: #998;font-style: italic;line-height: 26px;">// memcpy all the strings without compression</span><br/>        <span style="font-weight: bold;line-height: 26px;">size_t</span> stringBytes = re-&gt;entrySize - (<span style="font-weight: bold;line-height: 26px;">sizeof</span>(arg1) + <span style="font-weight: bold;line-height: 26px;">sizeof</span>(arg2) + <span style="font-weight: bold;line-height: 26px;">sizeof</span>(arg3) +  <span style="color: #008080;line-height: 26px;">0</span>)<br/>                                            - <span style="font-weight: bold;line-height: 26px;">sizeof</span>(NanoLogInternal::Log::UncompressedEntry);<br/>        <span style="font-weight: bold;line-height: 26px;">if</span> (stringBytes &gt; <span style="color: #008080;line-height: 26px;">0</span>) {<br/>            <span style="color: #0086b3;line-height: 26px;">memcpy</span>(out, args, stringBytes);<br/>            out += stringBytes;<br/>        }<br/>    }<br/>    <span style="font-weight: bold;line-height: 26px;">return</span> out - originalOutPtr;<br/>}<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">日志的记录和压缩函数都是 Python 的预处理器分析源代码生成的，工程师在开始时使用的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">NANO_LOG</code> 会被预处理器展开成新的代码，该预处理器与 C++ 的预处理器有非常相似的功能，只是这里需要展开的代码过于复杂，我们很难在 C++ 中使用预处理器实现。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>运行时<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">应用程序静态链接的 NanoLog 运行时会通过线程上的缓冲区解耦记录动态参数的低延时 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">record</code> 操作和磁盘 I/O 等高延迟操作。线程上的缓冲区会存储 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">record</code> 方法调用的结果，这些数据对后台的压缩线程也是可见的：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.30833333333333335" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=d76a7f3d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bpicVEibS8VlARHDgnUCVJicZk3hJG99GdqK9OIWAvIxRw4mT9sQ71th2uP6X6x8pC6lefd3neLjRicng%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - 运行时</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">线程上用于暂存数据的缓冲区是程序提升性能的关键，我们要尽可能满足避免锁竞争和缓存一致性带来的额外开销，暂存缓冲区使用环形队列和单生产者的消费者模型降低数据的同步开销。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="1" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="232" src="https://wechat2rss.xlab.app/img-proxy/?k=e0b106fa&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bpicVEibS8VlARHDgnUCVJicZkOz1xiaAP1aerR5gSPq33Lk33SCf9ic1Msc0ubr7mlvoT1f3daXKMMialg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 5 - 环形队列</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">环形队列是一个连续的、使用固定大小缓存的数据结构，它的头尾是相连的，非常适合缓存数据流，Linux 内核就使用这种数据结构作为 Socket 的读写缓冲区[^2]，而音视频也会使用环形队列暂存刚刚收到还没有被解码的数据。</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7bpicVEibS8VlARHDgnUCVJicZkdfHuO9z7I2ytzmwRYiaic2MvibECzQbic0ZZCOhiaAqL1LvWpyf4RGDo3Dg/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(248, 248, 248);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #333;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #f8f8f8;border-radius: 5px;"><span style="line-height: 26px;"><span style="font-weight: bold;line-height: 26px;">struct</span> __<span style="color: #458;font-weight: bold;line-height: 26px;">kfifo</span> {</span><br/> <span style="font-weight: bold;line-height: 26px;">unsigned</span> <span style="font-weight: bold;line-height: 26px;">int</span> in;<br/> <span style="font-weight: bold;line-height: 26px;">unsigned</span> <span style="font-weight: bold;line-height: 26px;">int</span> out;<br/> <span style="font-weight: bold;line-height: 26px;">unsigned</span> <span style="font-weight: bold;line-height: 26px;">int</span> mask;<br/> <span style="font-weight: bold;line-height: 26px;">unsigned</span> <span style="font-weight: bold;line-height: 26px;">int</span> esize;<br/> <span style="font-weight: bold;line-height: 26px;">void</span>  *data;<br/>};<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">运行时不仅通过无锁环形队列提升性能，还需要解决环形队列中的日志消费问题。为了提高后台线程的处理能力，运行时会将日志的组装推迟到后处理并压缩日志数据减少 I/O 高延迟带来的影响。</p><h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span>后处理<span style="display: none;"></span></h3><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">后处理器的实现相对比较简单，因为每条日志都包含特定的标识符，后处理器会根据该标识符在压缩后的日志头中找到编译时生成的相关信息，并根据这些信息展开和解压日志。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">需要注意的是，因为在运行期间每个线程都有自己的暂存缓冲区存储日志，所以 NanoLog 最终打印的日志不能严格保证时间顺序，它只能保证日志输出的顺序在大体上是有序的。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">NanoLog 是设计非常有趣、值得学习的日志系统，但是它并不适合所有的项目，它将原本运行时需要完成的工作转移到了编译期间和后处理阶段，减轻了程序运行时的负担，但是其输出的二进制日志是无法直接阅读的，这也增加了开发者处理日志的工作量。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然论文中提到，日志分析引擎基本都会收集、解析并分析工程师可以直接阅读的日志，而大多数的时间都花费在了日志的读取和解析上，使用二进制的日志不仅能够减少读取和解析的时间，还能减少昂贵的 I/O 和使用的带宽，但是这点在我们的系统中是否是优点就见仁见智了。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">日志使用二进制的输出格式确实能够降低多方面的成本，但是它不仅需要日志收集和解析系统的支持，还牺牲了开发者本地调试的体验，而如果没有自动收集并解压日志的模块，手动解压线上日志排查问题是非常糟糕的体验，不过在极端的性能场景下我们可能也没有太多的选择，哪怕牺牲体验可能也要上。</p><section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="padding-right: 10px;padding-left: 10px;max-width: 100%;overflow-wrap: break-word;white-space: normal;background-color: rgb(255, 255, 255);font-size: 16px;color: black;line-height: 1.6;letter-spacing: 0px;word-break: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;box-sizing: border-box !important;"><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">推荐阅读</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484620&amp;idx=1&amp;sn=323c44f5d13b722102f36c0bcca79abb&amp;chksm=fe795bc7c90ed2d153e79a2897cf92f84d18d4d355ec775c4acd29b3d2a2a7f6a27edfe3c938&amp;scene=21#wechat_redirect" textvalue="流量管理与数据中心故障缓解 · OSDI 2018" data-itemshowtype="0" tab="innerlink" data-linktype="2" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">流量管理与数据中心故障缓解 · OSDI 2018</a></section></li><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484647&amp;idx=1&amp;sn=5106a4b821783372fbd9aa87a25805e3&amp;chksm=fe795becc90ed2fa1883afecbea90b8c5fea4bfa36aa61849c09fa1abad3700202f9c1e4413c&amp;scene=21#wechat_redirect" textvalue="分布式存储 Ceph 的演进经验 · SOSP 2019" data-itemshowtype="0" tab="innerlink" data-linktype="2" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">分布式存储 Ceph 的演进经验 · SOSP 2019</a></section><p style="max-width: 100%;min-height: 1em;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"/></p></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">图是怎么画的</h2><ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;max-width: 100%;overflow-wrap: break-word !important;"><li style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><section style="margin-top: 5px;margin-bottom: 5px;max-width: 100%;line-height: 26px;color: rgb(1, 1, 1);box-sizing: border-box !important;overflow-wrap: break-word !important;"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">技术文章配图指南</a></section></li></ul></section><p style="max-width: 100%;min-height: 1em;font-family: -apple-system, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;"><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p></section>



<p><a href="https://draveness.me/papers-nanolog/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=ca774f73&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484716%26idx%3D1%26sn%3Dbdb00764de73ad9745d82a4944271b0f%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 02 Feb 2021 08:30:00 +0800</pubDate>
    </item>
    <item>
      <title>为什么 Linux 需要 Swapping</title>
      <link>https://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484703&amp;idx=1&amp;sn=c2a2fec0ad00dee06c857aaa92bdf310</link>
      <description>Linux 触发 Swapping 的时机以及执行路径</description>
      <content:encoded><![CDATA[<p>
原创 <span>Draveness</span> <span>2020-11-17 08:25</span> <span style="display: inline-block;"></span>
</p>

<p>Linux 触发 Swapping 的时机以及执行路径</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=33da1fa9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2FPvFQOcBK7boDdVubptx9ZawGNwpuTy5IhBsbDy3HBUjZ4FpPCHcFVkoyzuPr6F6qqHgYshNkLzDgKnEPoKg3ow%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<section data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="font-size: 16px;color: black;padding-right: 10px;padding-left: 10px;line-height: 1.6;letter-spacing: 0px;word-break: break-word;overflow-wrap: break-word;text-align: left;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;"><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">为什么这么设计（Why’s THE Design）是一系列关于计算机领域中程序设计决策的文章，我们在这个系列的每一篇文章中都会提出一个具体的问题并从不同的角度讨论这种设计的优缺点、对具体实现造成的影响。如果你有想要了解的问题，可以在文章下面留言。</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">对 Linux 稍有了解的人都知道，Linux 会将物理的随机读取内存（Random Access Memory、RAM）按页分割成 4KB 大小的内存块，而今天要介绍的 <em>Swapping</em> 机制就与内存息息相关，它是操作系统将物理内存页中的内容拷贝到硬盘上交换空间（Swap Space）以释放内存的过程，物理内存和硬盘上的交换分区组成了操作系统上可用的虚拟内存，而这些交换空间都是系统管理员预先配置好的[^1]。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.35833333333333334" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=57f2b2d2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boDdVubptx9ZawGNwpuTy5I9ebnLCerCNwTgk2FXsrSwwfPk3eVvmhiaJVSngaL3FbNfgUljbn6uibg%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 1 - Linux Swapping</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">正是因为 Linux 上的所有进程都会通过虚拟内存这一层抽象间接与物理内存打交道，而 Swapping 也充分利用了该特性，它能够让应用程序看到操作系统内存充足的假象，然而并不知道它使用的部分虚拟内存其实在磁盘上，因为内存和磁盘的读写速度上的巨大差异，这部分虚拟内存的读写非常缓慢，我们在 为什么 CPU 访问硬盘很慢 曾经介绍过：</p><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">在 SSD 中随机访问 4KB 数据所需要的时间是访问主存的 1,500 倍，机械磁盘的寻道时间是访问主存的 100,000 倍[^2]</p></blockquote><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如此巨大的性能差异使得触发 Swapping 的进程可能会遇到性能损失，同一个页面的频繁换入换出会导致极其明显的性能抖动，如果没有相应的背景知识，遇到类似的问题可能会很难查到根本原因，例如 MySQL 在错误配置 NUMA 时就会出现内存页频繁换入换出，影响服务质量的问题。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Linux 提供了两种不同的方法启用 Swapping，分别是 Swap 分区（Swap Partition）和 Swap 文件（Swapfile）：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Swap 分区是硬盘上的独立区域，该区域只会用于交换分区，其他的文件不能存储在该区域上，我们可以使用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">swapon -s</code> 命令查看当前系统上的交换分区；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Swap 文件是文件系统中的特殊文件，它与文件系统中的其他文件也没有太多的区别；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Swap 分区的大小是需要系统管理员手动设定的，然而不同的场景最好设置不同交换分区大小，例如：桌面系统的交换分区大小可以是系统内存的两倍，这可以让我们同时运行更多的应用程序；服务器的交换分区应该关闭或者使用少量的交换分区，不过一旦启用交换分区，就应该引入监控监控应用程序的性能。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">我们到现在已经对 Linux 上的 Swapping 有了一定的了解，接下来回到这篇文章想要讨论的问题 — 『为什么 Linux 需要 Swapping』，我们将从以下两个方面介绍 Swapping 解决的问题、触发入口和执行路径：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Swapping 可以直接将进程中使用相对较少的页面换出内存，立刻给正在执行的进程分配内存；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Swapping 可以将进程中的闲置页面换出内存，为其他进程未来使用内存做好准备；</section></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>内存不足</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当系统需要的内存超过了可用的物理内存时，内核会将内存中不常使用的内存页交换到磁盘上为当前进程让出内存，保证正在执行的进程的可用性，这个内存回收的过程是强制的直接内存回收（Direct Page Reclaim）。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.35833333333333334" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=9325b3d9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boDdVubptx9ZawGNwpuTy5IbCY4TY9FcbqmYV82EEHWvPwQRgvP2pacAAvWkBv3fSfiaXfIM4rmpkw%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 2 - 直接内存回收</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">直接内存回收是在 Linux 调用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">__alloc_pages_nodemask</code> 申请新内存页时触发的，该函数会先在空闲页列表中查找是否有可用的页面，如果不存在可用页面，就会进入 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">__alloc_pages_slowpath</code> 函数分配内存页，与从空闲列表中直接查找内存也相比，该函数会通过以下步骤分配内存：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7boDdVubptx9ZawGNwpuTy5IIQDoUoIDYfZzMyL6wCPnmRts3JNUF5YQWjcJQYRn0raNlBOgzqtTpw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;">static inline <span style="color: #c678dd;line-height: 26px;">struct</span> page * __alloc_pages_slowpath(gfp_t gfp_mask, unsigned <span style="color: #c678dd;line-height: 26px;">int</span> order, <span style="color: #c678dd;line-height: 26px;">struct</span> alloc_context *ac) {<br/>    ...<br/>    <span style="color: #c678dd;line-height: 26px;">if</span> (alloc_flags &amp; ALLOC_KSWAPD)<br/>        wake_all_kswapds(order, gfp_mask, ac);<br/>    page = get_page_from_freelist(gfp_mask, order, alloc_flags, ac);<br/>    <span style="color: #c678dd;line-height: 26px;">if</span> (page) <span style="color: #c678dd;line-height: 26px;">goto</span> got_pg;<br/>    <span style="color: #c678dd;line-height: 26px;">if</span> (can_direct_reclaim &amp;&amp; (costly_order || (order &gt; <span style="color: #d19a66;line-height: 26px;">0</span> &amp;&amp; ac-&gt;migratetype != MIGRATE_MOVABLE)) &amp;&amp; !gfp_pfmemalloc_allowed(gfp_mask)) {<br/>        page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac, INIT_COMPACT_PRIORITY, &amp;compact_result);<br/>        <span style="color: #c678dd;line-height: 26px;">if</span> (page) <span style="color: #c678dd;line-height: 26px;">goto</span> got_pg;<br/>        ...<br/>    }<br/> retry:<br/>    page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags, ac, &amp;did_some_progress);<br/>    page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac, compact_priority, &amp;compact_result);<br/>    page = __alloc_pages_may_oom(gfp_mask, order, ac, &amp;did_some_progress);<br/> got_pg:<br/>    <span style="color: #c678dd;line-height: 26px;">return</span> page;<br/>}<br/></code></pre><ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">唤醒 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">kswapd</code> 线程在后台回收内存并尝试调用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">get_page_from_freelist</code> 从空闲列表中快速获取内存页；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">昂贵的内存申请会先调用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">__alloc_pages_direct_compact</code> 尝试压缩内存页，并在压缩后的内存中调用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">get_page_from_freelist</code> 查找空闲的内存页；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">调用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">__alloc_pages_direct_reclaim</code> 直接回收并分配新的内存页；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">再次调用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">__alloc_pages_direct_compact</code> 尝试压缩内存并获取空闲内存页；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">调用 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">__alloc_pages_may_oom</code> 分配内存，如果内存分配失败会触发内存不足警告随机杀死操作系统上的几个进程；</section></li></ol><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">虽然获取内存页的步骤已经经过了大量的删减，但是其中展示了 Linux 在内存也不足时获取内存的几个常见方法：内存压缩、直接回收以及触发内存不足错误杀掉部分进程。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>内存闲置</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">应用程序在启动阶段使用的大量内存在启动后往往都不会使用，通过后台运行的守护进程，我们可以将这部分只使用一次的内存交换到磁盘上为其他内存的申请预留空间。kswapd 是 Linux 负责页面置换（Page replacement）的守护进程，它也是负责交换闲置内存的主要进程，它会在空闲内存低于一定水位时，回收内存页中的空闲内存保证系统中的其他进程可以尽快获得申请的内存，如下图所示：</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.49166666666666664" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=a0cbf095&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boDdVubptx9ZawGNwpuTy5IvYfD6bIs3dnILV0nMsDaFR4y9ugZYhM6cNjAQVoUziaAgcymgB0Oib9A%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 3 - Linux 空闲页面水位</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">当空闲页面小于 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">WMARK_LOW</code> 时，kswapd 进程才会开始工作，它会将内存页交换到磁盘上直到空闲页面的水位回到 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">WMARK_HIGH</code>，不过当空闲页面的水位低于 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">WMARK_MIN</code> 时会触发上一节提到的内存直接回收，而水位高于 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">WMARK_HIGH</code> 则意味着空闲内存充足，不需要进行回收。</p><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Linux 操作系统采用最近最少使用（Least Recently Used、LRU）算法置换内存中的页面，系统中的每个区都会在内存中持有 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">active_list</code> 和 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">inactive_list</code> 两种链表，其中前者包含活跃的内存页，后者中存储的内存页都是回收的候选页面，除此之外，Linux 还会在将 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">lru_list</code> 根据内存页的特性分成如下几种：</p><pre data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;"><span style="display: block;background: url(&#34;https://mmbiz.qpic.cn/mmbiz_png/PvFQOcBK7boDdVubptx9ZawGNwpuTy5IIQDoUoIDYfZzMyL6wCPnmRts3JNUF5YQWjcJQYRn0raNlBOgzqtTpw/640?wx_fmt=png&#34;) 10px 10px / 40px no-repeat rgb(40, 44, 52);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #abb2bf;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #282c34;border-radius: 5px;"><span style="color: #c678dd;line-height: 26px;">enum</span> lru_list {<br/> LRU_INACTIVE_ANON = LRU_BASE,<br/> LRU_ACTIVE_ANON = LRU_BASE + LRU_ACTIVE,<br/> LRU_INACTIVE_FILE = LRU_BASE + LRU_FILE,<br/> LRU_ACTIVE_FILE = LRU_BASE + LRU_FILE + LRU_ACTIVE,<br/> LRU_UNEVICTABLE,<br/> NR_LRU_LISTS<br/>};<br/></code></pre><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">其中包含 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">ANON</code> 的表示匿名内存页，这些内存页存储了与文件无关的进程堆栈等内容，而包含 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">FILE</code> 的表示与文件相关的内存，也就是程序文件或者数据对应的内存，而最后的 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">LRU_UNEVICTABLE</code> 表示禁止回收的内存页。</p><figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"><img data-ratio="0.275" style="display: block;margin-right: auto;margin-left: auto;" data-type="png" data-w="1200" src="https://wechat2rss.xlab.app/img-proxy/?k=b9b08e07&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7boDdVubptx9ZawGNwpuTy5I2RUPAUI8VffmOQg4byUNEKLBEV6ZQvoo0enJb0nSpLVysVSsAQQy2Q%2F640%3Fwx_fmt%3Dpng"/><figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 14px;"><strong style="color: black;font-size: 16px;letter-spacing: 0px;text-align: left;">图 4 - 活跃链表和不活跃链表</strong><br/></figcaption></figure><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">每当内存页被访问时，Linux 都会将被访问的内存页移到链表的头部，所以在活跃链表末尾的是链表中『最老的』内存页，守护进程 kswapd 的作用是平衡两个链表的长度，将活跃链表末尾的内存页移至不活跃链表的队首等待回收，而函数 <code style="font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;background-color: rgba(27, 31, 35, 0.05);font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;word-break: break-all;">shrink_zones</code> 会负责回收 LRU 链表中的不活跃内存页。</p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>总结</h2><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">很多人认为当系统内存不足时应该立即触发内存不足（Out of memory、OOM）并杀掉进程，但是 Swapping 其实为系统管理员提供了另外一种选择，利用磁盘的交换空间避免程序被直接退出，以<strong>降低服务质量的代价换取服务的部分可用性</strong>。Linux 中的 Swapping 机制主要是为内存不足和内存闲置两种常见的情况存在的</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Swapping 可以直接将进程中使用相对较少的页面换出内存：当系统需要的内存超过了可用的物理内存时，内核会将内存中不常使用的内存页交换到磁盘上为当前进程让出内存，保证正在执行的进程的可用性；</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Swapping 可以将进程中的闲置页面换出内存：应用程序在启动阶段使用的大量内存在启动后往往都不会使用，通过后台运行的守护进程，我们可以将这部分只使用一次的内存交换到磁盘上为其他内存申请预留空间；</section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">关于是否应该开启 Swapping 的讨论其实非常多，我们在今天也不应该一刀切地认为必须开启或者禁用 Swapping，我们仍然需要分析场景并利用好 Linux 为我们提供的这一机制，例如 Kubernetes 要求禁用 Swapping，我们就应该遵循社区提出的建议，在部署 Kubernetes 的机器上关闭这一特性[^3]。到最后，我们还是来看一些比较开放的相关问题，有兴趣的读者可以仔细思考一下下面的问题：</p><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Linux 提供了哪些参数来控制 Swapping 的行为？</section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">通过降低服务质量的代价换取部分可用在哪些场景下是可取的？</section></li></ul><blockquote data-tool="mdnice编辑器" style="border-top: none;border-right: none;border-bottom: none;font-size: 0.9em;overflow: auto;border-left-color: rgba(0, 0, 0, 0.4);background: rgba(0, 0, 0, 0.05);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;line-height: 26px;">如果对文章中的内容有疑问或者想要了解更多软件工程上一些设计决策背后的原因，可以在博客下面留言，作者会及时回复本文相关的疑问并选择其中合适的主题作为后续的内容。</p></blockquote><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>推荐阅读</h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484282&amp;idx=1&amp;sn=62ddd2e10613c126fa602c845c4d833e&amp;chksm=fe795c71c90ed56742bffa01e73fa1dc33c263bd215ecbadfbd6e8da2c16e8f225e18d995538&amp;scene=21#wechat_redirect" textvalue="为什么 Linux 需要虚拟内存" data-itemshowtype="0" tab="innerlink" data-linktype="2">为什么 Linux 需要虚拟内存</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484464&amp;idx=1&amp;sn=32bd106c7495207cabe97368a96697bb&amp;chksm=fe795b3bc90ed22dbd0dd0a520a74b6a1e27d4126a930ec2bba15773c38aafab166abfc42899&amp;scene=21#wechat_redirect" textvalue="为什么 CPU 访问硬盘很慢" data-itemshowtype="0" tab="innerlink" data-linktype="2">为什么 CPU 访问硬盘很慢</a></section></li></ul><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"><span style="display: none;"></span>参考资料</h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Kubelet/Kubernetes should work with Swap Enabled #53533 <a href="https://github.com/kubernetes/kubernetes/issues/53533" target="_blank">https://github.com/kubernetes/kubernetes/issues/53533</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Linux Performance: Why You Should Almost Always Add Swap Space <a href="https://haydenjames.io/linux-performance-almost-always-add-swap-space/" target="_blank">https://haydenjames.io/linux-performance-almost-always-add-swap-space/</a></section></li><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);">Do we really need swap on modern systems? <a href="https://www.redhat.com/en/blog/do-we-really-need-swap-modern-systems" target="_blank">https://www.redhat.com/en/blog/do-we-really-need-swap-modern-systems</a></section></li></ul><p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="font-size: 22px;font-weight: bold;letter-spacing: 0px;">图是怎么画的</span><br/></p><h2 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 22px;"></h2><ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"><li><section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"><a target="_blank" href="http://mp.weixin.qq.com/s?__biz=MzU5NTAzNjc3Mg==&amp;mid=2247484039&amp;idx=1&amp;sn=0d08cb064d6374f6e87982787d565aec&amp;chksm=fe795d8cc90ed49a406cdc143dbf65c0fda75c60621ba6f6495aff325b2c3fbed306fd91227d&amp;scene=21#wechat_redirect" textvalue="技术文章配图指南" data-itemshowtype="0" tab="innerlink" data-linktype="2">技术文章配图指南</a></section></li></ul></section><p><img data-type="jpeg" data-ratio="0.3648148148148148" data-w="1080" style="font-family: -apple-system-font, system-ui, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);box-sizing: border-box !important;overflow-wrap: break-word !important;visibility: visible !important;width: 677px !important;" src="https://wechat2rss.xlab.app/img-proxy/?k=7360c51e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2FPvFQOcBK7bq32UUkqP0QNGkkhH5fvlUoAu0iarLqzr9JXkBia4HGiaq1bvI1ZJkDRbQ3IuXIIicibLnsJzhdy8ckDFA%2F640%3Fwx_fmt%3Djpeg"/></p>



<p><a href="https://draveness.me/whys-the-design-linux-swapping/">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=9991b5e9&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzU5NTAzNjc3Mg%3D%3D%26mid%3D2247484703%26idx%3D1%26sn%3Dc2a2fec0ad00dee06c857aaa92bdf310%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 17 Nov 2020 08:25:00 +0800</pubDate>
    </item>
  </channel>
</rss>