<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>NoRipple</title>
        <link>https://nowave.cloud//</link>
        <description>如是我闻，姑妄听之</description>
        <lastBuildDate>Tue, 26 May 2026 11:10:51 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <copyright>All rights reserved 2026, 宗海</copyright>
        <item>
            <title><![CDATA[Java Serializable]]></title>
            <link>https://nowave.cloud//article/1f7beb96-1d72-80d7-829f-cf6b867f98d5</link>
            <guid>https://nowave.cloud//article/1f7beb96-1d72-80d7-829f-cf6b867f98d5</guid>
            <pubDate>Sun, 18 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[序列化是将数据结构或对象转换为可存储或传输的形式的过程，反序列化则是将其恢复为原始状态。Java中实现序列化需实现java.io.Serializable接口，静态变量不会被序列化。序列化的常见协议包括JDK、Kryo、Protobuf/ProtoStuff和Hessian。JDK序列化存在跨语言调用支持差、性能低和安全问题等缺点。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1f7beb961d7280d7829fcf6b867f98d5"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-text notion-block-1f7beb961d7281e4881ccd983d8e1ccc"><b>序列化：</b>将数据结构或对象转换成可以存储或传输的形式，通常是二进制字节流，也可以是 json、xml 等文本格式。</div><div class="notion-text notion-block-1f7beb961d7281f18577dc6930cfa7ec"><b>反序列化：</b>将在序列化过程中所生成的数据转化为原始数据结构或者对象的过程。</div><div class="notion-text notion-block-1f7beb961d728198bcf7fdecb0da8cec"><b>序列化（serialization）</b>在计算机科学的数据处理中，是指将数据结构或对象状态转换成可取用格式（例如存成文件，存于缓冲，或经由网络中发送），以留待后续在相同或另一台计算机环境中，能恢复原先状态的过程。依照序列化格式重新获取字节的结果时，可以利用它来产生与原始对象相同语义的副本。对于许多对象，像是使用大量引用的复杂对象，这种序列化重建的过程并不容易。面向对象中的对象序列化，并不概括之前原始对象所关系的函数。这种过程也称为对象编组（marshalling）。从一系列字节提取数据结构的反向操作，是反序列化（也称为解编组、deserialization、unmarshalling）。</div><div class="notion-text notion-block-1f7beb961d7281bd8eafc9e4e6e34dce">常见序列化协议：</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7beb961d7281ef9b47c4ffa33c2f17" data-id="1f7beb961d7281ef9b47c4ffa33c2f17"><span><div id="1f7beb961d7281ef9b47c4ffa33c2f17" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281ef9b47c4ffa33c2f17" title="1. JDK"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">1. JDK</span></span></h2><div class="notion-text notion-block-1f7beb961d72814f82d8d24cbf541838">实现 JDK 自带的序列化，只需要实现<em> java.io.Serializable </em>接口即可。</div><div class="notion-text notion-block-1f7beb961d72817eab56f1f1e622c853"><em>Serializable </em>是<b>标记接口</b>，实现标记接口类仅仅是<b>标记类为可序列化</b>，并没有增加任何方法，该接口告诉 JVM 该类的对象已经准备好写入持久性存储或者通过网络读取。</div><div class="notion-text notion-block-1f7beb961d7281a59962fc206ebbdb07">静态变量不会被序列化，因为其不是对象本身的一部分。</div><div class="notion-text notion-block-1f7beb961d7281b18893d6251ea3f6b9">默认情况，JVM 负责编写和读取可序列化对象的过程。序列化或者反序列化的功能通过对象流类的以下两种方法公开：</div><ul class="notion-list notion-list-disc notion-block-1f7beb961d7281b0b69ded4aaf26e1b5"><li><b>ObjectOutputStream writeObject(Object)</b>: 将可序列化的对象写入输出流。</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d72812087a4e27066a13419"><li><b>ObjectInputStream readObject():</b> 从输入流读取（寻找到序列化类之后）构造并返回一个对象。</li></ul><div class="notion-text notion-block-1f7beb961d728194831ddd539c49814a">对于可序列化类需要注意两个成员变量：</div><ul class="notion-list notion-list-disc notion-block-1f7beb961d7281498ffbf2d1751430ab"><li><b>serialVersionUID</b>：常数，用于唯一标识可序列化类的版本。从输入流构造对象时，JVM 检查此常数。serialVersionUID是可选的，这意味着即使不显示声明，编译器会基于类的元素自动生成（如果元素变化，serialVersionUID 也会随之变化），这也是显示指明的优点，即类的修改不会导致序列化失败。</li></ul><div class="notion-text notion-block-1f7beb961d728190911cdfd29a227219">A serializable class can declare its own serialVersionUID explicitly by declaring a field named &quot;serialVersionUID&quot; that must be <b>static</b>, <b>final</b>, and of type <b>long</b>;</div><ul class="notion-list notion-list-disc notion-block-1f7beb961d72811c861ae9ff46c56112"><li><b>transient</b>：标记瞬时变量，JVM 不会序列化并存储此变量值，在反序列化后变量值被设置为类型默认值。</li></ul><details class="notion-toggle notion-block-1f7beb961d7281ebbc3cfeb43ec7c8eb"><summary>不推荐使用</summary><div><div class="notion-text notion-block-1f7beb961d728181bffad1f046e29f46">很少或者说几乎不会直接使用 JDK 自带的序列化方式，主要原因有下面这些原因：</div><div class="notion-text notion-block-1f7beb961d728122a9dfd2f93ab2cf1c"><b>不支持跨语言调用</b> : 如果调用的是其他语言开发的服务的时候就不支持了。</div><div class="notion-text notion-block-1f7beb961d72817f95d9ec2c5ea3a5c9"><b>性能差</b>：相比于其他序列化框架性能更低，主要原因是序列化之后的字节数组体积较大，导致传输成本加大。</div><div class="notion-text notion-block-1f7beb961d728195be75edc2baf4fdb5"><b>存在安全问题</b>：序列化和反序列化本身并不存在问题。但当输入的反序列化的数据可被用户控制，那么攻击者即可通过构造恶意输入，让反序列化产生非预期的对象，在此过程中执行构造的任意代码。</div></div></details><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7beb961d7281a29671cc22108bd560" data-id="1f7beb961d7281a29671cc22108bd560"><span><div id="1f7beb961d7281a29671cc22108bd560" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281a29671cc22108bd560" title="2. Kryo"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2. Kryo</span></span></h2><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7beb961d728111bf8edbe10e2f29d2" data-id="1f7beb961d728111bf8edbe10e2f29d2"><span><div id="1f7beb961d728111bf8edbe10e2f29d2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d728111bf8edbe10e2f29d2" title="3. Protobuf / ProtoStuff"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">3. Protobuf / ProtoStuff</span></span></h2><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7beb961d728105aabaf63aa9fcc43b" data-id="1f7beb961d728105aabaf63aa9fcc43b"><span><div id="1f7beb961d728105aabaf63aa9fcc43b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d728105aabaf63aa9fcc43b" title="4. Hessian"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">4. Hessian</span></span></h2></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[CDN]]></title>
            <link>https://nowave.cloud//article/1f7beb96-1d72-804b-988c-ca0550d4f4fc</link>
            <guid>https://nowave.cloud//article/1f7beb96-1d72-804b-988c-ca0550d4f4fc</guid>
            <pubDate>Sun, 18 May 2025 00:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1f7beb961d72804b988cca0550d4f4fc"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-text notion-block-1f7beb961d72813bba36e75979b81be5"><b>Control Delivery Network，即内容分发网络。</b></div><div class="notion-text notion-block-1f7beb961d7281859601d93743ee6c50"><em>所谓内容指的是</em><em><b>静态资源</b></em><em>，比如图片、视频、文档、js、css、html。</em></div><div class="notion-text notion-block-1f7beb961d7281de8006e26ce94154c0"><em>所谓分发网络指的是讲这些静态资源分发到位于多个不同地理位置的机房中的服务器上，以实现静态资源的</em><em><b>就近访问</b></em><em>。</em></div><div class="notion-text notion-block-1f7beb961d7281b2b064e95f40673084"><b>CDN 就是将静态资源分发到不同的地方以实现对应地区的就近访问，进而加快静态资源的访问速度，减轻服务器以及带宽的负担。</b></div><div class="notion-text notion-block-1f7beb961d728152a1a1da59fce35071"><b>也可以将其视作服务上一层特殊缓存，主要用来处理静态资源。</b></div><ul class="notion-list notion-list-disc notion-block-1f7beb961d72815e9c9dd9e42aebc308"><li><em>ECDN / DCDN</em></li><ul class="notion-list notion-list-disc notion-block-1f7beb961d72815e9c9dd9e42aebc308"><li><em>同时加速动态资源和静态资源。</em><em><b>动态加速</b></em><em>是指对于那些不能缓存在边缘节点的资源，动态资源基于智能选路技术，从多条回源线路中择优选择一条线路进行传输。</em></li><li><em>支持边缘计算。</em></li></ul></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d728101be65ffd2ea5bf789"><li><em>CDN</em></li><ul class="notion-list notion-list-disc notion-block-1f7beb961d728101be65ffd2ea5bf789"><li><em>加速静态资源。</em></li><li><em>支持边缘计算。</em></li></ul></ul><div class="notion-text notion-block-1f7beb961d7281c7ad14f52aa01704c3">其实实现资源的就近访问还可以<b>直接将服务部署在不同地区</b>，但这样做会出现两个问题。一是成本太高，这不言自明；二是性能影响，静态资源的访问对系统资源的大到足以影响到系统中的其他服务。这种行为主要是实现系统的高可用而非高性能。</div><hr class="notion-hr notion-block-1f7beb961d7281129733e18771bd25ad"/><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72813e9635eacf60bb4d25" style="list-style-type:decimal"><li>静态资源缓存 CDN 节点。</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d72813e9635eacf60bb4d25" style="list-style-type:lower-alpha"><ul class="notion-list notion-list-disc notion-block-1f7beb961d7281fbab7cf4ce0fc30f5c"><li>回源当 CDN 节点上没有用户请求的资源或该资源的缓存已经过期时，CDN 节点需要从原始服务器获取最新的资源内容，这个过程就是回源。当用户请求发生回源的话，会导致该请求的响应速度比未使用 CDN 还慢，因为相比于未使用 CDN 还多了一层 CDN 的调用流程。</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d72817f9329fb14e39acd65"><li>预热指在 CDN 上提前将内容缓存到 CDN 节点上。这样当用户在请求这些资源时，能够快速地从最近的 CDN 节点获取到而不需要回源，进而减少了对源站的访问压力，提高了访问速度。</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d7281e59835f756a25648a2"><li>刷新如果资源有更新的话，你也可以对其刷新 ，删除 CDN 节点上缓存的旧资源，并强制 CDN 节点回源站获取最新资源。</li></ul><div class="notion-text notion-block-1f7beb961d72817c8597fa755925af54">命中率和回源率是衡量 CDN 服务质量的重要指标。</div></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d728123b0c7c8fea824e167" style="list-style-type:decimal"><li>服务定位 CDN 节点。</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d728123b0c7c8fea824e167" style="list-style-type:lower-alpha"><div class="notion-text notion-block-1f7beb961d7281adb437fee39a13f71c">Global Server Load Balance，全局负载均衡。</div><div class="notion-text notion-block-1f7beb961d7281d7ba55f07734e0ce27">负责多个 CDN 节点之间相互协作，最常用的是基于 DNS 的 GSLB。</div><div class="notion-text notion-block-1f7beb961d728175bb73c0cb13dee667">CDN 会通过 GSLB 找到最合适的 CDN 节点</div><li>防止静态资源盗用。</li><li><b>Referer 防盗链</b>，具体来说就是根据 HTTP 请求的头信息里面的 Referer 字段对请求进行限制。我们可以通过 Referer 字段获取到当前请求页面的来源页面的网站地址，这样我们就能确定请求是否来自合法的网站。</li><li>时间戳防盗链的 URL 通常会有两个参数一个是签名字符串，一个是过期时间。签名字符串一般是通过对用户设定的加密字符串、请求路径、过期时间通过 MD5 哈希算法取哈希的方式获得。</li></ol></ol><div class="notion-blank notion-block-1f7beb961d7281b28ab0d56682fe64e5"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[JVM]]></title>
            <link>https://nowave.cloud//article/1f7beb96-1d72-806f-8261-e3e1c6f44968</link>
            <guid>https://nowave.cloud//article/1f7beb96-1d72-806f-8261-e3e1c6f44968</guid>
            <pubDate>Sun, 18 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[JVM 是运行在计算机上的程序，负责解释执行和内存管理。文中讨论了字节码文件的结构、类加载器的工作机制及其分类、双亲委派机制、运行时数据区的组成、自动垃圾回收的原理及其算法，最后介绍了不同的垃圾回收器及其特点。重点强调了类的唯一性、内存管理的复杂性以及垃圾回收的效率和策略。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1f7beb961d72806f8261e3e1c6f44968"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7beb961d728152a9d5f2633bc1fd04" data-id="1f7beb961d728152a9d5f2633bc1fd04"><span><div id="1f7beb961d728152a9d5f2633bc1fd04" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d728152a9d5f2633bc1fd04" title="基础知识"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">基础知识</span></span></h2><div class="notion-text notion-block-1f7beb961d72814586ddec913ff8f12d">JVM 本质上是一个运行在计算机上的程序，主要职责是解释运行、内存管理和即时编译。</div><div class="notion-text notion-block-1f7beb961d728193af0dc18986e5dc47">JVM 遵循 JVM 虚拟机规范，各大厂家研发不同版本。本文章讨论的是 hotspot(oracle jdk</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1f7beb961d72814d96c3d21bf9f7ac46"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:576px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://cdn.nlark.com/yuque/0/2024/png/40820000/1728442770235-650699e6-12a9-40a4-a9bd-eb2bba092fd6.png?spaceId=f58ff682-9d29-4b29-b781-b313300f1828&amp;t=1f7beb96-1d72-814d-96c3-d21bf9f7ac46" alt="notion image" loading="lazy" decoding="async"/></div></figure><hr class="notion-hr notion-block-1f7beb961d7281c0b1cbf0d7f3f05364"/><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1f7beb961d7281419a12feba0fc141b4" data-id="1f7beb961d7281419a12feba0fc141b4"><span><div id="1f7beb961d7281419a12feba0fc141b4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281419a12feba0fc141b4" title="字节码文件"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">字节码文件</span></span></h3><details class="notion-toggle notion-block-1f7beb961d7281e09434d05b125d334a"><summary>基本信息</summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d728119a319ffdf3263abc6" style="list-style-type:decimal"><li>文件头
文件无法通过文件扩展名来确定文件类型，软件使用文件的头几个字节校验文件类型，这一部分也被成为 Magic Value。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d728160979ef878b68d5317" style="list-style-type:decimal"><li>主副版本号
编译字节码文件的 jdk 版本号，主要用来判断当前字节码版本和运行环境 jdk 是否兼容。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d728183b424d3e4a39edfb3" style="list-style-type:decimal"><li>访问标志</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d72818c8caafe71d2eae6d5" style="list-style-type:decimal"><li>类、父类、接口索引</li></ol></div></details><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281658ffec7ff7dac206a" style="list-style-type:decimal"><li>常量池</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281e895fbe2fb87aea22c" style="list-style-type:decimal"><li>字段</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d728163a46dd240b06a8764" style="list-style-type:decimal"><li>方法</li></ol><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1f7beb961d7281b4b35fe31c7ccc93b5" data-id="1f7beb961d7281b4b35fe31c7ccc93b5"><span><div id="1f7beb961d7281b4b35fe31c7ccc93b5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281b4b35fe31c7ccc93b5" title="类加载器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">类加载器</span></span></h3><details class="notion-toggle notion-block-1f7beb961d7281c0b2a2d4912daafaeb"><summary>类的生命周期</summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281dbb698d4f7adeea8b6" style="list-style-type:decimal"><li>Loading类加载器根据类的全限定名通过不同渠道以二进制流的方式获取字节码信息。<b>程序员可以通过 JAVA 代码扩展不同渠道（动态代理）</b>。JVM 会将字节码的信息<b>保存在内存方法区（InstanceKlass）</b>，同时生成类似数据<b>保存在堆区（ java.lang.Class ）</b>，作用是在 java 代码中去获取类的信息以及存储静态字段的数据，也就是<b>反射</b>。<em>方法区中的对象是由 c++编写，java 代码不能直接操作，所以在堆区创建一个 java 编写的对象以便获取。而且堆区中的类对象包含数据更少，这是基于开发者需要和安全性考虑，只让开发者访问一部分数据。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d5868be5390de23e89" style="list-style-type:decimal"><li>Linking<b>不需要程序员参与。</b></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281489b56cc614d673041" style="list-style-type:decimal"><li>验证内容是否合规。</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d728172a12dd61ba44789e6" style="list-style-type:decimal"><li>为静态变量分配内存并赋初始值。</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d72810cbdc9ded424d88fce" style="list-style-type:decimal"><li>将常量池中的符号引用替换成指向内存的直接引用。</li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d728126b0f7f6c912c01349" style="list-style-type:decimal"><li>Initialiazation<b>执行静态代码块中的代码，并为静态变量赋值。</b>类的初始化并不会在加载到堆区后马上进行，需要满足<b>触发条件</b>：</li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-1f7beb961d7281f98870cc430ddea660" style="list-style-type:decimal"><li>访问类的静态变量或者静态方法，访问父类的静态变量，不会触发子类的初始化。</li></ol><ol start="8" class="notion-list notion-list-numbered notion-block-1f7beb961d7281509529f021f221e905" style="list-style-type:decimal"><li>调用 Class.forName(className)</li></ol><ol start="9" class="notion-list notion-list-numbered notion-block-1f7beb961d72813ca7d5e3859972d7f3" style="list-style-type:decimal"><li>new Instance，数组创建不会导致数组中元素类初始化。</li></ol><ol start="10" class="notion-list notion-list-numbered notion-block-1f7beb961d728171b172f1dec33555c2" style="list-style-type:decimal"><li>执行 Main 方法的当前类</li></ol><ol start="11" class="notion-list notion-list-numbered notion-block-1f7beb961d72819fac36e1c6acd3d8ba" style="list-style-type:decimal"><li>Using</li></ol><ol start="12" class="notion-list notion-list-numbered notion-block-1f7beb961d72818f8191d621fa423e4a" style="list-style-type:decimal"><li>Unloading</li></ol></div></details><div class="notion-text notion-block-1f7beb961d728175b45dc674b7d7fb15">类加载器负责把字节码文件加载到内存。</div><hr class="notion-hr notion-block-1f7beb961d7281c48389c63f4eaf70f2"/><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d72814b9262c1bb0b24b516" data-id="1f7beb961d72814b9262c1bb0b24b516"><span><div id="1f7beb961d72814b9262c1bb0b24b516" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72814b9262c1bb0b24b516" title="类加载器分类"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">类加载器分类</span></span></h4><div class="notion-text notion-block-1f7beb961d728146944acbc2806e99a4">除了顶层启动类加载器，所有的其他加载器都有自己的父类加载器。</div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281e5a38bffea5596178b" style="list-style-type:decimal"><li><b>启动类加载器</b>
Bootstrap ClassLoader（c++）或者 BootClassLoader（java）
加载 Java 最核心的类，是由虚拟机提供的类加载器。默认加载/jre/lib 下的类文件。
开发者无法直接获取到启动类加载器的引用，所以不允许直接通过引用进行操作。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d28b35e1a7daeaa81d" style="list-style-type:decimal"><li><b>扩展类加载器</b>
Extension ClassLoader（jdk 1.8） 或者 Platform ClassLoader（jdk 9）
负责将Java_Home /lib/ext或者由系统变量 java.ext.dir指定位置中的类库加载到内存中。
开发者可以直接使用标准扩展类加载器。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d728162b3a7f7a6125c1d92" style="list-style-type:decimal"><li><b>应用程序类加载器 / 系统加载器</b>
负责将系统类路径（CLASSPATH）中指定的类库加载到内存中。
由于这个类加载器是ClassLoader中的getSystemClassLoader()方法的返回值，因此一般称为系统（System）加载器。
开发者可以直接使用系统类加载器。</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281ca9097d1b70deacca7" style="list-style-type:decimal"><li>自定义类加载器</li></ol><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d7281d7b6e9d4a269cc22f7" data-id="1f7beb961d7281d7b6e9d4a269cc22f7"><span><div id="1f7beb961d7281d7b6e9d4a269cc22f7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281d7b6e9d4a269cc22f7" title="双亲委派机制"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">双亲委派机制</span></span></h4><details class="notion-toggle notion-block-1f7beb961d72819ea555f96fd36ad1ff"><summary>类加载器与类唯一性</summary><div><div class="notion-text notion-block-1f7beb961d7281cd9cf1ceec13a11ba0">类加载器虽然只用于实现类的加载动作，但是<b>对于任意一个类，都需要由加载它的类加载器和这个类本身共同确立其在Java虚拟机中的唯一性。</b>通俗的说，JVM中两个类是否“相等”，首先就必须是同一个类加载器加载的，否则，即使这两个类来源于同一个Class文件，被同一个虚拟机加载，只要类加载器不同，那么这两个类必定是不相等的（同时在堆区也会有两份 class 文件）</div><div class="notion-text notion-block-1f7beb961d7281ce9354c03e3f6ef11d">这里的“相等”，包括代表类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果，也包括使用instanceof关键字做对象所属关系判定等情况。</div></div></details><div class="notion-text notion-block-1f7beb961d728136beb8f26e10a33568">双亲委派机制的核心是<b>解决类由谁加载</b>。</div><div class="notion-text notion-block-1f7beb961d728100b621d1c5dae0d103">双亲委派模型的工作过程为：<em><span class="notion-inline-underscore">如果一个类收到类加载请求，首先检查自己是否加载过，若是则直接返回，否则把这个请求委派给父类加载器完成而不是尝试自己加载，因此所有的类加载请求都会传到顶层的启动类加载器，只有当父类加载器反馈自己无法完成该加载请求，才会自上而下尝试加载。</span></em></div><div class="notion-text notion-block-1f7beb961d728143bbbbdf0eec003a33">这显然<b>避免了重复加载</b>的问题，同时自然的<b>类随着类加载器带有优先级</b>。</div><blockquote class="notion-quote notion-block-1f7beb961d7281149571c8a5609c636f"><div>例如类java.lang.Object，它存在在rt.jar中，无论哪一个类加载器要加载这个类，最终都是委派给处于模型最顶端的Bootstrap ClassLoader进行加载，因此Object类在程序的各种类加载器环境中都是同一个类。</div><div class="notion-text notion-block-1f7beb961d728115889fc55fc8ba0809">相反，如果没有双亲委派模型而是由各个类加载器自行加载的话，如果用户编写了一个java.lang.Object的同名类并放在ClassPath中，那系统中将会出现多个不同的Object类，程序将混乱。因此，如果开发者尝试编写一个与rt.jar类库中重名的Java类，可以正常编译，但是永远无法被加载运行。</div></blockquote><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d72810081acd2425dc131bf" data-id="1f7beb961d72810081acd2425dc131bf"><span><div id="1f7beb961d72810081acd2425dc131bf" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72810081acd2425dc131bf" title="破坏双亲委派机制"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">破坏双亲委派机制</span></span></h4><div class="notion-text notion-block-1f7beb961d72813992c1d49532f88553">双亲委派模型是Java设计者推荐给开发者的类加载器的实现方式，并不是强制规定的。</div><div class="notion-text notion-block-1f7beb961d7281ae99b1e9a3387515c7">大多数的类加载器都遵循这个模型，但是JDK中也有较大规模破坏双亲模型的情况。</div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72814ba8f9d2ccc412cc70" style="list-style-type:decimal"><li><b>自定类加载器</b>
自定义类<b>继承 ClassLoader 类，重写 loadClass 方法（首先尝试自己加载而不是寻找父类加载器）和 findClass（从特定路径寻找类） 方法。</b>
Tomcat 通过这种方式实现应用之间的<b>类隔离</b>。
___。
__根据类的唯一性，这需要确保二者的类加载器不同，所以 Tomcat 使用自定义类加载器来实现应用之间类的隔离，每一个应用会有一个独立的类加载器加载对应类。
_自定义类仍然可以用于类加密、热部署和热更新以及模块化和插件化。</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d72814ba8f9d2ccc412cc70" style="list-style-type:lower-alpha"><div class="notion-text notion-block-1f7beb961d72816bab82db6007505777"><span class="notion-inline-underscore">一个 Tomcat 程序可以运行多个 Web 应用，如果两个应用中出现了限定名相同的类，Tomcat 需要保证他们都能被加载并且明确是两个不同的类</span></div></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d72810db213e06cda5ed5c3" style="list-style-type:decimal"><li><b>线程上下文加载器</b><em><span class="notion-inline-underscore">线程上下文类加载器允许开发者在特定的线程中指定一个类加载器，这样在该线程内的一些操作（如资源查找等）就会使用这个指定的类加载器，而不是默认的类加载器。</span></em>
每个类都会使用加载自己的类加载器去加载其他类，但是对于SPI来说，有些接口是Java核心库所提供的,而Java核心库是由启动类加载器来加载的，而这些接口的实现却来自于不同的jar包(厂商提供)， Java的启动类加载器是不会加载其他来源的jar包，这样<b>传统的双亲委托模型就无法满足SPI的要求</b>。而通过给当前线程设置上下文类加载器，就可以由设置的上下文类加载器来实现对于接口实现类的加载。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281c192a0e0932cb94a2a" style="list-style-type:decimal"><li>OSGi
实现一整套类加载机制，<em><span class="notion-inline-underscore">允许同级类加载器之间互相调用。</span></em></li></ol><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1f7beb961d7281959ca5e63818cc265d" data-id="1f7beb961d7281959ca5e63818cc265d"><span><div id="1f7beb961d7281959ca5e63818cc265d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281959ca5e63818cc265d" title="运行时数据区"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">运行时数据区</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d7281068fa8fbe755680ad9" data-id="1f7beb961d7281068fa8fbe755680ad9"><span><div id="1f7beb961d7281068fa8fbe755680ad9" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281068fa8fbe755680ad9" title="线程私有"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">线程私有</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281349252f7a26c9c9afc" style="list-style-type:decimal"><li>Program Counter Register
程序计数器，每个线程会通过程序计数器记录当前要执行的字节码指令地址。
可以控制程序指令的进行和线程切换恢复执行。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281878dbac647e3647a02" style="list-style-type:decimal"><li>Java Virtual Machine Stack
每个 Java 方法被执行的时候，Java虚拟机都会同步创建一个栈帧（Stack Frame）用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281e1837ee03c77dc580c" style="list-style-type:decimal"><li>Native Method Stacks
本地方法栈存储本地方法栈帧，在 HotSpot 虚拟机中，Java 虚拟机栈和本地方法栈实现上使用了同一个栈空间。</li></ol><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d728187a983fad502a34c35" data-id="1f7beb961d728187a983fad502a34c35"><span><div id="1f7beb961d728187a983fad502a34c35" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d728187a983fad502a34c35" title="线程共享"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">线程共享</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281de8a00d66f3bd590ed" style="list-style-type:decimal"><li><b>Java Heap</b>
分配内存空间以创建并存储对象。</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d7281de8a00d66f3bd590ed" style="list-style-type:lower-alpha"><li>Heap
对于堆空间来说，有三个需要关注的值，_<b>used、total、max</b>__。_
used 指当前已使用的堆内存，total 指 JVM 已经分配的可用堆内存，max 指 JVM 可以分配的最大堆内存。
max 默认是系统内存 1/4，total 默认是系统内存 1/64。<em><b>在实际应用中一般需要设置 max 和 total 的值。（-Xmx -Xms）</b></em></li><li>Object</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d7281b6a062f30c9ba6b333" style="list-style-type:lower-roman"><li><b>创建过程</b></li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d728191b7efcc79640f3a82" style="list-style-type:decimal"><li>类加载检查</li><li><em><b>划分可用空间
</b></em><b>同步锁定：</b>对分配内存空间的动作进行同步处理，实际上虚拟机是采用CAS配上失败重试的方式保证更新操作的原子性。
<b>分配缓冲：</b>另外一种是把内存分配的动作按照线程划分在不同的空间之中进行，即每个线程在Java堆中预先分配一小块内存，称为本地线程分配缓冲（Thread Local Allocation Buffer，TLAB），哪个线程要分配内存，就在哪个线程的本地缓冲区中分配，只有本地缓冲区用完了，分配新的缓存区时才需要同步锁定。</li><li>初始化零值
内存分配完成之后，虚拟机必须将分配到的内存空间（但不包括对象头）都初始化为零值（如果使用了TLAB的话，这一项工作也可以提前至TLAB分配时顺便进行）。
这步操作保证了对象的实例字段在Java代码中可以不赋初始值就直接使用，使程序能访问到这些字段的数据类型所对应的零值。</li><li>设置对象头</li><li>执行 init 方法</li></ol><li><b>内存布局</b></li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d728143870dcc1a62be8d33" style="list-style-type:decimal"><li>Header<em><span class="notion-inline-underscore">存储对象自身的运行时数据。</span></em> 如哈希码（HashCode）、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等，这部分数据的长度在32位和64位的虚拟机（未开启压缩指针）中分别为32个比特和64个比特，官方称它 为“Mark Word”。<em><span class="notion-inline-underscore">指向它的类型元数据的指针。</span></em></li><li>Instance Data
对象真正存储的有效信息。</li><li>Padding<em>占位符。HotSpot虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍，如果对象实例数据部分没有对齐的话，就需要通过占位符对齐填充来补全。</em></li></ol><li><b>访问定位</b></li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d7281569f82ccd71f6bc973" style="list-style-type:decimal"><li>句柄访问
句柄来访问的最大好处就是reference中存储的是稳定句柄地址，在对象被移动（垃圾收集时移动对象是非常普遍的行为）时只会改变句柄中的实例数据指针，而reference本身不需要被修改。</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d72819390e9cdde7db52115" style="list-style-type:lower-alpha"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1f7beb961d728123b725fbabd06b732b"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://cdn.nlark.com/yuque/0/2024/png/40820000/1728549856838-4c051e26-fe57-42fb-8bd3-acaa3385cc88.png?spaceId=f58ff682-9d29-4b29-b781-b313300f1828&amp;t=1f7beb96-1d72-8123-b725-fbabd06b732b" alt="notion image" loading="lazy" decoding="async"/></div></figure></ol><li>直接指针
使用直接指针来访问最大的好处就是速度更快，它节省了一次指针定位的时间开销，由于对象访问在Java中非常频繁，因此这类开销积少成多也是一项极为可观的执行成本，_<b>就虚拟机HotSpot而言，它主要直接指针进行对象访问。</b>__
_</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d7281bca3d6eabc40e0ebb7" style="list-style-type:lower-alpha"><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1f7beb961d728115be30d80f4ec2e0bf"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://cdn.nlark.com/yuque/0/2024/png/40820000/1728549890853-ab891fde-ddbe-403c-9445-fdd9e0842321.png?spaceId=f58ff682-9d29-4b29-b781-b313300f1828&amp;t=1f7beb96-1d72-8115-be30-d80f4ec2e0bf" alt="notion image" loading="lazy" decoding="async"/></div></figure></ol></ol></ol></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d72813a9206f97c5ee8556c" style="list-style-type:decimal"><li><b>Method Area</b>
JDK 7 将 Method Area 存放在在 <b>Java Heap: Permanent Generation</b>
（-XX:MaxPermSize=Value 控制）
JDK 8 将 Method Area 存放在<b>操作系统维护的直接内存</b>（元空间）
（默认情况下只要不超过操作系统承受上限，可以无限分配。也可以使用 -XX：MaxMataSpaceSize=Value 控制）</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d72813a9206f97c5ee8556c" style="list-style-type:lower-alpha"><li>类的元信息<a class="notion-link" href="about:blank#u2ab52d12">主要是类的定义</a>。</li><li>运行时常量池
字节码文件中通过编号查表的方法找到常量，这种常量池成为<b>静态常量池</b>。当静态常量池加载到内存中，可以通过内存地址快速定位到常量池中内容，这种常量池称为<b>运行时常量池</b>。</li><li>字符串常量池<b>存储代码中定义的常量字符串内容。</b></li></ol></ol><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1f7beb961d72811ab6f8ec03b35ae5db" data-id="1f7beb961d72811ab6f8ec03b35ae5db"><span><div id="1f7beb961d72811ab6f8ec03b35ae5db" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72811ab6f8ec03b35ae5db" title="自动垃圾回收"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">自动垃圾回收</span></span></h3><div class="notion-text notion-block-1f7beb961d72814fb81effe963fb6af7">Java 为了简化对象释放，引入自动垃圾回收机制。</div><div class="notion-text notion-block-1f7beb961d72810ba97dfa28befb20cc">Java 通过垃圾回收器对<b>不再使用的对象</b>完成自动回收。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d7281b8bd43c1070e410e66" data-id="1f7beb961d7281b8bd43c1070e410e66"><span><div id="1f7beb961d7281b8bd43c1070e410e66" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281b8bd43c1070e410e66" title="标记可回收"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">标记可回收</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d7281688fc6db0b87003938" data-id="1f7beb961d7281688fc6db0b87003938"><span><div id="1f7beb961d7281688fc6db0b87003938" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281688fc6db0b87003938" title="常见对象引用"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">常见对象引用</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72814a934bf3df692620ad" style="list-style-type:decimal"><li>Strongly Re-ference
指在程序代码中<b>普遍存在</b>的引用赋值。
无论任何情况，只要强引用关系存在，GC 永远不会回收被引用的对象。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d728153b72ccf1e9a295426" style="list-style-type:decimal"><li>Soft Reference
描述非必须对象。
JDK 提供 SoftReference Class 实现，主要<b>应用于缓存</b>。
只被软引用关联的对象，在系统将要发生内存溢出异常前，会被列入回收范围进行二次回收，回收后若仍没有足够内存，才会抛出 OOM。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d72814eb4dbfa371578aa35" style="list-style-type:decimal"><li>Weak Reference
描述非必须对象。
JDK 提供 WeakReference Class 实现，弱引用<b>主要在 ThreadLocal 应用</b>。
GC 工作时无论当前内存是否足够都会回收掉只被弱引用关联的对象。</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d728116bf3dfc5089ea4d23" style="list-style-type:decimal"><li>Phantom Reference
最弱引用关系。
一个对象是否有虚引用的存在，完全不会对其生存时间构成影响，也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。<b>常规开发不会使用。</b></li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d0a544d44a259de178" style="list-style-type:decimal"><li>终结器引用
指在对象需要被回收时，终结器引用会关联对象并放置在 Finalizer 类中的引用队列中，在稍后由 FinalizerThread 线程从队列中获取对象，然后执行对象的 fianlize 方法，在对象第二次被回收时，该对象才真正被回收。
这个过程中可以在 finalize 方法中再将自身对象使用强引用关联。
在最终清除资源之前，给予一定缓冲区来进行必要的处理和恢复操作。<b>常规开发不会使用。</b></li></ol><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d72812ea909deaaa9fc8a15" data-id="1f7beb961d72812ea909deaaa9fc8a15"><span><div id="1f7beb961d72812ea909deaaa9fc8a15" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72812ea909deaaa9fc8a15" title="判断对象存活"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">判断对象存活</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d68c61d861a26516e7" style="list-style-type:decimal"><li>引用计数法
引用计数法会为每个对象维护一个引用计数器，当对象被引用时加1，取消引用时减1.
但每次引用和取消引用都需要维护计数器，影响系统性能。同时存在循环引用，出现对象无法回收的问题。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d72818e9a45e22eefd47194" style="list-style-type:decimal"><li><b>可达性分析算法
</b>可达性分析算法将对象分为两类：垃圾回收的<b>根对象</b>和<b>普通对象，</b>对象与对象之间存在引用关系。
可达性分析算法指的就是如果某个对象到 GC Root 是可达的，该对象就不会被回收。
JVM 采用此方法判断对象是否可回收。
GC Root 大致有：</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d72818e9a45e22eefd47194" style="list-style-type:lower-alpha"><li>线程 Thread 对象，引用线程栈帧中的方法参数、局部变量。</li><li>系统类加载器加载的 java.lang.Class 独享，引用类中的静态变量。</li><li>监视器对象，用来保存同步锁 synchronized 关键字持有对象。</li><li>本地方法调用时使用的对象。</li></ol></ol><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d7281e88427e81b65f91067" data-id="1f7beb961d7281e88427e81b65f91067"><span><div id="1f7beb961d7281e88427e81b65f91067" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281e88427e81b65f91067" title="方法区回收"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">方法区回收</span></span></h4><div class="notion-text notion-block-1f7beb961d728155a2f3e74c3e320e45">主要回收废弃常量和不再使用的类。</div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281bda0d9e3f72a802346" style="list-style-type:decimal"><li>手动触发回收
System.gc()，但是这并不会立即回收垃圾，仅仅是向 JVM 发送垃圾回收请求，是否需要执行由 JVM 判断。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281b29a37e0bd136e60d2" style="list-style-type:decimal"><li>自动判定回收</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d7281b29a37e0bd136e60d2" style="list-style-type:lower-alpha"><li>此类所有实例对象都已经被回收，Java Heap 中不存在任何派生子类实例。</li><li>加载该类的类加载器已经被回收，也就是说自定义类只要被加载就永远不会被回收，因为它们的加载器是应用加载器。</li><li>该类对应的 java.lang.Class 对象没有在任何地方被引用，无法在任何地方通过反射访问该类方法。</li></ol></ol><div class="notion-text notion-block-1f7beb961d72813cb91af7125635698e">JVM 被允许对满足上述条件的类进行回收。</div><div class="notion-text notion-block-1f7beb961d728183aff0d2730612f43f">HotSpot 提供参数 -Xnoclassgc 参数控制是否回收。</div><div class="notion-text notion-block-1f7beb961d72811fb8e6f75cc8fe6767">在大量使用反射、动态代理等字节码框架，动态生成 JSP 这类频繁自定义类加载器的场景中，通常需要 JVM 具备类型卸载能力，以保证不会对方法区造成过大的内存压力。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d7281f7b20cfb18a63a7905" data-id="1f7beb961d7281f7b20cfb18a63a7905"><span><div id="1f7beb961d7281f7b20cfb18a63a7905" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281f7b20cfb18a63a7905" title="堆回收"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">堆回收</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d72813d826bc573225fcf64" data-id="1f7beb961d72813d826bc573225fcf64"><span><div id="1f7beb961d72813d826bc573225fcf64" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72813d826bc573225fcf64" title="垃圾回收算法"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">垃圾回收算法</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281fbae04d48eaa4fcfd3" style="list-style-type:decimal"><li>Mark-Sweep
<b>标记所有需要回收对象，标记完成后，统一回收所有被标记对象。</b>
这种方法有以下缺点：</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d7281fbae04d48eaa4fcfd3" style="list-style-type:lower-alpha"><li>执行效率不稳定，标记和清除的效率随对象数量增长而降低。</li><li>内存空间碎片化，标记清除后产生大量不连续内存碎片，可能导致无法为大对象分配空间而不得不提前触发另一次垃圾回收。</li></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d592bddf1babb261e3" style="list-style-type:decimal"><li>Marker-Compact
<b>标记过程和 Marker-Sweep 相同</b>，但是后续步骤不是直接对回收对象进行清理，而是<b>让所有对象都向内存空间的一端移动，然后直接清理掉边界以外的内存。</b>
这问题，但是移动存活对象并更新所有引用是一种相当负重的操作，而且这种移动对象，像这样长时间的停顿被表述 （Stop The World）。
另外 Mark-Sweep 算法也是需要停顿用户线程来标记、清理可回收对象的，只是时间相对较短。</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d7281d592bddf1babb261e3" style="list-style-type:lower-alpha"><div class="notion-text notion-block-1f7beb961d7281089923f614b4e79aaf"><span class="notion-inline-underscore">解决了 Marke-Sweep 算法带来的内存空间碎片化</span></div><div class="notion-text notion-block-1f7beb961d7281449730dc100ca46a2a"><span class="notion-inline-underscore">操作必须全程暂停用户应用程序才能进行</span></div><div class="notion-text notion-block-1f7beb961d72816f8f49c3e18299e274"><span class="notion-inline-underscore">STW</span></div></ol></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d728103be12f3686ba6f23d" style="list-style-type:decimal"><li>Semispace Copying
<b>将 Java Heap 按容量划分为大小相等的两块，每次只使用其中的一块。当这一块的内存用尽，就将仍然存活的对象复制到另外一块内存，并把已经使用的内存空间一次性清理。</b>对于大多数对象可回收的情况，算法需要复制的就是极少数的对象。而且每次都是针对整个半区进行内存回收，分配内存的时候也不用考虑空间碎片。
但是如果内存中大多数对象存活，将会产生大量内存间复制的开销；</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281e2afd7e6ffc2cd56f2" style="list-style-type:decimal"><li>Generational Collection
开发者观察到这样一些事实：</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d7281e2afd7e6ffc2cd56f2" style="list-style-type:lower-alpha"><li>绝大多数对象都是朝生夕灭。</li><li>经过垃圾回收次数越多的对象就越难以消亡。</li><li>跨代引用相对于同代引用来说仅占极少数。</li></ol></ol><div class="notion-text notion-block-1f7beb961d7281afba0dc0b1fd37c7aa">于是<b>收集器进一步划分 Java Heap 为更多区域，然后将回收对象根据其年龄（经过垃圾收集的次数）分配到不同区域存储</b>，不同区域可以采用适应环境的回收算法。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1f7beb961d72811795f0d1ec92673311"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://cdn.nlark.com/yuque/0/2024/png/40820000/1729060104735-dc364ab8-460b-4bae-80dd-4b1827d0efed.png?spaceId=f58ff682-9d29-4b29-b781-b313300f1828&amp;t=1f7beb96-1d72-8117-95f0-d1ec92673311" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-1f7beb961d7281a78754d25c76522ea2">具体的说，内存区域被划分为 Young 和 Old，前者存储存活时间较短的对象，后者存放存活时间较长的对象。其中 Young 又被进一步划分为 Eden 和 Survivor 区域。（默认 Eden 和 Survivor 大小比例 8:2）而 Survivor 又被划分为 From 和 To 区域。</div><div class="notion-text notion-block-1f7beb961d7281799642cd47be024ef7">新创建对象会被放入 Young:Eden 区域，随着对象增多到达上限，触发所谓 <b><span class="notion-inline-underscore">Young GC 也叫 Minor GC</span></b>。</div><div class="notion-text notion-block-1f7beb961d7281eeb916d1888e3b60e8">Young GC 类似于 Semispace Copying 算法，回收 Young:Elden 和 Young:Survivor:From 区域内的可回收对象，其他对象放入 Young:Survivor:To 区域并交换 From 区域和 To 区域（这里的交换是逻辑交换），倘若 Young:Survivor:To 不足以容纳所有对象触发<b>分配担保机制</b>即多余对象直接进入老年代。</div><div class="notion-text notion-block-1f7beb961d72816daf75cf0b6ee676a6">每次 Young GC 会记录对象年龄，当对象年龄达到阈值，对象晋升至 Old 区域，如此积累，当 Old 区域空间不足时，先尝试触发 Young GC，再紧接着触发 Full GC。<b><span class="notion-inline-underscore">Full GC </span></b>会对整个 Java Heap 进行垃圾回收，倘若仍然无法腾出空间，当新对象继续放入 Old 时抛出 OOM 异常。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1f7beb961d72811fb770fbea28a61140" data-id="1f7beb961d72811fb770fbea28a61140"><span><div id="1f7beb961d72811fb770fbea28a61140" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72811fb770fbea28a61140" title="垃圾回收器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">垃圾回收器</span></span></h4><div class="notion-text notion-block-1f7beb961d728158b2f0f62d0b51ecea">垃圾回收器是垃圾回收算法的具体实现。</div><div class="notion-text notion-block-1f7beb961d7281df995af7332f10d1bb"><b>经典搭配：</b></div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281e58bdce03e95a37942" style="list-style-type:decimal"><li><b>Serial &amp; Serial Old</b></li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d7281e58bdce03e95a37942" style="list-style-type:lower-alpha"><li>Serial：单线程串行回收年轻代（Semispace Copying），单核 CPU 表现较好。</li><li>Serial Old：单线程串行回收老年代（Sweep-Compact）</li></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d72816c8017d298c9fa9cc0" style="list-style-type:decimal"><li><b>ParNew &amp; CMS</b></li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d72816c8017d298c9fa9cc0" style="list-style-type:lower-alpha"><li>ParNew：多线程回收年轻代（Semispace Copying）</li><li>CMS：并发回收老年代（Mark-Sweep），垃圾回收停顿时间较短。</li></ol></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d728160a872ebb929906ee7" style="list-style-type:decimal"><li><b>Parallel Scavenge &amp; Parallel Old</b></li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d728160a872ebb929906ee7" style="list-style-type:lower-alpha"><li>Parallel Scavenge，多线程并行回收 Young（Semispace Copying），能自动调整堆内存。</li><li>Parallel Old，多线程并发回收 Old（Mark-Sweep），多核 CPU 效率高。</li></ol></ol><div class="notion-text notion-block-1f7beb961d7281c19465c7b2dd2adac2">不同于上述垃圾回收器，G1 收集器建立了<b>可预测的停顿时间模型</b>（_<b>Pause Prediction Model</b>__，即能够实现在一个长度为 m 毫秒的时间片段内，消耗在垃圾回收的时间大概率不超过 n 毫秒_）。</div><div class="notion-text notion-block-1f7beb961d72812289b9edf37952a2c3"><b>G1 不再坚持固定大小以及固定数量的分代区域划分，Java Heap 被划分为多个大小相等的 Region（Region 可以根据需要扮演 Eden、Survivor 或者 Old）。</b></div><details class="notion-toggle notion-block-1f7beb961d72811690e5ddff2f2e30a9"><summary>将Java堆分成多个独立Region后，Region里面存在的跨Region引用对象如何解决？记忆集。</summary><div><div class="notion-text notion-block-1f7beb961d7281ec8811f19cfde953e7"><b>使用记忆集避免全堆作为GC Roots扫描</b>，但在G1收集器上记忆集的应用其实要复杂很多，它的每个Region都维护有自己的记忆集，这些记忆集会记录下别的Region指向自己的指针，并标记这些指针分别在哪些卡页的范围之内。</div><div class="notion-text notion-block-1f7beb961d72812ba106f5d6514c7d02">G1的记忆集在存储结构的本质上是一种哈希表，Key是别的Region的起始地址，Value是一个集合，里面存储的元素是卡表的索引号。</div><div class="notion-text notion-block-1f7beb961d72814bb923e1b8100ba6ce">这种“双向”的卡表结构（卡表是“我指向谁”，这种结构还记录了“谁指向我”）比原来的卡表实现起来更复杂，同时由于Region数量比传统收集器的分代数量明显要多得多，因此G1收集器要比其他的传统垃圾收集器有着更高的内存占用负担。</div><div class="notion-text notion-block-1f7beb961d728178b9b1d88bffa84256">根据经验，G1至少要耗费大约相当于Java堆容量10%至20%的额外内存来维持收集器工作。</div></div></details><div class="notion-text notion-block-1f7beb961d7281bbb11ed459a0fdd103">G1 将 Region 作为单次回收的最小单元，即每次收集到的内存空间都是 Region 的整数倍，这样可以有计划的避免在整个 Java Heap 中进行全区域的垃圾收集。同时 G1 会跟踪每个 Region 的回收优先级（受回收获得空间和回收所需时间影响），每次根据用户设置允许收集停顿时间（默认 200 ms）优先处理回收优先级最高的 Region，这也是 Garbage First 名称的由来。</div><div class="notion-text notion-block-1f7beb961d7281bdb11ae6ca8f43daea"><b>这种使用 Region 划分内存空间、具有优先级的区域回收方式，保证 G1 在有限的时间内获取尽可能高的收集效率。</b></div><ul class="notion-list notion-list-disc notion-block-1f7beb961d728145a4a7e92f19370f2b"><li><b>Young GC当 G1 判断年轻代不足（默认 Eden / Java Heap = 60%），触发 Young GC。</b>
回收 Eden 和 Survivor 中可回收对象，会导致 STW，G1 支持设置最大暂停时间并尽力保证。</li><ul class="notion-list notion-list-disc notion-block-1f7beb961d728145a4a7e92f19370f2b"><li>标记 Eden 区域和 Survivor 区域中存活对象。</li><li>根据设定最大暂停时间复制存活对象到新的 Survivor 区域，清空标记区域。（过程中会记录每次垃圾回收时 Eden 和 Survivor 的平均耗时作为下次回收的参考依据）</li><li>当某个存活对象年龄达到阈值（默认 15），移入 Old 区域。</li><li>某个对象大小超过 Region / 2，直接放入 Humongous（Old），对象过大可以横跨多个 Region。</li></ul></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d72817588afdd490ce10397"><li><b>Mixed GC
当 G1 判断老年代过大（默认 Old / Java Heap = 45%），触发 Mixed GC。
Mixed GC 回收部分 Eden、Old 以及 Humongous</b>，选择依据 G1 维护的优先级列表，<b>回收采用复制算法</b>，<b>如果清理过程中没有足够的 Region 存放转移对象触发 Full GC</b>，单线程执行 Mark Sweep 算法，此时会导致不受控制的用户线程停顿。</li></ul><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1fbbeb961d7280e8accec408f897b18e" data-id="1fbbeb961d7280e8accec408f897b18e"><span><div id="1fbbeb961d7280e8accec408f897b18e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1fbbeb961d7280e8accec408f897b18e" title="参数调优"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">参数调优</span></span></h2><div class="notion-blank notion-block-1fbbeb961d7280e49824f912e2eaa143"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Nginx]]></title>
            <link>https://nowave.cloud//article/1f7beb96-1d72-8019-b3ef-f040690a4a8f</link>
            <guid>https://nowave.cloud//article/1f7beb96-1d72-8019-b3ef-f040690a4a8f</guid>
            <pubDate>Sun, 18 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Nginx 是一个高性能的Web服务器，采用master-worker架构，其中master进程管理多个worker进程，worker进程负责处理请求。Nginx支持多种配置项，包括基本配置、优化性能的配置、事件类配置等，能够有效管理网络连接、内存和磁盘资源。Nginx还具备反向代理功能，通过负载均衡将请求分发到上游服务器，降低服务器负载。其事件驱动架构和模块化设计使其具备高并发处理能力和良好的可扩展性。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-full-width notion-block-1f7beb961d728019b3eff040690a4a8f"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1f7beb961d7281eaa73fe9dd1ff718d7" data-id="1f7beb961d7281eaa73fe9dd1ff718d7"><span><div id="1f7beb961d7281eaa73fe9dd1ff718d7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281eaa73fe9dd1ff718d7" title="Nginx 功能与配置"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Nginx 功能与配置</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d72819c9408e62f011dae7f" data-id="1f7beb961d72819c9408e62f011dae7f"><span><div id="1f7beb961d72819c9408e62f011dae7f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72819c9408e62f011dae7f" title="2.1 master-worker"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2.1 master-worker</span></span></h4><div class="notion-text notion-block-1f7beb961d7281af8e75e80206d9981c">正式提供服务的产品环境下，部署 Nginx 使用一个 master 进程管理若干 worker 进程，一般情况下，worker 进程数量等同于服务器的 CPU 核心数。</div><div class="notion-text notion-block-1f7beb961d7281308abac0ae5580ce24">worker 进程真正提供互联网服务。</div><div class="notion-text notion-block-1f7beb961d728198a29ff1b5fd3195ec">master 负责监控 worker 进程。</div><div class="notion-text notion-block-1f7beb961d7281b2a001dcc9f899b3b2">当然 Nginx 支持单进程提供服务（master），采用 master-workers 的好处：</div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281b2b43be8d945ab9f01" style="list-style-type:decimal"><li>master 可以专注于管理，并且为程序员提供命令行服务。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d72812b8e45f02f47063246" style="list-style-type:decimal"><li>worker 可以充分利用常见 SMP 多核架构，从而实现围观上真正的并发处理。</li></ol><details class="notion-toggle notion-block-1f7beb961d7281eca5f4cc09ae9612b1"><summary>为什么要把worker进程数量设置得与CPU核心数量一致呢？</summary><div><div class="notion-text notion-block-1f7beb961d7281a5ae39f3da26078415">这正是 Nginx与Apache服务器的不同之处。</div><div class="notion-text notion-block-1f7beb961d728154b4cbff746d63891d">在<b>Apache上每个进程在一个时刻只处理一个请求</b>，因此，如果希望Web服务器拥有并发处理的请求数更多，就要把 Apache的进程或线程数设置得更多，通常会达到一台服务器拥有几百个工作进程，这样<b>大量的进程间切换将带来无谓的系统资源消耗</b>。</div><div class="notion-text notion-block-1f7beb961d728166a227c6f963653614">而 Nginx则不然，<b>一个worker进程可以同时处理的请求数只受限于内存大小</b>，而且在架构设计上，不同的worker进程之间处理并发请求时几乎没有同步锁的限制，worker进程通常不会进入睡眠状态，因此，当Nginx上的进程数与CPU核心数相等时（最好每一个worker进程都绑定特定的CPU核心），<b>进程间切换的代价是最小的。</b></div></div></details><div class="notion-blank notion-block-1f7beb961d72812b9e29c2b89d944b16"> </div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d728135ac8ce3116fa5b5da" data-id="1f7beb961d728135ac8ce3116fa5b5da"><span><div id="1f7beb961d728135ac8ce3116fa5b5da" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d728135ac8ce3116fa5b5da" title="2.2 通用语法"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2.2 通用语法</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d72811dad2fef47f2996865" data-id="1f7beb961d72811dad2fef47f2996865"><span><div id="1f7beb961d72811dad2fef47f2996865" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72811dad2fef47f2996865" title="块配置项"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">块配置项</span></span></h4><div class="notion-text notion-block-1f7beb961d72812196fff5a2a1ccc24b">块配置项名和大括号。</div><blockquote class="notion-quote notion-block-1f7beb961d728187a6aada5ca1e9de5c"><div>events、http、server、location、upstream等都是块配置项，块配置项之后是否如“location/webstatic{…}”那样在后面加上参数，取决于解析这个块配置项的模块，不能一概而论，但块配置项一定会用大括号把一系列所属的配置项全包含进来，表示大括号内的配置项同时生效。所有的事件类配置都要在events块中，http、server等配置也遵循这个规定。</div></blockquote><blockquote class="notion-quote notion-block-1f7beb961d7281a9b573cea2d1c4e39f"><div>块配置项可以嵌套。内层块直接继承外层块，例如，上例中，server块里的任意配置都是基于http块里的已有配置的。当内外层块中的配置发生冲突时，究竟是以内层块还是外层块的配置为准，取决于解析这个配置项的模块，第4章将会介绍http块内配置项冲突的处理方法。</div></blockquote><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d728141a877fdfe557b46d1" data-id="1f7beb961d728141a877fdfe557b46d1"><span><div id="1f7beb961d728141a877fdfe557b46d1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d728141a877fdfe557b46d1" title="配置项"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">配置项</span></span></h4><div class="notion-text notion-block-1f7beb961d7281ef8c75cf0e234c60f0">配置项名+配置项值</div><blockquote class="notion-quote notion-block-1f7beb961d7281388b27cfc7434579fb"><div>在行首的是配置项名，这些配置项名必须是Nginx的某一个模块想要处理的，否则Nginx会认为配置文件出现了非法的配置项名。配置项名输入结束后，将以空格作为分隔符。</div></blockquote><blockquote class="notion-quote notion-block-1f7beb961d7281649daac5e82f4de1e7"><div>其次是配置项值，它可以是数字或字符串（当然也包括正则表达式）。</div><div class="notion-text notion-block-1f7beb961d72815db907f89f45b10ee9">针对一个配置项，既可以只有一个值，也可以包含多个值，配置项值之间仍然由空格符来分隔。当然，一个配置项对应的值究竟有多少个，取决于解析这个配置项的模块。</div></blockquote><blockquote class="notion-quote notion-block-1f7beb961d72818fbfa4c2d00917d9d0"><div>有些模块允许在配置项中使用变量，使用它的时候前面要加上$符号。需要注意的是，这种变量只有少数模块支持，并不是通用的。</div><div class="notion-text notion-block-1f7beb961d72811885ddfbfd0d3ea347">许多模块在解析请求时都会提供多个变量（如本章后面提到的http coremodule、http proxy module、http upstream module等），以使其他模块的配置可以即时使用。</div></blockquote><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d728161b030e6b90f60ecfc" data-id="1f7beb961d728161b030e6b90f60ecfc"><span><div id="1f7beb961d728161b030e6b90f60ecfc" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d728161b030e6b90f60ecfc" title="2.3 基本配置"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2.3 基本配置</span></span></h4><div class="notion-text notion-block-1f7beb961d7281dda8cdd2e81e427a0c">Nginx在运行时，至少必须加载几个核心模块和一个事件类模块。</div><div class="notion-text notion-block-1f7beb961d7281b7bed1e03b4313d377">这些模块运行时所支持的配置项称为基本配置（所有其他模块执行时都依赖的配置项）。</div><details class="notion-toggle notion-block-1f7beb961d72810692aae7c90b8125c9"><summary>用于调试进程和定位问题的配置项</summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d728152ab06e76084ba4fcf" style="list-style-type:decimal"><li><b>daemon on|off</b>是否以守护进程方式运行Nginx（默认 daemon on;）守护进程（daemon）是脱离终端并且在后台运行的进程。它脱离终端是为了避免进程执行过程中的信息在任何终端上显示，这样一来，进程也不会被任何终端所产生的信息所打断。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d72813d9c43e4e1caea1746" style="list-style-type:decimal"><li><b>master_process on|off;</b>是否以master/worker方式工作（默认 master_process on;）</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281b488e6e3407fdbf929" style="list-style-type:decimal"><li><b>error_log/path/file level;</b>error日志设置（默认 error_log logs/error.log error;）/path/file 可以是一个具体文件，日志保存在该文件；/path/file 也可以是/dev/null，这样不会输出日志；/path/file 也可以是stderr，这样日志会输出到标准错误文件中。level是日志的输出级别，取值范围是debug、info、notice、warn、error、crit、alert、emerg，从左至右级别依次增大。当设定为一个级别时，大于或等于该级别的日志都会被输出到/path/file文件中，小于该级别的日志则不会输出。</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d728174a41afc6fa9524605" style="list-style-type:decimal"><li><b>worker_rlimit_core size</b>限制coredump核心转储文件的大小。在 Linux 系统中，当进程发生错误或收到信号而终止时，系统会将进程执行的内存内容（核心影响）写入文件（core 文件），以作为调试之用，这就是所谓核心转储（core dumps）。当 Nginx 进程出现非法操作导致进程直接被操作系统强制结束时，就会生成核心转储文件。</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d7281dd8410eeafe5b3d206" style="list-style-type:decimal"><li><b>working_directory path;</b>指定coredump文件生成目录。worker 进程的工作目录，确保 worker 进程有权向指定目录写入文件。</li></ol></div></details><details class="notion-toggle notion-block-1f7beb961d728121bbb7f6367a1ddbd3"><summary>正常运行的配置项</summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72816c80edea8dad8f5dbe" style="list-style-type:decimal"><li>env VAR|VAR=VALUE定义环境变量</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281c9acfbff6ef12f4952" style="list-style-type:decimal"><li>include/path/file;嵌入其他配置文件到当前 nginx.conf</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281b8910ed2aa941a6488" style="list-style-type:decimal"><li>pid path/file;（默认 pid logs/nginx.pid;）保存 master 进程 ID 的 pid 文件存放路径。</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d728182abb3e095a2a1e1fb" style="list-style-type:decimal"><li>user username[groupname];（默认 user nobody nobody;）user用于设置master进程启动后，fork出的worker进程运行在哪个用户和用户组下。当按照“user username;”设置时，用户组名与用户名相同。若用户在configure命令执行时使用了参数–user=username和–group=groupname，此时nginx.conf将使用参数中指定的用户和用户组。</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d728198822ee86013a57976" style="list-style-type:decimal"><li>worker_rlimit_sigpending limit;设置每个用户发往Nginx的信号队列的大小。也就是说，当某个用户的信号队列满了，这个用户再发送的信号量会被丢掉。</li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d7281ff91f3dbf90a82d8f7" style="list-style-type:decimal"><li>worker_rlimit_nofile limit;指定Nginx worker进程可以打开的最大句柄描述符个数</li></ol></div></details><details class="notion-toggle notion-block-1f7beb961d7281c190acea3c05d9f07f"><summary>优化性能的配置项</summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281fba9c8d6180dbf7829" style="list-style-type:decimal"><li><b>worker_processes number;（默认 worker_processes 1;）</b>在master/worker运行方式下，定义worker进程的个数。<em>worker进程的数量会直接影响性能，这实际上与业务需求有关。每个worker进程都是单线程的进程，它们会调用各个模块以实现多种多样的功能。如果这些模块确认不会出现阻塞式的调用，那么，有多少CPU内核就应该配置多少个进程；反之，如果有可能出现阻塞式调用，那么需要配置稍多一些的worker进程。多worker进程可以充分利用多核系统架构，但若worker进程的数量多于CPU内核数，那么会增大进程间切换带来的消耗（Linux是抢占式内核）。一般情况下，用户要配置与CPU内核数相等的worker进程，并且使用下面的worker_cpu_affinity配置来绑定CPU内核。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d728105896ed4260449c555" style="list-style-type:decimal"><li><b>worker_cpu_affinity cpumask[cpumask…]</b>绑定Nginx worker进程到指定的CPU内核。<em>假定每一个worker进程都是非常繁忙的，如果多个worker进程都在抢同一个CPU，那么这就会出现同步问题。反之，如果每一个worker进程都独享一个CPU，就在内核的调度策略上实现了完全的并发。（worker_cpu_affinity配置仅对Linux操作系统有效。Linux操作系统使用sched_setaffinity()系统调用实现这个功能。）</em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281f8bd03da65b7fbe2d6" style="list-style-type:decimal"><li>ssl_engine device；<em>如果服务器上有SSL硬件加速设备，那么就可以进行配置以加快SSL协议的处理速度。</em></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281a59574e7bbb519b3ef" style="list-style-type:decimal"><li>worker_priority nice;（默认 worker_priority 0;）该配置项用于设置Nginx worker进程的nice优先级。<em>在Linux或其他类UNIX操作系统中，当许多进程都处于可执行状态时，将按照所有进程的优先级来决定本次内核选择哪一个进程执行。进程所分配的CPU时间片大小也与进程优先级相关，优先级越高，进程分配到的时间片也就越大。这样，优先级高的进程会占有更多的系统资源。优先级由静态优先级和内核根据进程执行情况所做的动态调整共同决定。nice值是进程的静态优先级，它的取值范围是–20~+19，–20是最高优先级，+19是最低优先级。因此，如果用户希望Nginx占有更多的系统资源，那么可以把nice值配置得更小一些，但不建议比内核进程的nice值（通常为–5）还要小。</em></li></ol></div></details><details class="notion-toggle notion-block-1f7beb961d7281d0addbcd9f3af5888d"><summary>事件类配置项</summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72812aa54dffe17add79ee" style="list-style-type:decimal"><li><b>accept_mutex[on|off]</b>（默认 accept_mutext on;）是否打开accept锁<em>accept_mutex 是 Nginx 的负载均衡锁，可以让多个 worker 进程轮流的、序列化的与新的客户端建立 TCP 连接。当一个 worker 进程建立的连接数量达到 worker_connections 配置的最大连接数的 7/8，会大大减小该 worker 进程试图建立新 TCP 连接的机会，以此实现所有 worker 进程处理的客户端请求数尽量接近。accept锁默认是打开的，如果关闭它，那么建立TCP连接的耗时会更短，但worker进程之间的负载会非常不均衡，因此不建议关闭它。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d728172aa61f2f93732e515" style="list-style-type:decimal"><li><b>lock_file path/file;</b>（默认 lock_file logs/nginx.lock;）lock文件的路径<em>accept锁可能需要这个lock文件，如果accept锁关闭，lock_file配置完全不生效。如果打开了accept锁，并且由于编译程序、操作系统架构等因素导致Nginx不支持原子锁，这时才会用文件锁实现accept锁，这样lock_file指定的lock文件才会生效。</em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d72812a8d7bdadb84c7c079" style="list-style-type:decimal"><li><b>accept_mutex_delay Nms;</b>（默认 accept_mutex_delay 500ms;）使用accept锁后到真正建立连接之间的延迟时间。<em>在使用accept锁后，同一时间只有一个worker进程能够取到accept锁。这个accept锁不是阻塞锁，如果取不到会立刻返回。如果有一个worker进程试图取accept锁而没有取到，它至少要等accept_mutex_delay定义的时间间隔后才能再次试图取锁。</em></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281529b18c497f44d382b" style="list-style-type:decimal"><li><b>multi_accept[on|off];</b>（默认 multi_accept off;）是否批量建立新连接。<em>当事件模型通知有新连接时，尽可能地对本次调度中客户端发起的所有TCP请求都建立连接。</em></li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d7281f6afb5cd5276f3a57f" style="list-style-type:decimal"><li><b>use</b>[kqueue|rtsig|epoll|/dev/poll|select|poll|eventport];（Nginx会自动使用最适合的事件模型。）选择事件模型。<em>对于Linux操作系统来说，可供选择的事件驱动模型有poll、select、epoll三种。epoll当然是性能最高的一种，在9.6节会解释epoll为什么可以处理大并发连接。</em></li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d7281aeb3dce05767e38008" style="list-style-type:decimal"><li><b>worker_connections number;</b>每个worker的最大连接数。</li></ol></div></details><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d72812fb180c6d1f2bb3883" data-id="1f7beb961d72812fb180c6d1f2bb3883"><span><div id="1f7beb961d72812fb180c6d1f2bb3883" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72812fb180c6d1f2bb3883" title="2.4 ngx_http_core_module 配置静态代理服务器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2.4 ngx_http_core_module 配置静态代理服务器</span></span></h4><div class="notion-text notion-block-1f7beb961d728173a89fcce5e88f4633"><em>静态Web服务器的主要功能由ngx_http_core_module模块（HTTP框架的主</em></div><div class="notion-text notion-block-1f7beb961d7281d5b2cbf8c02a26b793"><em>要成员）实现。本节主要讨论如何配置一个包含基本功能的静态Web服务</em></div><div class="notion-text notion-block-1f7beb961d728160a45fd66880acd73d"><em>器，文中会完整地说明ngx_http_core_module模块提供的配置项及变量的用</em></div><div class="notion-text notion-block-1f7beb961d7281b3b99cf68f8a083a4e"><em>法，但不会过多说明其他HTTP模块的配置项。</em></div><details class="notion-toggle notion-block-1f7beb961d728121bf08e9ae46edd848"><summary><b>虚拟主机与请求分发</b></summary><div><div class="notion-text notion-block-1f7beb961d72816b87dbe65046d69c02"><em><b>存在多个主机域名对应相同 IP 地址的情况，此时可以按照 server_name 并通过 server 块来定义虚拟主机，每个 server 块就是一个虚拟主机，并且只处理阈值相对应的主机域名请求。这样一台服务器上的 Nginx 就能以不同的方式处理访问不同主机域名的 HTTP 请求。</b></em></div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72819c9c2de6f2137b39e0" style="list-style-type:decimal"><li><b>listen address:port[options]；listen 80；server；</b><em><b>listen参数 options 决定Nginx服务如何监听端口。default：将所在 server 块设为 Web 服务默认块。倘若没有这个参数 nginx.conf 中的首个 server 块将作为默认 server 块。当一个请求无法匹配文件中的所有主机域名的时候，就会选择默认虚拟主机。backlog = num:表示 TCP 中 backlog 队列大小，默认-1，表示不予设置。在 TCP 建立三次握手过程中，进程还没有开始处理监听句柄，backlog 队列会放置这些新连接，若队列满，新的连接会建立连接失败。rcvbuf=sizesndbuf=sizeaccept_filterdeferred：设置参数后，若用户发起建立连接请求，并完成 TCP 三次握手，内核也不会为此次连接调度 worker 进程处理，只有用户发送请求数据时内核才会唤醒 worker 进程处理连接。这个参数适用于高并发的情况下，减轻 worker 进程的负担。bind：绑定当前端口/地址对。ssl:在当前监听的端口上建立的连接必须基于 SSL 协议。</b></em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d728163a1e8d6ba825b76f9" style="list-style-type:decimal"><li><b>server_name name[···]server_name &quot;&quot;server</b><em><b>server_name 后可以附加多个主机名称。在开始处理 HTTP 请求时，Nginx 会取出 header 头中的 Host 与 server_name 进行匹配来决定处理请求的 server 块。可能会有 Host 与多个 server_name 匹配，这时会根据匹配优先级来选择实际处理的 server 块。Nginx 正是使用此配置项针对特定域名的请求提供不同服务，以此实现虚拟主机功能。</b></em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d72812892e8e3a9a8be2960" style="list-style-type:decimal"><li><b>server_names_hash_bucket_size size；server_names_hash_bucket_size 32|64|128http、server、location</b><em><b>为了提高快速寻找到相应server name的能力，Nginx使用散列表来存储server name。server_names_hash_bucket_size设置了每个散列桶占用的内存大小。</b></em></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281e5adbdf37f74248e69" style="list-style-type:decimal"><li><b>server_names_hash_max_size size；server_names_hash_max_size 512;http、server、location</b><em><b>server_names_hash_max_size会影响散列表的冲突率。</b></em></li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d49029f0c09e4d8115" style="list-style-type:decimal"><li><b>server_name_in_redirect on|off;server_name_in_redirect on;http、server或者location</b><em><b>重定向主机名称的处理该配置需要配合server_name使用。在使用on打开时，表示在重定向请求时会使用server_name里配置的第一个主机名代替原先请求中的Host头部，而使用off关闭时，表示在重定向请求时使用请求本身的Host头部。</b></em></li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d72819ea449dabe55644ea2" style="list-style-type:decimal"><li><b>location[=|||^~|@]/uri/{…}server</b><em><b>location 会尝试根据用户请求中的 URL 来匹配上面的/uri 表达式，（uri 参数可以使用正则表达式）如果可以匹配，就选择 location{}块中的配置来处理用户请求。当然 location 的匹配方式多样：=表示完全匹配。表示字母大小写敏感匹配。表示忽略字母大小写匹配。^~表示前半部分相同匹配。@表示仅用于 Nginx 服务内部请求之间的重定向，带有@的location不直接处理用户请求。/表示匹配所有请求。</b></em></li></ol></div></details><details class="notion-toggle notion-block-1f7beb961d7281d5b69dd3764c3a81c9"><summary><b>文件路径的定义</b></summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72819c9230d311f02e1a94" style="list-style-type:decimal"><li><em><b>root path(默认 root html)</b></em><em>http、server、location、if定义资源文件相对于 HTTP 请求的根目录。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d728144b55cede7ac6ade78" style="list-style-type:decimal"><li><em><b>alias path</b></em><em>location也是用来设置文件资源路径，它与 alias 不同点主要在于如何处理 location 后面的 uri 参数。使用 alias 时会丢弃 location 后面的 uri 参数，root 则不然，它会根据完整的 uri 请求来映射。</em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d728153a41acc25c45f30aa" style="list-style-type:decimal"><li><em>index file···(默认 index index.html)http、server、location有时，访问站点时的 url 是 /，这时一般是返回网站的首页。可以后缀多个文件参数，Nginx 会按照顺序来访问这些文件，如果可以访问则直接返回文件内容并结束请求，否则尝试访问其他。</em></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281879675c54285296f0f" style="list-style-type:decimal"><li><em>error_page code [code···] /sth.htmlhttp server location if对于某个请求返回错误码时，如果匹配上 error_page 中设置的 code，则重定向到新的 URL 中。虽然重定向 URI，但返回的 HTTP 错误码与原来相同。用户可以使用 = 来更改返回的错误码，或者不指定确切的错误码，而是由重定向后的实际处理结果决定。error_page 404 =200 /empty.giferror_page 404 = /empty.gif如果不想修改 uri，可以想让这样的请求重定向到另一个 location 中进行处理：error_page 404 @fallback&lt;br /&gt;location @fallback{}</em></li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d7a7abc4ed9cc84a89" style="list-style-type:decimal"><li><em>recursive_error_pages[on|off];</em></li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d7281259b80f02e2bc3e597" style="list-style-type:decimal"><li><em>try_files path1[path2]uri;尝试按照顺序访问每一个path，如果可以有效地读取，就直接向用户返回这个path对应的文件结束请求，否则继续向下访问。如果所有的path都找不到有效的文件，就重定向到最后的参数uri上。因此，最后这个参数uri必须存在，而且它应该是可以有效重定向的。</em></li></ol></div></details><details class="notion-toggle notion-block-1f7beb961d72813d9a7cd321a5b8b3db"><summary>内存及磁盘资源的分配</summary><div><div class="notion-text notion-block-1f7beb961d72810ba5b6ce91e9223c0b"><em>处理请求时内存、磁盘资源的分配项。</em></div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281b1b4bfddb43e43712a" style="list-style-type:decimal"><li><em>client_body_in_file_only on|clean|off;（默认 client_body_in_file_only off;）http、server、locationHTTP 包体只存储到磁盘文件中。当值为非off时，用户请求中的HTTP包体一律存储到磁盘文件中，即使只有0字节也会存储为文件。当请求结束时，如果配置为on，则这个文件不会被删除（该配置一般用于调试、定位问题），但如果配置为clean，则会删除该文件。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d7b1e0e4ba8857ce18" style="list-style-type:decimal"><li><em>client_body_in_single_buffer on|off;（默认 client_body_in_single_buffer off;）http、server、locationHTTP包体尽量写入到一个内存buffer中。用户请求中的HTTP包体一律存储到内存buffer中。当然，如果HTTP包体的大小超过了下面client_body_buffer_size设置的值，包体还是会写入到磁盘文件中。</em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281bfb222f2a3bed1f99b" style="list-style-type:decimal"><li><em>client_header_buffer_size size;（默认 client_header_buffer_size 1k;）http、server存储HTTP头部的内存buffer大小。定义了正常情况下Nginx接收用户请求中HTTP header部分（包括HTTP行和HTTP头部）时分配的内存buffer大小。有时，请求中的HTTP header部分可能会超过这个大小，这时large_client_header_buffers定义的buffer将会生效。</em></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281f58ecedc3df54e38be" style="list-style-type:decimal"><li><em>large_client_header_buffers number size;（默认 large_client_header_buffers 48k; ）http、server存储超大HTTP头部的内存buffer大小。large_client_header_buffers定义了Nginx接收一个超大HTTP头部请求的buffer个数和每个buffer的大小。如果HTTP请求行（如GET/indexHTTP/1.1）的大小超过上面的单个buffer，则返回&quot;Request URI toolarge&quot;(414)。请求中一般会有许多header，每一个header的大小也不能超过单个buffer的大小，否则会返回&quot;Bad request&quot;(400)。当然，请求行和请求头部的总和也不可以超过buffer个数*buffer大小。</em></li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d72812fb934c05c748b9909" style="list-style-type:decimal"><li><em>client_body_buffer_size size;（默认 client_body_buffer_size 8k/16k;）http、server、location定义了Nginx接收HTTP包体的内存缓冲区大小。也就是说，HTTP包体会先接收到指定的这块缓存中，之后才决定是否写入磁盘。如果用户请求中含有HTTP头部Content-Length，并且其标识的长度小于定义的buffer大小，那么Nginx会自动降低本次请求所使用的内存buffer，以降低内存消耗。</em></li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d72816bae5dc376962b68f9" style="list-style-type:decimal"><li><em>client_body_temp_path dir-path[level1[level2[level3]]]（默认 client_body_temp_path client_body_temp;）http、server、location定义HTTP包体存放的临时目录。在接收HTTP包体时，如果包体的大小大于client_body_buffer_size，则会以一个递增的整数命名并存放到client_body_temp_path指定的目录中。后面跟着的level1、level2、level3，是为了防止一个目录下的文件数量太多，从而导致性能下降，因此使用了level参数，这样可以按照临时文件名最多再加三层目录。</em></li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-1f7beb961d72818a93bcd473af648095" style="list-style-type:decimal"><li><em>connection_pool_size size;（默认 connection_pool_size 256）</em><em><b>Nginx 对于每个建立成功的 TCP 连接会预先分配一个内存池</b></em><em>，size 指定内存池的初始大小，用于减少内核对于小块内存的分配次数。需要谨慎设置，因为更大的 size 会使服务器消耗的内存增多，而更小的 size 会引发更多的内存分配次数。</em></li></ol><ol start="8" class="notion-list notion-list-numbered notion-block-1f7beb961d7281f1ab08e3028bc1bbff" style="list-style-type:decimal"><li><em>request_pool_size size;（默认 request_pool_size 4k;）http、server</em><em><b>Nginx 开始处理 HTTP 请求时将会为每个请求都分配一个内存池，</b></em><em>size 指定内存池的初始大小，用于减少内核对于小块内存的分配次数。TCP 连接关闭时会销毁 connection_pool_size 指定的连接内存池，HTTP 请求结束时会销毁 request_pool_size 指定的请求内存池，但它们的创建、销毁时间并不一致。（因为一个 TCP 连接可能被复用于多个 HTTP 请求）</em></li></ol></div></details><details class="notion-toggle notion-block-1f7beb961d7281f2a14dee923c04ffc0"><summary><b>网络连接的设置</b></summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72818f93dbc9da67aa67e4" style="list-style-type:decimal"><li><em>client_header_timeout time（client_header_timeout 60;）http、server、location读取HTTP头部的超时时间。客户端与服务器建立连接后将开始接收HTTP头部，在这个过程中，如果在一个时间间隔（超时时间）内没有读取到客户端发来的字节，则认为超时，并向客户端返回408(&quot;Request timed out&quot;)响应。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281758f9fd17aac5d17f1" style="list-style-type:decimal"><li><em>client_body_timeout time（ client_body_timeout 60;）http、server、location读取HTTP包体的超时时间。此配置项与client_header_timeout相似，只是这个超时时间只在读取HTTP包体时才有效。</em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d1820af4e8d942ead0" style="list-style-type:decimal"><li><em>send_timeout time（send_timeout 60;）http、server、location发送响应的超时时间。这个超时时间是发送响应的超时时间，即Nginx服务器向客户端发送了数据包，但客户端一直没有去接收这个数据包。如果某个连接超过send_timeout定义的超时时间，那么Nginx将会关闭这个连接。</em></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281cab010ce8772784faf" style="list-style-type:decimal"><li><em>reset_timeout_connection on|off;（reset_timeout_connection off;）连接超时后将通过向客户端发送RST包来直接重置连接。这个选项打开后，Nginx会在某个连接超时后，不是使用正常情形下的四次握手关闭TCP连接，而是直接向用户发送RST重置包，不再等待用户的应答，直接释放Nginx服务器上关于这个套接字使用的所有缓存（如TCP滑动窗口）。相比正常的关闭方式，它使得服务器避免产生许多处于FIN_WAIT_1、FIN_WAIT_2、TIME_WAIT状态的TCP连接。注意，使用RST重置包关闭连接会带来一些问题，默认情况下不会开启。</em></li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d72819fbdeecc6e991b7a68" style="list-style-type:decimal"><li><em>lingering_close off|on|always;（lingering_close on;）http、server、location该配置控制Nginx关闭用户连接的方式。always表示关闭用户连接前必须无条件地处理连接上所有用户发送的数据。off表示关闭连接时完全不管连接上是否已经有准备就绪的来自用户的数据。on是中间值，一般情况下在关闭连接前都会处理连接上的用户发送的数据，除了有些情况下在业务上认定这之后的数据是不必要的。</em></li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d72816281d0f9ed6e5129cb" style="list-style-type:decimal"><li><em>lingering_time（lingering_time 30s;）http、server、locationlingering_close启用后，这个配置项对于上传大文件很有用。上文讲过，当用户请求的Content-Length大于max_client_body_size配置时，Nginx服务会立刻向用户发送413（Request entity too large）响应。但是，很多客户端可能不管413返回值，仍然持续不断地上传HTTP body，这时，经过了lingering_time设置的时间后，Nginx将不管用户是否仍在上传，都会把连接关闭掉。</em></li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-1f7beb961d728146a61ec821f0343952" style="list-style-type:decimal"><li><em>lingering_timeout（ lingering_timeout 5s;）http、server、locationlingering_close生效后，在关闭连接前，会检测是否有用户发送的数据到达服务器，如果超过lingering_timeout时间后还没有数据可读，就直接关闭连接；否则，必须在读取完连接缓冲区上的数据并丢弃掉后才会关闭连接。</em></li></ol><ol start="8" class="notion-list notion-list-numbered notion-block-1f7beb961d72815c8dfbc287d4d9fe05" style="list-style-type:decimal"><li><em>keepalive_timeout time（keepalive_timeout 75;）http、server、location一个keepalive连接在闲置超过一定时间后（默认的是75秒），服务器和浏览器都会去关闭这个连接。当然，keepalive_timeout配置项是用来约束Nginx服务器的，Nginx也会按照规范把这个时间传给浏览器，但每个浏览器对待keepalive的策略有可能是不同的。</em></li></ol><ol start="9" class="notion-list notion-list-numbered notion-block-1f7beb961d7281269ed6dcb14ad7d02e" style="list-style-type:decimal"><li><em>keepalive_requests n;（keepalive_requests 100;）一个keepalive连接上默认最多只能发送100个请求。</em></li></ol><ol start="10" class="notion-list notion-list-numbered notion-block-1f7beb961d7281ca8c04c8172b704e5c" style="list-style-type:decimal"></ol></div></details><details class="notion-toggle notion-block-1f7beb961d7281128bece6ad1665b0df"><summary><b>MIME类型的设置</b></summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72817ebf16f1832ff3d131" style="list-style-type:decimal"><li><em>type{…};http、server、location定义MIME type到文件扩展名的映射。多个扩展名可以映射到同一个MIME type。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d72816aa8b5caa3e79795a8" style="list-style-type:decimal"><li><em>default_type MIME-type;（default_type text/plain;）http、server、location当找不到相应的MIME type与文件扩展名之间的映射时，使用默认的MIME type作为HTTP header中的Content-Type。</em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281bb915ef39357accf21" style="list-style-type:decimal"><li><em>types_hash_bucket_size</em></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281a0a184ff0ba89ecabf" style="list-style-type:decimal"><li><em>types_hash_max_size</em></li></ol></div></details><details class="notion-toggle notion-block-1f7beb961d728190b1f9c74e858fb7a0"><summary><b>对客户端请求的限制</b></summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d728179ba9fc52b74418a68" style="list-style-type:decimal"><li><em>limit_except method…{…}locationNginx通过limit_except后面指定的方法名来限制用户请求。方法名可取值包括：GET、HEAD、POST、PUT、DELETE、MKCOL、COPY、MOVE、OPTIONS、PROPFIND、PROPPATCH、LOCK、UNLOCK或者PATCH。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281b1a885efa5bb44b35e" style="list-style-type:decimal"><li><em>client_max_body_size size;（client_max_body_size 1m;）http、server、location浏览器在发送含有较大HTTP包体的请求时，其头部会有一个Content-Length字段，client_max_body_size是用来限制Content-Length所示值的大小的。因此，这个限制包体的配置非常有用处，因为不用等Nginx接收完所有的HTTP包体——这有可能消耗很长时间——就可以告诉用户请求过大不被接受。例如，用户试图上传一个10GB的文件，Nginx在收完包头后，发现Content-Length超过client_max_body_size定义的值，就直接发送413(&quot;RequestEntity Too Large&quot;)响应给客户端。</em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281609fedf8b000b8c153" style="list-style-type:decimal"><li><em>limit_rate speed;（limit_rate 0;）http、server、location、if此配置是对客户端请求限制每秒传输的字节数。speed可以使用2.2.4节中提到的多种单位，默认参数为0，表示不限速。针对不同的客户端，可以用$limit_rate参数执行不同的限速策略。</em></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d728107994bf4c3a2441ea8" style="list-style-type:decimal"><li><em>limit_rate_after time;（limit_rate_after 1m;）http、server、location、if此配置表示Nginx向客户端发送的响应长度超过limit_rate_after后才开始限速。</em></li></ol></div></details><details class="notion-toggle notion-block-1f7beb961d7281cc896be9a2f2c6a913"><summary>文件操作的优化</summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72813ca788c0d592f400e4" style="list-style-type:decimal"><li><em>sendfile on|off;（sendfile off;）http、server、location可以启用Linux上的sendfile系统调用来发送文件，它减少了内核态与用户态之间的两次内存复制，这样就会从磁盘中读取文件后直接在内核态发送到网卡设备，提高了发送文件的效率。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281339026df8d3788e2a1" style="list-style-type:decimal"><li><em>aio on|off;（aio off;）http、server、location此配置项表示是否在FreeBSD或Linux系统上启用内核级别的异步文件I/O功能。注意，它与sendfile功能是互斥的。</em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d6acb4e8051b7cccd7" style="list-style-type:decimal"><li><em>directio size|off;</em></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281f99759f9d3d3ff147b" style="list-style-type:decimal"><li><em>directio_alignment</em></li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d72816cb1cbc3986bd51ccc" style="list-style-type:decimal"><li><em>open_file_cache max=N[inactive=time]|off;（off）http、server、location打开文件缓存</em></li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d7281669aadd52dbac89b84" style="list-style-type:decimal"><li><em>open_file_cache_errors on|off;（off）此配置项表示是否在文件缓存中缓存打开文件时出现的找不到路径、没有权限等错误信息。</em></li></ol></div></details><details class="notion-toggle notion-block-1f7beb961d7281c3b4b4cf1b33e8351e"><summary><b>对客户端请求的特殊处理</b></summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72816c93dac69b76dd9c71" style="list-style-type:decimal"><li><em>ignore_invalid_headers on|off;（on）忽略不合法的HTTP头部如果将其设置为off，那么当出现不合法的HTTP头部时，Nginx会拒绝服务，并直接向用户发送400（Bad Request）错误。如果将其设置为on，则会忽略此HTTP头部。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d72813d8bb3e857cf1249eb" style="list-style-type:decimal"><li><em>underscores_in_headers on|off;（off）HTTP头部是否允许下划线。默认为off，表示HTTP头部的名称中不允许带“_”（下划线）。</em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d72811fb54fd81f4f6cfdd2" style="list-style-type:decimal"><li><em><b>if_modified_since[off|exact|before];（exact）</b></em><em>http、server、location出于性能考虑，Web 浏览器一般会在客户端本地缓存一些文件，并存储获取时间。这样，下次向 Web 服务器获取缓存过的资源时，就可以使用 If-Modified-Since 头部把上次获取的时间捎带上，而 if_modified_since 将根据参数决定如何处理该头部：</em><em><b>off</b></em><em>：表示忽略用户请求中的If-Modified-Since头部。这时，如果获取一个文件，那么会正常地返回文件内容。HTTP响应码通常是200。</em><em><b>exact</b></em><em>：将If-Modified-Since头部包含的时间与将要返回的文件上次修改的时间做精确比较，如果没有匹配上，则返回200和文件的实际内容，如果匹配上，则表示浏览器缓存的文件内容已经是最新的了，没有必要再返回文件从而浪费时间与带宽了，这时会返回304 Not Modified，浏览器收到后会直接读取自己的本地缓存。</em><em><b>before</b></em><em>：是比exact更宽松的比较。只要文件的上次修改时间等于或者早于用户请求中的If-Modified-Since头部的时间，就会向客户端返回304 NotModified。</em></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d72812eb633d24e783d1cfa" style="list-style-type:decimal"><li><em>log_not_found on|off;（on）文件未找到时是否记录到error日志。</em></li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d72813a9cd8cbdb3f0f80ab" style="list-style-type:decimal"><li><em>resolver address…;设置DNS名字解析服务器的地址。</em></li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d7281b7a19cd8089cf9396f" style="list-style-type:decimal"><li><em>resolver_timeout time;（30）表示DNS解析的超时时间。</em></li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-1f7beb961d728165b380c3c185e6014d" style="list-style-type:decimal"><li><em>server_tokens on|off;（on）返回错误页面时是否在Server中注明Nginx版本。</em></li></ol></div></details><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d728133b000c69461ece710" data-id="1f7beb961d728133b000c69461ece710"><span><div id="1f7beb961d728133b000c69461ece710" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d728133b000c69461ece710" title="2.5 HTTP proxy module 配置反向代理服务器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">2.5 HTTP proxy module 配置反向代理服务器</span></span></h4><div class="notion-text notion-block-1f7beb961d7281a5a912c4b26ad18aca"><b>反向代理</b>方式（<b>reverse proxy</b>）是指用代理服务器接受 Internet 的连接请求，然后将请求转发给内部网络中的<b>上游服务器</b>（<b>upstream</b>），并将从上游服务器上取得的结果返回给请求连接的客户端，反向代理服务器必须能够处理大量并发请求。</div><div class="notion-text notion-block-1f7beb961d72819fab6df8f89dc90c43">Nginx 具有高并发高负载能力，一方面可以作为前端的服务器直接向客户端提供静态文件服务，另一方面在面对复杂多变的业务时可以转发动态请求到 Apache、Tomcat 等服务器来处理。于是 <b>Nginx 通常会被配置为既是静态 Web 服务器，也是反向代理服务器</b>，不适合 Nginx 处理的请求就会被直接转发到上游服务器进行处理。</div><div class="notion-text notion-block-1f7beb961d72819793baf69a37dc85a3">与其他反向代理服务器相比，Nginx 有自己的特点：</div><div class="notion-text notion-block-1f7beb961d72810f8599f3e8dc133c51">当客户端发来 HTTP 请求时，Nginx 并不会立刻转发到上游服务器，而是先把用户请求完整接受到 Nginx 所在服务器的硬盘或者内存，然后再向上游服务器发起连接，把缓存的客户端请求转发到上游服务器。而 Squid 等 dialing 服务器则采用一边接受客户端请求，一边转发到上游服务器的方式。</div><blockquote class="notion-quote notion-block-1f7beb961d72815cb8d1d932524bb9b1"><div>Squid 等反向代理服务器在与客户端建立连接且还没有开始接受 HTTP 包体时就已经向上游服务器建立连接。上游服务器在接受完整 HTTP 包体的漫长过程中要始终维持这个链接，这直接对上游服务器的并发能力提出挑战。</div><div class="notion-text notion-block-1f7beb961d72817e8ce1ccbcfa737162">Nginx 则不然，它在接收到完整请求，才会与上游服务器建立连接并转发请求，由于是内网，这个转发过程会执行得很快。这样一个客户端请求占用上游服务器的连接时间就会非常短，也就是说，Nginx 的这种反向代理方案主要是为了降低上游服务器的并发压力。</div></blockquote><div class="notion-text notion-block-1f7beb961d7281669be5cd9c04f37e86"><b>优点是降低上游服务器负载，缺点是延长请求处理时间并增加开销（用于缓存请求内容的内存和磁盘空间）</b></div><details class="notion-toggle notion-block-1f7beb961d72817299d3e457ab56cc3c"><summary>负载均衡基本配置</summary><div><div class="notion-text notion-block-1f7beb961d728123b60dc38fcf9e6b3d"><em>作为代理服务器，一般都需要向上游服务器的集群转发请求。这里的负载均衡是指选择一种策略，尽量把请求平均地分布到每一台上游服务器上。</em></div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281c89160d30218dff9e3" style="list-style-type:decimal"><li><em>upstream name{…}httpupstream块定义了一个上游服务器的集群，便于反向代理中的proxy_pass使用。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281ffba88f0c0ac0519fb" style="list-style-type:decimal"><li><em><b>server name[parameters];</b></em><em>upstreamserver配置项指定了一台上游服务器的名字，这个名字可以是域名、IP地址端口、UNIX句柄等，在其后还可以跟下列参数。weight=number：设置向这台上游服务器转发的权重，默认为1。max_fails=number：该选项与fail_timeout配合使用，指在fail_timeout时间段内，如果向当前的上游服务器转发失败次数超过number，则认为在当前的fail_timeout时间段内这台上游服务器不可用。max_fails默认为1，如果设置为0，则表示不检查失败次数。fail_timeout=time：fail_timeout表示该时间段内转发失败多少次后就认为上游服务器暂时不可用，用于优化反向代理功能。它与向上游服务器建立连接的超时时间、读取上游服务器的响应超时时间等完全无关。fail_timeout默认为10秒。down：表示所在的上游服务器永久下线，只在使用ip_hash配置项时才有用。backup：在使用ip_hash配置项时它是无效的。它表示所在的上游服务器只是备份服务器，只有在所有的非备份上游服务器都失效后，才会向所在的上游服务器转发请求。</em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d72811e9b2ce8c5cfe4d590" style="list-style-type:decimal"><li><em>ip_hashupstream在有些场景下，我们可能会希望来自某一个用户的请求始终落到固定的一台上游服务器中。ip_hash就是用以解决上述问题的，它首先根据客户端的IP地址计算出一个key，将key按照upstream集群里的上游服务器数量进行取模，然后以取模后的结果把请求转发到相应的上游服务器中。这样就确保了同一个客户端的请求只会转发到指定的上游服务器中。ip_hash与weight（权重）配置不可同时使用。如果 upstream 集群中有一台上游服务器暂时不可用，不能直接删除该配置，而是要 down 参数标识，确保转发策略的一贯性。</em></li></ol></div></details><details class="notion-toggle notion-block-1f7beb961d7281d99177c25096717da3"><summary>反向代理基本配置</summary><div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281868ba3d6c7813b2b25" style="list-style-type:decimal"><li><em>proxy_pass URL;location、if此配置项将当前请求反向代理到URL参数指定的服务器上，URL可以是主机名或IP地址加端口的形式，也可以是 UNIX 句柄，还可以直接使用 upstream 块。</em></li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d728102825cc7360ab34348" style="list-style-type:decimal"><li><em>proxy_method method;http、server、location此配置项表示转发时的协议方法名。例如设置为：proxy_method POST;那么客户端发来的GET请求在转发时方法名也会改为POST。</em></li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281c3983dc98dfcbf2cde" style="list-style-type:decimal"><li><em><b>proxy_hide_header the_header &amp; proxy_pass_header</b></em><em>http、server、locationNginx会将上游服务器的响应转发给客户端，但默认不会转发以下HTTP头部字段：Date、Server、X-Pad和X-Accel-*。使用proxy_hide_header后可以任意地指定哪些HTTP头部字段不能被转发。与proxy_hide_header功能相反，proxy_pass_header会将原来禁止转发的header设置为允许转发。</em></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281e3a96cff49d17b4974" style="list-style-type:decimal"><li><em>proxy_pass_request_body on|off;（on）http、server、location作用为确定是否向上游服务器发送HTTP包体部分。</em></li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d7281efa9ead977809d5f2a" style="list-style-type:decimal"><li><em>proxy_pass_request_headers on|off;（on）http、server、location作用为确定是否转发HTTP头部。</em></li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d728115a6e9d50862de759f" style="list-style-type:decimal"><li><em>proxy_redirect[default|off|redirect replacement];（default）http、server、location当上游服务器返回的响应是重定向或刷新请求（如HTTP响应码是301或者302）时，proxy_redirect可以重设HTTP头部的location或refresh字段。例如，如果上游服务器发出的响应是302重定向请求，location字段的URI是</em><em><a class="notion-link" href="http://localhost:8000/two/some/uri/" target="_blank" rel="noopener noreferrer">http://localhost:8000/two/some/uri/</a></em><em> ，那么在下面的配置情况下，实际转发给客户端的location是</em><em><a class="notion-link" href="http://frontend/one/some/uri/" target="_blank" rel="noopener noreferrer">http://frontend/one/some/uri/</a></em><em> 。</em></li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-1f7beb961d728120b81be815eecb3f97" style="list-style-type:decimal"><li><em>proxy_next_upstream[error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_404|off];（proxy_next_upstream error timeout;）http、server、location此配置项表示当向一台上游服务器转发请求出现错误时，继续换一台上游服务器处理这个请求。前面已经说过，上游服务器一旦开始发送应答，Nginx反向代理服务器会立刻把应答包转发给客户端。因此，一旦Nginx开始向客户端发送响应包，之后的过程中若出现错误也是不允许换下一台上游服务器继续处理的。这很好理解，这样才可以更好地保证客户端只收到来自一个上游服务器的应答。proxy_next_upstream的参数用来说明在哪些情况下会继续选择下一台上游服务器转发请求。</em></li></ol></div></details><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1f7beb961d72816ca03dd583789ce011" data-id="1f7beb961d72816ca03dd583789ce011"><span><div id="1f7beb961d72816ca03dd583789ce011" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72816ca03dd583789ce011" title="Nginx 模块"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Nginx 模块</span></span></h3><div class="notion-text notion-block-1f7beb961d7281da9c58dc6e61fe2c88">wait</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1f7beb961d7281aa8989ffd6f69439b3" data-id="1f7beb961d7281aa8989ffd6f69439b3"><span><div id="1f7beb961d7281aa8989ffd6f69439b3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281aa8989ffd6f69439b3" title="Nginx 原理"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Nginx 原理</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281488a96e6efa76e3a1f" data-id="1f7beb961d7281488a96e6efa76e3a1f"><span><div id="1f7beb961d7281488a96e6efa76e3a1f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281488a96e6efa76e3a1f" title="Nginx 基础架构"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Nginx 基础架构</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281838766e9a00f57789b" data-id="1f7beb961d7281838766e9a00f57789b"><span><div id="1f7beb961d7281838766e9a00f57789b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281838766e9a00f57789b" title="Web 服务器设计中的关键约束"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Web 服务器设计中的关键约束</span></span></h4><div class="notion-text notion-block-1f7beb961d72816c9f86c71ca2cee335">Nginx作为Web服务器受制于Web传输协议自身的约束，另外，下面将说明的7个关注点也是Nginx架构设计中的关键约束。</div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281588d2ad634ae1730a1" style="list-style-type:decimal"><li>性能</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d7281588d2ad634ae1730a1" style="list-style-type:lower-alpha"><li>网络性能</li><li>单次请求延迟</li><li>网络效率</li></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281ac9efbfb9d1e190c9c" style="list-style-type:decimal"><li>可伸缩性</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d72819a8424c3d67e0f05ca" style="list-style-type:decimal"><li>简单性</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d728107b1e4e7178ce047c2" style="list-style-type:decimal"><li>可修改性</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d7281028457d2b1de14fac0" style="list-style-type:decimal"><li>可见性</li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d72812b8a3ae546a540943d" style="list-style-type:decimal"><li>可移植性</li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-1f7beb961d7281fd8e88c7540cf6d808" style="list-style-type:decimal"><li>可靠性</li></ol><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281759741d3aaaa5042c8" data-id="1f7beb961d7281759741d3aaaa5042c8"><span><div id="1f7beb961d7281759741d3aaaa5042c8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281759741d3aaaa5042c8" title="Nginx 架构设计"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Nginx 架构设计</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281b082fcf1c7aa038de8" data-id="1f7beb961d7281b082fcf1c7aa038de8"><span><div id="1f7beb961d7281b082fcf1c7aa038de8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281b082fcf1c7aa038de8" title="模块化设计"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">模块化设计</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281969ca9c9a0a01cebce" data-id="1f7beb961d7281969ca9c9a0a01cebce"><span><div id="1f7beb961d7281969ca9c9a0a01cebce" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281969ca9c9a0a01cebce" title="事件驱动架构"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">事件驱动架构</span></span></h4><blockquote class="notion-quote notion-block-1f7beb961d72815dada6c584162ad482"><div>_事件驱动__：一些事件发生源产生事件，由一个或者多个事件收集器来收集、分发事件，许多时间处理器会注册自己感兴趣的时间，同时消费这些事件。_</div><div class="notion-text notion-block-1f7beb961d728182847bdb12f964459f">_对于__<b>传统 Web 服务器</b>__而言，采用的事件驱动局限在 TCP 连接建立、关闭事件上，一个连接建立以后，在其关闭之前的所有操作都不是事件驱动，此时退化成按序执行每个操作的批处理模式，这样每个请求在连接建立后都将始终占用系统资源，直到连接关闭才会释放，这造成服务器资源的浪费。</div><div class="notion-text notion-block-1f7beb961d7281b9afcfc557ec8a47bc"><b>对于 Nginx </b>__而言，它不会使用进程或者线程作为事件消费者，事件消费者只能是某个模块。只有事件收集、分发器才有资格占用进程资源，它们会在分发某个事件时调用事件消费模块使用当前占用资源。_</div><div class="notion-text notion-block-1f7beb961d728193ab4ad4725750d758"><em>前者是每个事件消费者独占一个进程资源，后者的事件消费者只是被事件分发者进程短期调用。</em></div><div class="notion-text notion-block-1f7beb961d7281ca899bda2568832a30"><em>这样的设计使得网络性能、用户感知的请求时延都得到提升，整个服务器的网络吞吐量都会由于事件的基石响应而增大。但也带来一个问题：每个事件消费者都不能有阻塞行为，否则将会由于长时间占用事件分发者进程而导致其他事件得不到及时响应。</em></div></blockquote><div class="notion-text notion-block-1f7beb961d728177acc6fa78b3fbb92e">对于 Nginx 而言，一般由网卡、磁盘产生事件。事件模块负责事件的手机、分发操作。其他所有模块都可能是时间消费者，它们需要先向事件模块注册感兴趣的事件类型。</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281a7a8e1e37706a0e032" data-id="1f7beb961d7281a7a8e1e37706a0e032"><span><div id="1f7beb961d7281a7a8e1e37706a0e032" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281a7a8e1e37706a0e032" title="多阶段异步处理请求"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">多阶段异步处理请求</span></span></h4><div class="notion-text notion-block-1f7beb961d72819a925cda1a64b899c4">多阶段异步处理请求给予事件驱动架构实现。</div><div class="notion-text notion-block-1f7beb961d72817aa0bac2eb70f7c86a">所谓<b>多阶段</b>，即一个请求的处理过程按照事件的触发方式划分为多个阶段，每个阶段可以由事件收集、分发器来触发。</div><div class="notion-text notion-block-1f7beb961d728112a761e89231f5f7af">至于<b>异步</b>则表现为当一个事件被分发到事件消费者中进行处理，事件消费者处理完这个事件相当于处理完请求的某个阶段，然后等待下次事件出现的时候，epoll 等事件分发器获取通知，继续调用事件消费者处理请求，这样每个阶段中的事件消费者都只能异步被动的等待通知。</div><div class="notion-text notion-block-1f7beb961d7281478ad8fd0c5e4e5b0d">这种设计配合配合事件驱动架构，将会提升网络性能，同时使得每个进程全力运转（尽量少地出现进程休眠）。</div><blockquote class="notion-quote notion-block-1f7beb961d7281209bdbdd55a9af82d5"><div>基于请求处理流程中的阻塞方法划分阶段。</div><ul class="notion-list notion-list-disc notion-block-1f7beb961d72816384afea8071cefbed"><li>_将阻塞进程的方法按照相关的触发事件分解成两个阶段。
__阻塞方法改为非阻塞方法调用，这个调用非阻塞方法并将进程归还给事件分发器的阶段就是第一阶段。
__增加新的处理阶段用于处理非阻塞方法最终返回的结果，这里的结果返回事件就是第二阶段的触发事件。_</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d7281b88926c17dede442b5"><li><em>将阻塞方法调用按照时间分解为多个阶段的方法调用。</em></li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d7281d3b977d9f7d2423b4b"><li><em>进程空转时，使用定时器划分阶段。</em></li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d72816c9a37e5034459682e"><li><em>如果阻塞方法完全无法继续划分，则必须使用独立的进程执行这个阻塞方法。</em></li></ul></blockquote><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d728173875bfb1cffa0651a" data-id="1f7beb961d728173875bfb1cffa0651a"><span><div id="1f7beb961d728173875bfb1cffa0651a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d728173875bfb1cffa0651a" title="管理进程、多工作进程设计"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">管理进程、多工作进程设计</span></span></h4><div class="notion-text notion-block-1f7beb961d7281d1894ad0af37192154"><a class="notion-link" href="about:blank#lGjLo">2.1 master-worker</a></div><blockquote class="notion-quote notion-block-1f7beb961d7281309ca5c8e4d581ac00"><div>事件和进程的关系</div><div class="notion-text notion-block-1f7beb961d7281b3876ee6c56a59bac1"><em>事件循环：每个工作进程都有自己的事件循环（event loop）。事件循环是事件驱动架构的核心，它不断检查是否有新的事件发生，并根据事件类型调用相应的处理函数。</em></div><div class="notion-text notion-block-1f7beb961d7281dcb781c0a1fc473455"><em>事件循环通常使用高效的 I/O 多路复用技术（如 epoll、kqueue、select 等）来管理多个文件描述符的 I/O 事件。进程间协作：虽然每个工作进程都有自己的事件循环，但它们共享同一个监听套接字（listening socket）。这意味着所有的工作进程都可以接收新的连接请求。当一个新的连接请求到达时，Nginx 会使用某种策略（如轮询、最少连接数等）将请求分配给某个工作进程。一旦某个工作进程接受了连接请求，该连接的所有后续 I/O 操作都由这个工作进程处理，直到连接关闭。</em></div><div class="notion-text notion-block-1f7beb961d72816783c7ec750074d4a3"><em>负载均衡：通过多个工作进程，Nginx 可以在多核 CPU 上并行处理请求，从而实现负载均衡。每个工作进程独立运行，减少了进程间的同步开销，提高了系统的整体性能。</em></div></blockquote><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d72810fba54ffb3e8dd7a4b" data-id="1f7beb961d72810fba54ffb3e8dd7a4b"><span><div id="1f7beb961d72810fba54ffb3e8dd7a4b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72810fba54ffb3e8dd7a4b" title="平台无关的代码实现"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">平台无关的代码实现</span></span></h4><div class="notion-text notion-block-1f7beb961d7281f58976e04fe97eaed0">在使用C语言实现Nginx时，尽量减少使用与操作系统平台相关的代码，如某个操作系统上的第三方库。Nginx重新封装了日志、各种基本数据结构（如第7章中介绍的容器）、常用算法等工具软件，在核心代码都使用了与操作系统无关的代码实现，在与操作系统相关的系统调用上则分别针对各个操作系统都有独立的实现，这最终造就了Nginx的可移植性，实现了对主流操作系统的支持。</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d72816f91d7f87f32c151bc" data-id="1f7beb961d72816f91d7f87f32c151bc"><span><div id="1f7beb961d72816f91d7f87f32c151bc" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72816f91d7f87f32c151bc" title="内存池设计"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">内存池设计</span></span></h4><div class="notion-text notion-block-1f7beb961d7281cd8fcede79a9163f63">Nginx 设计简单的内存池。</div><div class="notion-text notion-block-1f7beb961d728150bf73d854c49588a0">它通常不负责回收内存池中已经分配出的内存，它的优点在于把多次向系统申请内存的操作整合成一次，减少 CPU 资源消耗和内存碎片。</div><div class="notion-text notion-block-1f7beb961d7281c2971ee3a94eeac187">因此，通常每一个请求都有一个独立的线程池，在请求结束的时候销毁整个内存池，把曾经分配的内存一次性归还给操作系统。</div><div class="notion-text notion-block-1f7beb961d7281228a52ca74a8ec862f">这种设计他提高了模块开发的简单性，分配内存次数的减少使得请求时延降低，同时，通过减少内存碎片，提高了内存的有效利用率和系统可处理的并发连接数。</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281028bdef51df7e66d15" data-id="1f7beb961d7281028bdef51df7e66d15"><span><div id="1f7beb961d7281028bdef51df7e66d15" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281028bdef51df7e66d15" title="核心结构体 ngx_cycle_t"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">核心结构体 ngx_cycle_t</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d72813c8431f7b5bf4676ac" data-id="1f7beb961d72813c8431f7b5bf4676ac"><span><div id="1f7beb961d72813c8431f7b5bf4676ac" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72813c8431f7b5bf4676ac" title="Nginx 启动时框架处理流程"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Nginx 启动时框架处理流程</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d728148b50afeca2a340e61" style="list-style-type:decimal"><li>根据命令行得到配置文件路径。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d72810aac7aee0754bea039" style="list-style-type:decimal"><li>如果处于升级，则监听环境变量中传递的监听句柄。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d59fbbf7217bc9bc62" style="list-style-type:decimal"><li>调用所有核心模块的 create.conf 方法生成存放配置项的结构体。</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281bfbc81fe4164df7123" style="list-style-type:decimal"><li>调用配置模块解析的配置项方法。
遍历所有配置项，对于任一个配置项，将会检查所有核心模块以找出对他感兴趣的模块，并调用该模块在 ngx_command_t 结构体中定义的配置项处理方法。</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d72811d8535f19a4681a35e" style="list-style-type:decimal"><li>调用所有核心模块的 init_conf 方法。
这一步骤的目的在于让所有核心模块在解析完配置项后可以做综合性处理。</li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d7281899a96cff58574f29c" style="list-style-type:decimal"><li>创建目录、打开文件、初始化共享内存等进程间通信方式。</li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-1f7beb961d7281edacd1fc6fe7b4c4c9" style="list-style-type:decimal"><li>打开各个模块从配置文件中读取到的监听端口并监听端口。</li></ol><ol start="8" class="notion-list notion-list-numbered notion-block-1f7beb961d7281bbbaf3d17459509b7f" style="list-style-type:decimal"><li>调用所有模块的 init_module 方法。</li></ol><ol start="9" class="notion-list notion-list-numbered notion-block-1f7beb961d7281f6b8abcdb1632d13b0" style="list-style-type:decimal"><li>如果配置为 single 工作模式。</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d7281f6b8abcdb1632d13b0" style="list-style-type:lower-alpha"><li>则调用 ngx_single_process_cycle 方法进入单进程工作模式。</li><li>调用所有模块的 init_process 方法，单进程工作模式启动完成。</li></ol></ol><ol start="10" class="notion-list notion-list-numbered notion-block-1f7beb961d728108ac9cde2e5cc0f586" style="list-style-type:decimal"><li>如果配置为 master、worker 工作模式。</li><ol class="notion-list notion-list-numbered notion-block-1f7beb961d728108ac9cde2e5cc0f586" style="list-style-type:lower-alpha"><li>在启动worker子进程、cache manage子进程、cache loader子进程后，master进程启动流程执行完毕。</li><li>由 master 进程按照配置文件中 worker 进程的数目，启动子进程。调用所有模块的 init_process 方法，worker 进程的启动工作完成，进入正常的循环处理事件流程。</li><li>由 master 进程根据之前各模块的初始化情况决定是否启动 cache manager 子进程或者 cache loader 子进程。</li></ol></ol><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d728168b527fc189c42317c" data-id="1f7beb961d728168b527fc189c42317c"><span><div id="1f7beb961d728168b527fc189c42317c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d728168b527fc189c42317c" title="事件模块"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">事件模块</span></span></h4><blockquote class="notion-quote notion-block-1f7beb961d72812da2fddcd2e8c39aab"><div>事件处理框架概述</div><div class="notion-text notion-block-1f7beb961d7281e49c42f71b133691b0"><em>事件处理框架要解决的问题是如何收集、管理、分发事件。</em></div><div class="notion-text notion-block-1f7beb961d7281559490c48011e99fa1"><em>事件主要以网络事件和定时器事件为主，而网络事件又以 TCP 网络事件为主。</em></div><div class="notion-text notion-block-1f7beb961d7281a09d72cffe84cb43d5"><em>由于网络事件与网卡中断处理程序、内核提供的系统调用密切相关，所以网络事件驱动取决于不同的操作系统平台以及操作系统内核版本。（通常事件驱动机制也被叫做 I/O 多路复用），所以事件框架需要在不同的操作系统内核中选择一种事件驱动机制支持网络事件的处理。（Nginx 定义了一系列运行在不同操作系统、不同内核版本上的事件驱动模块，在 ngx_event_core_module 模块初始的过程选择）</em></div></blockquote><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281908e0ec9e09bf17bbe" data-id="1f7beb961d7281908e0ec9e09bf17bbe"><span><div id="1f7beb961d7281908e0ec9e09bf17bbe" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281908e0ec9e09bf17bbe" title="epoll 事件驱动模块"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">epoll 事件驱动模块</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281629410ff6e66b236a2" data-id="1f7beb961d7281629410ff6e66b236a2"><span><div id="1f7beb961d7281629410ff6e66b236a2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281629410ff6e66b236a2" title="epoll 原理"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">epoll 原理</span></span></h4><div class="notion-text notion-block-1f7beb961d72816e923ae97315e1bc81">采用红黑树管理事件，并通过回调实现事件通知，以达到事件触发后被收集到特定数据结构（rdlist），epoll 只需要遍历 rdlist 就可以实现同时监控并处理大量 fd。</div><blockquote class="notion-quote notion-block-1f7beb961d7281469e2bfef38fa671e3"><div>epoll_create： 创建 epoll 对象，每一个 epoll 对象都有一个独立的 eventepoll 结构体（主要是红黑树和双向列表）。</div><div class="notion-text notion-block-1f7beb961d7281bd953fe8d99c6c2571">结构体会在内核空间中创建独立内存，用于存储 epoll_ctl 向 epoll 对象添加的事件，这些事件都会被挂到 rbr 红黑树。_</div><div class="notion-text notion-block-1f7beb961d7281939ea5e09107d8f761"><em>在 epoll 中每一个事件都会建立 epitem 结构体，这里包含每个事件的信息，红黑树中每个节点都是基于 epitem 结构中的 rbn 成员。</em></div><div class="notion-text notion-block-1f7beb961d7281158b6cdf25cb322fdd"><em>所有添加到 epoll 对象的事件都会与设备驱动程序建立回调关系，事件发生时会调用回调方法（epoll_callback），然后事件会添加到 rdlist 中。</em></div><div class="notion-text notion-block-1f7beb961d72812ca757ea90dc29ae96"><b>epoll_ctl</b> 向 epoll 对象中添加、修改、删除事件时，从 rbr 红黑树中查找事件非常快。</div><div class="notion-text notion-block-1f7beb961d728182bc1cca2d3447d24e"><b>epoll_wait </b>检查是否有发生事件的连接时，只是检查 eventpoll 对象中 rdlist 是否存在 epitem 元素，存在则复制事件到用户态内存，同时返回事件数量给用户。</div></blockquote><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d72813fb524eef98af10252" data-id="1f7beb961d72813fb524eef98af10252"><span><div id="1f7beb961d72813fb524eef98af10252" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72813fb524eef98af10252" title="epoll 使用"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">epoll 使用</span></span></h4><div class="notion-text notion-block-1f7beb961d7281fb8c94d4495172f718">epoll 有 LT 模式（水平触发）和 ET 模式（边缘触发）两种工作模式。</div><div class="notion-text notion-block-1f7beb961d7281af9114ea6db31f60b1">默认情况下，epoll 使用水平触发模式，这时可以处理阻塞和非阻塞套接字。边缘触发模式的效率要更高，但是只支持非阻塞套接字。</div><blockquote class="notion-quote notion-block-1f7beb961d7281f4959fd01223cef734"><div>二者区别在：</div><div class="notion-text notion-block-1f7beb961d7281cbb753e1f23fad866e"><em>ET 模式下 epoll_wait 只在文件描述符状态发生变化时返回，比如从不可读变为可读。这意味着如果应用程序这次没有处理完所有数据，即使文件描述符仍然处于可读状态， epoll_wait 不会再返回该文件描述符，直至下次状态改变。</em></div><div class="notion-text notion-block-1f7beb961d72819b8c1fe1de842995f3"><em>LT模式则相反，只要一个事件对应的套接字缓冲区还有数据，就总能从 epoll_wait中获取这个事件。</em></div></blockquote><blockquote class="notion-quote notion-block-1f7beb961d7281d898f5cb601a4279b4"><div>实际意义：</div><div class="notion-text notion-block-1f7beb961d7281d19c8de598fd093fac"><em>在 LT 模式下如果一个文件描述符长时间保持可读状态，那可能会导致频繁的 epoll_wait 唤醒，从而增加 CPU 开销，而在 ET 模式下，只有当状态真正改变时才会被唤醒，有助于减少 CPU 负载。</em></div></blockquote><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281349572f71c02d569ce" data-id="1f7beb961d7281349572f71c02d569ce"><span><div id="1f7beb961d7281349572f71c02d569ce" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281349572f71c02d569ce" title="ngx_epoll_module 模块实现"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">ngx_epoll_module 模块实现</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281bf860ac7e3db521166" data-id="1f7beb961d7281bf860ac7e3db521166"><span><div id="1f7beb961d7281bf860ac7e3db521166" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281bf860ac7e3db521166" title="定时器事件"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">定时器事件</span></span></h4><div class="notion-text notion-block-1f7beb961d7281a8a9bfeb10f8a72dea">在网络事件中，网络事件的触发是由内核完成的，内核如果支持epoll就可以使用ngx_epoll_module模块驱动事件，内核如果仅支持select那就得使用ngx_select_module模块驱动事件；定时器事件则完全是由Nginx自身实现的，它与内核完全无关。</div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Tomcat]]></title>
            <link>https://nowave.cloud//article/1f7beb96-1d72-8044-addc-f85c299ae670</link>
            <guid>https://nowave.cloud//article/1f7beb96-1d72-8044-addc-f85c299ae670</guid>
            <pubDate>Sun, 18 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Tomcat 是一个 HTTP 解析器和 Servlet 容器，主要功能是将网络数据流转换为 Servlet 请求和响应对象。其结构包括 Server、Service、Connector 和 Container，Connector 负责网络通信和应用层协议解析，使用不同的处理器来处理不同类型的网络连接。]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1f7beb961d728044addcf85c299ae670"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><blockquote class="notion-quote notion-block-1f7beb961d72810c94f9ed748ce92273"><div>参考 <a class="notion-link" href="https://segmentfault.com/a/1190000023475177" target="_blank" rel="noopener noreferrer">java - Tomcat 架构原理解析到架构设计借鉴 - 个人文章 - SegmentFault 思否</a>。</div></blockquote><div class="notion-text notion-block-1f7beb961d7281cabaccfd457bfd4cae">Tomcat 可以视作 <b>HTTP解析器 + Servlet容器</b>。</div><div class="notion-text notion-block-1f7beb961d7281218d79c55c5351c771">主要作用是屏蔽应用层协议和网络通信细节，将网络数据流转换为 Servlet Request 和 Servlet Response 对象。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1f7beb961d7281c7b3ecf9d78ffb828f"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff58ff682-9d29-4b29-b781-b313300f1828%2F687adcff-c22c-4ddf-9d9b-c06801c50cd3%2Fimage.png?table=block&amp;id=1f7beb96-1d72-81c7-b3ec-f9d78ffb828f&amp;t=1f7beb96-1d72-81c7-b3ec-f9d78ffb828f&amp;width=583.9750366210938&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-1f7beb961d7281bdaf32dc1688e6856e">简单描述 Tomcat 的结构。</div><div class="notion-text notion-block-1f7beb961d7281d8a01bfc6ea2a93d1a">Tomcat Server 对应 Service，Service 中包含 Connector 和 Container。</div><div class="notion-text notion-block-1f7beb961d72818fa3e3f9ea5336a36a">对于不同类型的网络连接，Tomcat 使用不同的 Connector 处理并输出到唯一的 Container。</div><h3 class="notion-h notion-h2 notion-h-indent-0 notion-block-1f7beb961d7281b58dfcc3f6104e20ac" data-id="1f7beb961d7281b58dfcc3f6104e20ac"><span><div id="1f7beb961d7281b58dfcc3f6104e20ac" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281b58dfcc3f6104e20ac" title="Connector：HTTP 解析器"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Connector：HTTP 解析器</span></span></h3><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1f7beb961d7281e3bd90d5ed5c077dee"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff58ff682-9d29-4b29-b781-b313300f1828%2Fd9b6bdb2-58e0-4fe7-970b-2b12342084cf%2Fimage.png?table=block&amp;id=1f7beb96-1d72-81e3-bd90-d5ed5c077dee&amp;t=1f7beb96-1d72-81e3-bd90-d5ed5c077dee&amp;width=567.1749877929688&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-1f7beb961d7281cb97f9e5d398e6b380">主要职责是网络通信、应用层协议解析、对象转换。</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281be9e3df43027a56369" data-id="1f7beb961d7281be9e3df43027a56369"><span><div id="1f7beb961d7281be9e3df43027a56369" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281be9e3df43027a56369" title="ProtocolHandler"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">ProtocolHandler</span></span></h4><details class="notion-toggle notion-block-1f7beb961d72814fac01d67a55db5a7c"><summary>Endpoint</summary><div><div class="notion-text notion-block-1f7beb961d728182af29dcbb07203dbd">Endpoint 负责网络通信，其本身是接口。</div><div class="notion-text notion-block-1f7beb961d728170a2eded0e05afe9da">AbstractEndpoint 实现 Endpoint 并规范行为，NioEndpoint 具体实现逻辑。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1f7beb961d72818bbb22c13f2d0a8bcd"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2Ff58ff682-9d29-4b29-b781-b313300f1828%2F0257ca73-088e-4447-8381-6c823a8af414%2Fimage.png?table=block&amp;id=1f7beb96-1d72-818b-bb22-c13f2d0a8bcd&amp;t=1f7beb96-1d72-818b-bb22-c13f2d0a8bcd&amp;width=539.1875&amp;cache=v2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-1f7beb961d7281f4add5e3ec0a7db8e4">NioEndpoint 中涉及的组件有 LimitLatch、Acceptor、Poller 和 Executor。</div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281239276f1195908545d" style="list-style-type:decimal"><li>LimitLatch
控制最大连接数。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281a29276e197a446d0c4" style="list-style-type:decimal"><li>Acceptor
监听 Socket 连接请求，独立线程。
死循环调用 accept() 接收新连接。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281ecbdaed09c01d51ac4" style="list-style-type:decimal"><li>Poller </li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d7281b79227fc3f0c041041" style="list-style-type:decimal"><li>Executor</li></ol></div></details><details class="notion-toggle notion-block-1f7beb961d72811ca0e2fa1d80ae6425"><summary>Processor</summary><div></div></details><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d72813b91f9d6d276bda4b4" data-id="1f7beb961d72813b91f9d6d276bda4b4"><span><div id="1f7beb961d72813b91f9d6d276bda4b4" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72813b91f9d6d276bda4b4" title="Adapter"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Adapter</span></span></h4><div class="notion-blank notion-block-1f7beb961d72817299dce0f1431c8f19"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Kakfa]]></title>
            <link>https://nowave.cloud//article/1f7beb96-1d72-801c-a0f7-fbc800be34fc</link>
            <guid>https://nowave.cloud//article/1f7beb96-1d72-801c-a0f7-fbc800be34fc</guid>
            <pubDate>Sun, 18 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Kafka 特性及其实现]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-1f7beb961d72801ca0f7fbc800be34fc"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7beb961d7281aaaa33f307820c1d6c" data-id="1f7beb961d7281aaaa33f307820c1d6c"><span><div id="1f7beb961d7281aaaa33f307820c1d6c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281aaaa33f307820c1d6c" title="Intro"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Intro</span></span></h2><div class="notion-text notion-block-1f7beb961d7281749bb2f4aa026821e2"><b>distributed </b>event streaming platform</div><div class="notion-text notion-block-1f7beb961d7281078963c6f308f43718">as <b>meaaage queue</b> or as <b>stream processing system</b></div><div class="notion-text notion-block-1f7beb961d7281339819f8cb09e9ffbc">Kafka 擅长提供高性能、可扩展性和耐用性。他旨在实时处理大量数据，不会丢失任何信息，并且尽可能快速地处理每条数据。</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7beb961d7281928e89cf9ddbdfad02" data-id="1f7beb961d7281928e89cf9ddbdfad02"><span><div id="1f7beb961d7281928e89cf9ddbdfad02" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281928e89cf9ddbdfad02" title="Basic Terminology and Architecture"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Basic Terminology and Architecture</span></span></h2><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281ab8ccffbb2b0561529" style="list-style-type:decimal"><li>Broker</li></ol><div class="notion-text notion-block-1f7beb961d7281b79332d9f659c0aaa9">A Kafka cluster is  made up of multiple brokers.These are just individual servers (physical or virtual)</div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d7281498ae8e053214976ab" style="list-style-type:decimal"><li>Partition</li></ol><div class="notion-text notion-block-1f7beb961d7281d5af35cc8a931f6bcb">Each Broker has a number of partitions.</div><div class="notion-text notion-block-1f7beb961d728110bfeed872e740f859">Each partition is an <em><b>ordered, immutable sequence of messages that is continually appended to</b></em>.</div><div class="notion-text notion-block-1f7beb961d7281088e93dbf658df06f3"><b>Partitions are the way Kafka scales </b>as they allow for sth. for meaagaed to be consumed in parallel.</div><div class="notion-text notion-block-1f7beb961d72819594e4cd696b6bb858">Each partition in Kafka functions essentially as an <b>append-only log file</b>. Messages are sequentially added to the end of this log, which is why Kafka is commonly described as a distributed commit log. This append-only design is central to Kafka’s architecture, providing several important benefits:</div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d728136ac8bcee01dd1cc5a" style="list-style-type:decimal"><li><b>Immutability</b>: Once written, messages in a partition cannot be altered or deleted. This immutability is crucial for Kafka’s performance and reliability. It simplifies replication, speeds up recovery processes, and avoids consistency issues common in systems where data can be changed.</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d7281738a4cf5ff2d45615c" style="list-style-type:decimal"><li><b>Efficiency</b>: By restricting operations to appending data at the end of the log, Kafka minimizes disk seek times, which are a major bottleneck in many storage systems.</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d7281729e70c35f2fee55f6" style="list-style-type:decimal"><li><b>Scalability</b>: The simplicity of the append-only log mechanism facilitates horizontal scaling. More partitions can be added and distributed across a cluster of brokers to handle increasing loads, and each partition can be replicated across multiple brokers to enhance fault tolerance.</li></ol><div class="notion-text notion-block-1f7beb961d7281dfaff2dd47df81ab97">Each message in a Kafka partition is assigned a unique <b>offset</b>, which is <b>a sequential identifier indicating the message’s position in the partition</b>.</div><div class="notion-text notion-block-1f7beb961d7281a39d93d467bbafdcb0">This offset is used by consumers to track their progress in reading messages from the topic. As consumers read messages, they maintain their current offset and periodically commit this offset back to Kafka. This way, they can resume reading from where they left off in case of failure or restart.</div><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72813ea264f829ba2d8eca" style="list-style-type:decimal"><li>Topic</li></ol><div class="notion-text notion-block-1f7beb961d72815fa2f6f6ff31f152a2"><b>A logical grouping of partitions.</b></div><div class="notion-text notion-block-1f7beb961d728131b14ec07202083019">Topics  are the way you publish and subscrible to data in Kafka.</div><div class="notion-text notion-block-1f7beb961d7281ecb6fef072670d77fe">Topics are  always <em><b>multi-producer</b></em>; that is, a topic can have zero, one, or many producers that write data to it.</div><details class="notion-toggle notion-block-1f7beb961d7281d99faaef1a21bcb402"><summary>The difference between a topic and a partition</summary><div><div class="notion-text notion-block-1f7beb961d7281de877ad58561d18ad8">A topic is a logical grouping of messages.</div><div class="notion-text notion-block-1f7beb961d7281aa9844ed2d2ce9cb99">A partition is a physical grouping of messages.</div><div class="notion-text notion-block-1f7beb961d7281669e26d9c7bfc200ca">A topic can have multiple partitions, and each partition can be on a different broker.</div><div class="notion-text notion-block-1f7beb961d728198bdf2fef35c59ed5d">Topics are just a way to organize your data, while partitions are a way to scale your data.</div></div></details><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d72810ea342e771ef93c029" style="list-style-type:decimal"><li>Producers and Consumers</li></ol><div class="notion-text notion-block-1f7beb961d7281408e84c7ba8c25479b"><b>Producers are the ones who write data to topics, and consumers are the ones who read data from topics. </b>While Kafka exposes a simple API for both producers and consumers, the creation and processing of messages is on you, the developer. Kafka doesn&#x27;t care what the data is, it just stores and serves it.</div><div class="notion-text notion-block-1f7beb961d7281e5aabfef082fd00cf7">Importantly, you can <b>use Kafka as either a message queue or a stream</b>. Frankly, the distinction here is minor. <b>The only meaningful difference is with how consumers interact with the data</b>. In a message queue, consumers read messages from the queue and then acknowledge that they have processed the message. In a stream, consumers read messages from the stream and then process them, but they don&#x27;t acknowledge that they have processed the message. This allows for more complex processing of the data.</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7beb961d7281fe811fcba81dcefa63" data-id="1f7beb961d7281fe811fcba81dcefa63"><span><div id="1f7beb961d7281fe811fcba81dcefa63" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281fe811fcba81dcefa63" title="How Kafka Works"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">How Kafka Works</span></span></h2><ol start="1" class="notion-list notion-list-numbered notion-block-1f7beb961d728114a3dfc47abbbc8c06" style="list-style-type:decimal"><li>When an event occurs, the producer formats a <b>message</b>, also referred to as a <b>record</b>, and sends it to a Kafka topic. A message consists of one required field, the <em><b>value</b></em>, and three optional fields: <em><b>a key, a timestamp, and headers</b></em>. The key is used to determine which partition the message is sent to, and the timestamp is used to order messages within a partition. Headers, like HTTP headers, are key-value pairs that can be used to store metadata about the message.</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-1f7beb961d72817abf8bff1302786852" style="list-style-type:decimal"><li>When a message is published to a Kafka topic, <b>Kafka first determines the appropriate partition for the message.</b> This partition selection is critical because it influences the distribution of data across the cluster. This is a two-step process:</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-1f7beb961d728132b1cefd2328740464" style="list-style-type:decimal"><li><b>Partition Determination</b>Kafka <em>uses a partitioning algorithm that hashes the message key</em> to assign the message to a specific partition. <em>If the message does not have a key, Kafka can either round-robin the message to partitions or follow another partitioning logic defined in the producer configuration</em>. This <b>ensures that messages with the same key always go to the same partition, preserving order at the partition level.</b></li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1f7beb961d72817fbc4ceaeb592188ff" style="list-style-type:decimal"><li><b>Broker Assignment</b>Once the partition is determined, <em>Kafka then identifies which broker holds that particular partition</em>. The mapping of partitions to specific brokers is managed by the Kafka cluster metadata, which is maintained by the Kafka controller (a role within the broker cluster). The producer uses this metadata to <em>send the message directly to the broker that hosts the target partition.</em></li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-1f7beb961d7281fa86d8f0b0da97a8cf" style="list-style-type:decimal"><li>Once a message is published to the designated partition, Kafka ensures its durability and availability through a robust replication mechanism. Kafka employs a leader-follower model for replication, which works as follows:Replic 是 Kafka 为了确保数据的可靠性和可用性为每个分区创建的副本，这些副本分布在不同的 broker 上，以防止单点故障。</li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-1f7beb961d7281a0bcc0c14f1f9a9ce0" style="list-style-type:decimal"><li><b>Leader Replica Assignment</b>Each partition has a designated <em>leader replica</em>, which resides on a broker. This leader replica is <em>responsible for handling all read and write requests for the partition</em>. The assignment of the leader replica is managed centrally by the cluster controller, which ensures that each partition’s leader replica is effectively distributed across the cluster to balance the load.</li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-1f7beb961d728143b4dad8f76085da41" style="list-style-type:decimal"><li><b>Follower Replication:</b>Alongside the leader replica, several follower replicas exist for each partition, residing on different brokers. These followers do not handle direct client requests; <em>instead, they passively replicate the data from the leader replica</em>. By replicating the messages received by the leader replica, these followers act as backups, ready to take over should the leader replica fail.</li></ol><ol start="8" class="notion-list notion-list-numbered notion-block-1f7beb961d72817e9997fb1466bd88fb" style="list-style-type:decimal"><li><b>Synchronization and Consistency</b>Followers continuously sync with the leader replica to ensure they have the latest set of messages appended to the partition log. This synchronization is crucial for maintaining consistency across the cluster. <em>If the leader replica fails, one of the follower replicas that has been fully synced can be quickly promoted to be the new leader, minimizing downtime and data loss.</em></li></ol><ol start="9" class="notion-list notion-list-numbered notion-block-1f7beb961d728176a6a6d82bb353f8c0" style="list-style-type:decimal"><li><b>Controller&#x27;s Role in Replication</b>The controller within the Kafka cluster manages this replication process. It <em>monitors the health of all brokers and manages the leadership and replication dynamics.</em> When a broker fails, the controller reassigns the leader role to one of the in-sync follower replicas to ensure continued availability of the partition.</li></ol><ol start="10" class="notion-list notion-list-numbered notion-block-1f7beb961d7281d7a28cf368ff14db37" style="list-style-type:decimal"><li>Last up, consumers read messages from Kafka topics. They can read messages in two ways: either by subscribing to a topic and receiving messages as they arrive (this is called a <b>push model</b>), or by polling Kafka for new messages at regular intervals (this is called a <b>pull model</b>).</li></ol><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7beb961d7281f6acd4fba4b7048952" data-id="1f7beb961d7281f6acd4fba4b7048952"><span><div id="1f7beb961d7281f6acd4fba4b7048952" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281f6acd4fba4b7048952" title="When to use Kafka"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">When to use Kafka</span></span></h2><div class="notion-text notion-block-1f7beb961d728199bebbc00f7c79eb91">Kafka can be use as either a message queue or a stream.</div><div class="notion-text notion-block-1f7beb961d7281e2b977e3599cc4220c">The key difference between the two lies in how consumers interact with the data. In a message queue, consumers typically pull messages from the queue when they are ready to process them. In a  stream, consumers continuously consume and process messages as they arrive in real-time, similar to drinking from a flowing river.</div><div class="notion-text notion-block-1f7beb961d7281c9b34cd322be7569c2">Consider adding a <b>message queue</b> to your system when:</div><ul class="notion-list notion-list-disc notion-block-1f7beb961d7281b181badbc516e272f9"><li>You have processing that <b>can be done asynchronously.</b>YouTube is a good example of this. When user upload a video we can make the standard definition video available immediately and then put the video a Kafka topic to be transcoded when the system has time.</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d72815ab27afc6a494c3722"><li>You need to ensure that <b>messages are processed in order.</b></li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d7281f199e3e0e3fa334200"><li>You want to <b>decouple the producer and consumer so that they can scale independently</b>. Usually this means that the producer is producing messages faster than the consumer can consume them. This is a common pattern in microservices where you want to ensure that one service can&#x27;t take down another.</li></ul><div class="notion-text notion-block-1f7beb961d728143a528f2c096347c86"><b>Streams</b> are useful when:</div><ul class="notion-list notion-list-disc notion-block-1f7beb961d7281578d3cfd0cdcd67ea2"><li>You require <b>continuous and immediate processing of incoming data</b>, treating it as a real-time flow. See Design an Ad Click Aggregator for an example where we aggregate click data in real-time.</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d728105b91cc3e9b2dc0676"><li><b>Messages need to be processed by multiple consumers simultaneously.</b> In Design FB Live Comments we can use Kafka as a pub/sub system to send comments to multiple consumers.</li></ul><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7beb961d7281949674dcbf132c9517" data-id="1f7beb961d7281949674dcbf132c9517"><span><div id="1f7beb961d7281949674dcbf132c9517" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281949674dcbf132c9517" title="Kafka For System Design"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Kafka For System Design</span></span></h2><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281ab8df8d045d4656616" data-id="1f7beb961d7281ab8df8d045d4656616"><span><div id="1f7beb961d7281ab8df8d045d4656616" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281ab8df8d045d4656616" title="Scalability"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Scalability</span></span></h4><div class="notion-text notion-block-1f7beb961d72814ba280ffddc19206da">Let&#x27;s start by understanding the constraints of a single Kafka broker. It&#x27;s important in your interview to <b>estimate the throughput and number of messages you&#x27;ll be storing in order to determine whether we need to worry about scaling</b> in the first place.</div><div class="notion-text notion-block-1f7beb961d72813b9a85d0b0f15ced68">First, there is no hard limit on the size of a Kafka message as this can be configured via <em>message.max.bytes</em>. However, it is recommended to keep messages under 1MB to ensure optimal performance via reduced memory pressure and better network utilization.</div><div class="notion-text notion-block-1f7beb961d728135bfe5e1c91a98707d">On good hardware, a single broker can store around 1TB of data and handle around 10,000 messages per second (this is very hand wavy as it depends on message size and hardware specs, but is a useful estimate). If your design does not require more than this, than scaling is likely not a relevant conversation.</div><div class="notion-text notion-block-1f7beb961d7281ce8e3fc4ea2fbe76ef">It&#x27;s a common anti-pattern in system design interviews to store large blobs of data in Kafka. Kafka is not a database, and it&#x27;s not meant to store large files. It&#x27;s meant to store small messages that can be processed quickly.</div><div class="notion-text notion-block-1f7beb961d7281fd9c83e16dcbee9397">For example, when designing YouTube, we need to perform post-processing on videos after uploading to chunk and transcode them. Naively, you might place the videos in Kafka so that the chunk/transcoding worker can pull them off the queue asynchronously and process them. This is not a good idea. Instead, you should store the videos in a distributed file system like S3 and place a message in Kafka with the location of the video in S3. This way, the Kafka message is small and serves as a pointer to the full video in S3.</div><div class="notion-text notion-block-1f7beb961d7281f3a657daaef4f5a0f3">In the case that you do need to scale, you have a couple strategies at your disposal:</div><ul class="notion-list notion-list-disc notion-block-1f7beb961d728100adbef5c7a1cd83cb"><li><b>Horizontal Scaling With More Brokers</b>The simplest way to scale Kafka is by adding more brokers to the cluster. This helps distribute the load and offers greater fault tolerance. Each broker can handle a portion of the traffic, increasing the overall capacity of the system. It&#x27;s really important that when adding brokers you ensure that your topics have sufficient partitions to take advantage of the additional brokers. More partitions allow more parallelism and better load distribution. If you are under partitioned, you won&#x27;t be able to take advantage of these newly added brokers.</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d72812d8d3fedb8061df7fa"><li><b>Partitioning Strategy</b>This should be the main focus of your scaling strategy in an interview and is the main decision you make when dealing with Kafka clusters (since much of the scaling happens dynamically in managed services nowadays). You need to decide how to partition your data across the brokers. This is done by choosing a key for your messages since the partition is determined by a consistent hashing algorithm on the key. If you choose a bad key, you can end up with hot partitions that are overwhelmed with traffic. <b>Good keys are ones that are evenly distributed across the partition space.</b></li></ul><div class="notion-text notion-block-1f7beb961d728148b37fea1d51fea28e"><b>When working with Kafka, you&#x27;re usually thinking about scaling topics rather than the entire cluster.</b> This is because different topics can have different requirements. For example, you may have a topic that is very high throughput and needs to be partitioned across many brokers, while another topic is low throughput and can be handled by a single broker. To scale a topic, you can increase the number of partitions, which will allow you to take advantage of more brokers.</div><details class="notion-toggle notion-block-1f7beb961d7281cb8b44fa0a643b2ef0"><summary><b>How can we handle hot partitions?</b></summary><div><div class="notion-text notion-block-1f7beb961d7281a9a5acc80c06ae132f">Consider an Ad Click Aggregator where Kafka stores a stream of click events from when users click on ads. Naturally, you would start by partitioning by ad id. But when Nike launches their new Lebron James ad, you better believe that partition is going to be overwhelmed with traffic and you&#x27;ll have a hot partition on your hands.</div><div class="notion-text notion-block-1f7beb961d7281fc822ce49f80b6d564">There are a few strategies to handle hot partitions:</div><ul class="notion-list notion-list-disc notion-block-1f7beb961d72810f9524dc458b6ab30e"><li><b>Random partitioning with no key</b>If you don&#x27;t provide a key, Kafka will randomly assign a partition to the message, guaranteeing even distribution. The downside is that you lose the ability to guarantee order of messages. If this is not important to your design, then this is a good option.</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d7281d39bc0fe2479104345"><li><b>Random salting</b>We can add a random number or timestamp to the ad ID when generating the partition key. This can help in distributing the load more evenly across multiple partitions, though it may complicate aggregation logic later on the consumer side. This is often referred to as &quot;salting&quot; the key.</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d728199bb1dcf532708acbb"><li><b>Use a compound key</b>Instead of using just the ad ID, use a combination of ad ID and another attribute, such as geographical region or user ID segments, to form a compound key. This approach helps in distributing traffic more evenly and is particularly useful if you can identify attributes that vary independently of the ad ID.</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d7281579d5bd01510c396d7"><li><b>Back pressure</b>Depending on your requirements, one easy solution is to <b>just slow down the producer.</b> If you&#x27;re using a managed Kafka service, they may have built-in mechanisms to handle this. If you&#x27;re running your own Kafka cluster, you can implement back pressure by having the producer check the lag on the partition and slow down if it&#x27;s too high.</li></ul></div></details><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d72814b89d2c71029d2e212" data-id="1f7beb961d72814b89d2c71029d2e212"><span><div id="1f7beb961d72814b89d2c71029d2e212" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72814b89d2c71029d2e212" title="Fault Tolerance and Durability"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Fault Tolerance and Durability</span></span></h4><div class="notion-text notion-block-1f7beb961d72815a96dfd51964d13374">Kafka ensures data durability through its <b>replication mechanism</b>. Each partition is replicated across multiple brokers, with one broker acting as the leader and others as followers. When a producer sends a message, it is written to the leader and then replicated to the followers. This ensures that even if a broker fails, the data remains available. <b>Producer acknowledgments (acks setting)</b> play a crucial role here. Setting acks=all ensures that the message is acknowledged only when all replicas have received it, guaranteeing maximum durability.</div><details class="notion-toggle notion-block-1f7beb961d728115ad30c8ad93260823"><summary><b>Producer acknowledgments (acks setting)</b></summary><div><ul class="notion-list notion-list-disc notion-block-1f7beb961d72815f8c9eea810aa56c38"><li>No Acknowledgment (acks=0)在这种模式下，生产者发送消息后不会等待任何确认。这意味着消息一旦发送出去，生产者就认为它已经被成功提交。这种方法效率最高，但也最不安全，因为如果Broker在消息被持久化之前发生故障，消息可能会丢失。</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d72812e9dcadbfd5dc6161c"><li>All Brokers Acknowledgment (acks=1)在这种模式下，生产者发送的消息只需要被Leader接收并写入其本地日志即可认为成功。Leader不会等待Follower的确认。这种方法比acks=0更可靠，但仍存在一定的风险，因为在Follower完成同步之前，Leader可能已经崩溃。</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d728114a15bf72035fa07a5"><li>Full Acknowledgment (acks=all 或 acks=-1)在这种模式下，生产者发送的消息必须被Leader以及所有Follower接收并持久化后，生产者才会收到确认。这是最安全的方式，因为只有当整个Replica集都保存了消息后，才认为消息已成功提交。不过，这也意味着生产者的性能会受到最慢Follower的影响。</li></ul></div></details><div class="notion-text notion-block-1f7beb961d72811bba02dcf2d5040c7c">Depending on how much durability you need, you can configure <b>the replication factor of your topics</b>. <em>The replication factor is the number of replicas that are maintained for each partition</em>. A replication factor of 3 is common, meaning that each partition has 2 replicas. So if one broker fails, the data is still available on the other two and we can promote a follower to be the new leader.</div><details class="notion-toggle notion-block-1f7beb961d7281c6b32fd6ce8def5b88"><summary><b>But what happens when a consumer goes down?</b></summary><div><div class="notion-text notion-block-1f7beb961d7281a28ed9daf8fa2ee0e5">What is far more relevant and likely is that a consumer goes down. When a consumer fails, Kafka&#x27;s fault tolerance mechanisms help ensure continuity:</div><ul class="notion-list notion-list-disc notion-block-1f7beb961d728122afebdd5f8b06563c"><li><b>Offset Management</b>Remember that partitions are just append-only logs where each message is assigned a unique offset. Consumers commit their offsets to Kafka after they process a message. This is the consumers way of saying, &quot;I&#x27;ve processed this message.&quot; When a consumer restarts, it reads its last committed offset from Kafka and resumes processing from there, ensuring no messages are missed or duplicated.</li></ul><ul class="notion-list notion-list-disc notion-block-1f7beb961d72811d9a6cc93ba371284d"><li><b>Rebalancing</b>When part of a consumer group, if one consumer goes down, Kafka will redistribute the partitions among the remaining consumers so that all partitions are still being processed.</li></ul></div></details><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281e18ba9efb61e64fe69" data-id="1f7beb961d7281e18ba9efb61e64fe69"><span><div id="1f7beb961d7281e18ba9efb61e64fe69" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281e18ba9efb61e64fe69" title="Handling Retries and Errors"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Handling Retries and Errors</span></span></h4><div class="notion-text notion-block-1f7beb961d72816d9b7ce923dbfba2d6">While Kafka itself handles most of the reliability (as we saw above), our system may fail getting messages into and out of Kafka. We need to handle these scenarios gracefully.</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281b4940fcf320168e10a" data-id="1f7beb961d7281b4940fcf320168e10a"><span><div id="1f7beb961d7281b4940fcf320168e10a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281b4940fcf320168e10a" title="Producer Retries"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Producer Retries</span></span></h4><div class="notion-text notion-block-1f7beb961d72818096e1da5fa6d23ab1">First up, we may fail to get a message to Kafka in the first place. Errors can occur due to network issues, broker unavailability, or transient failures. To handle these scenarios gracefully, <b>Kafka producers support automatic retries</b>.</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281cf8b0ada9dd1c06f1d" data-id="1f7beb961d7281cf8b0ada9dd1c06f1d"><span><div id="1f7beb961d7281cf8b0ada9dd1c06f1d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281cf8b0ada9dd1c06f1d" title="Consumer Retries"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Consumer Retries</span></span></h4><div class="notion-text notion-block-1f7beb961d72818aae26df8bd5a7b8a9">On the consumer side, we may fail to process a message for any number of reasons. <b>Kafka does not actually support retries for consumers out of the box</b> (but AWS SQS does!) so<b> we need to implement our own retry logic</b>. One common pattern is to <b>set up a custom topic that we can move failed messages to and then have a separate consumer that processes these messages</b>. This way, we can retry messages as many times as we want without affecting the main consumer. <b>If a given message is retried too many times, we can move it to a dead letter queue (DLQ).</b> DLQs are just a place to store failed messages so that we can investigate them later.</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d72811c8a40f2be2f4a1da2" data-id="1f7beb961d72811c8a40f2be2f4a1da2"><span><div id="1f7beb961d72811c8a40f2be2f4a1da2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d72811c8a40f2be2f4a1da2" title="Performance Optimizations"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Performance Optimizations</span></span></h4><div class="notion-text notion-block-1f7beb961d7281d290c5c9daa75812de">Especially when <b>using Kafka as an event stream</b>, we need to be mindful of performance so that we can process messages as quickly as possible.</div><div class="notion-text notion-block-1f7beb961d728129bb16e0c8760db6fc">This first thing we can do is <b>batch messages in the producer before sending them to Kafka</b>.</div><div class="notion-text notion-block-1f7beb961d7281ab9408ef1dafdebf0a">Another common way to improve throughput is by <b>compressing the messages on the producer</b>. This can be done by setting the compression option in the producer configuration. Kafka supports several compression algorithms, including GZIP, Snappy, and LZ4. Essentially, we&#x27;re just making the messages smaller so that they can be sent faster.</div><div class="notion-text notion-block-1f7beb961d728101bb2ae4fb0f2488f7"><b>Arguably the biggest impact you can have to performance comes back to your choice of partition key.</b> The goal is to maximize parallelism by ensuring that messages are evenly distributed across partitions. In your interview, discussing the partition strategy, as we go into above, should just about always be where you start.</div><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1f7beb961d7281118a64db9e03978af9" data-id="1f7beb961d7281118a64db9e03978af9"><span><div id="1f7beb961d7281118a64db9e03978af9" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281118a64db9e03978af9" title="Retention Policies"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Retention Policies</span></span></h4><div class="notion-text notion-block-1f7beb961d7281b3be14cc639d843b2e">Kafka topics have a retention policy that<b> determines how long messages are retained in the log</b>. This is configured via the <em>retention.ms</em> and <em>retention.bytes</em> settings. The default retention policy is to keep messages for 7 days or until the log reaches 1GB, whichever comes first.</div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-1f7beb961d7281d2823fe9ca65ca954d" data-id="1f7beb961d7281d2823fe9ca65ca954d"><span><div id="1f7beb961d7281d2823fe9ca65ca954d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1f7beb961d7281d2823fe9ca65ca954d" title="Summary"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Summary</span></span></h2><div class="notion-text notion-block-1f7beb961d72813f8576d5c33b40089e">通过阅读 <a class="notion-link" href="https://www.hellointerview.com/learn/system-design/deep-dives/kafka" target="_blank" rel="noopener noreferrer">Kafka Deep Dive for System Design Interviews (hellointerview.com)</a> 我对 Kafka 有了一个非实战的理论的基础了解。</div><div class="notion-text notion-block-1f7beb961d72811f9fdfe6f31fbf3eab">下一步需要阅读 <a class="notion-link" href="https://kafka1x.apachecn.org/documentation.html#operations" target="_blank" rel="noopener noreferrer">Kafka 中文文档 - ApacheCN</a> 中设计、实现和操作部分来加深对 Kafka 的了解。</div><div class="notion-text notion-block-1f7beb961d72816abb7bf62fd8371b1e">同样也可以试着在开发环境中使用 Kafka。</div><div class="notion-text notion-block-1f7beb961d728105ac8dd2affdf1e10b"><a class="notion-link" href="https://developer.aliyun.com/article/1560408#:~:text=%E6%9C%AC%E6%96%87%E8%AF%A6%E7%BB%86%E4%BB%8B%E7%BB%8D%E4%BA%86%E5%A6%82%E4%BD%95%E5%9C%A8" target="_blank" rel="noopener noreferrer">如何在Java中使用Kafka-阿里云开发者社区 (aliyun.com)</a></div><div class="notion-text notion-block-1f7beb961d728100a162fcdcf78eaa06"><a class="notion-link" href="https://cloud.tencent.com/developer/article/1991788" target="_blank" rel="noopener noreferrer">超详细的Kafka教程-从部署到开发到原理都有讲解-腾讯云开发者社区-腾讯云 (tencent.com)</a></div><div class="notion-text notion-block-1f7beb961d7281d28c15d2ead8cb3e5e"><a class="notion-link" href="https://cloud.tencent.com/developer/article/1547380" target="_blank" rel="noopener noreferrer">学习 Kafka 入门知识看这一篇就够了！（万字长文）-腾讯云开发者社区-腾讯云 (tencent.com)</a></div></main></div>]]></content:encoded>
        </item>
    </channel>
</rss>