<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Tsing Wind - 清风徐来 水波不兴</title>
    <link>https://kiyonlin.github.io/</link>
    <description>Recent content on Tsing Wind - 清风徐来 水波不兴</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-cn</language>
    <lastBuildDate>Thu, 25 Mar 2021 07:59:41 +0800</lastBuildDate>
    
        <atom:link href="https://kiyonlin.github.io/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>2021年度计划</title>
      <link>https://kiyonlin.github.io/post/plans/2021/</link>
      <pubDate>Thu, 25 Mar 2021 07:59:41 +0800</pubDate>
      
      <guid>https://kiyonlin.github.io/post/plans/2021/</guid>
      
        <description>&lt;p&gt;今年的计划：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http2&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 查看&lt;a href=&#34;https://http2.github.io&#34;&gt;http2&lt;/a&gt;(&lt;a href=&#34;https://github.com/abbshr/rfc7540-translation-zh_cn&#34;&gt;翻译&lt;/a&gt;)的RFC&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 在fasthttp中实现&lt;code&gt;http2&lt;/code&gt;功能&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 学习使用&lt;code&gt;gRPC&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;工具
&lt;ul&gt;
&lt;li&gt;&lt;input checked=&#34;&#34; disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 实现http压测工具&lt;a href=&#34;https://github.com/gonetx/httpit&#34;&gt;httpit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 实现&lt;code&gt;hping&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; 实现&lt;code&gt;tcping&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;幼儿园
&lt;ul&gt;
&lt;li&gt;阅读&lt;/li&gt;
&lt;li&gt;识中文字&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;健康
&lt;ul&gt;
&lt;li&gt;保护眼睛&lt;/li&gt;
&lt;li&gt;坚持锻炼&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;未完待续&lt;/li&gt;
&lt;/ul&gt;
</description>
      
    </item>
    
    <item>
      <title>golang调度器笔记</title>
      <link>https://kiyonlin.github.io/post/read/golang/golang%E8%B0%83%E5%BA%A6%E5%99%A8%E7%AC%94%E8%AE%B0/</link>
      <pubDate>Fri, 27 Nov 2020 08:25:05 +0800</pubDate>
      
      <guid>https://kiyonlin.github.io/post/read/golang/golang%E8%B0%83%E5%BA%A6%E5%99%A8%E7%AC%94%E8%AE%B0/</guid>
      
        <description>&lt;h1 id=&#34;cpu寄存器&#34;&gt;CPU寄存器&lt;/h1&gt;
&lt;p&gt;汇编代码所做的工作就是把数据在内存和寄存器中搬来搬去或做一些基础的数学和逻辑运算。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;通用寄存器&lt;/strong&gt;：rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8, r9, r10, r11, r12, r13, r14, r15寄存器。CPU对这16个通用寄存器的用途没有做特殊规定，程序员和编译器可以自定义其用途。&lt;strong&gt;rsp 栈顶寄存器和rbp栈基址寄存器&lt;/strong&gt;:这两个寄存器都跟函数调用栈有关，其中rsp寄存器一般用来存放函数调用栈的栈顶地址，而rbp寄存器通常用来存放函数的栈帧起始地址，编译器一般使用这两个寄存器加一定偏移的方式来访问函数局部变量或函数参数。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;程序计数寄存器（PC寄存器，有时也叫IP寄存器）&lt;/strong&gt;：rip寄存器。它用来存放&lt;strong&gt;下一条&lt;/strong&gt;即将执行的指令的地址，这个寄存器决定了程序的执行流程；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;段寄存器&lt;/strong&gt;：fs和gs寄存器。一般用它来实现线程本地存储（TLS），比如AMD64 linux平台下go语言和pthread都使用fs寄存器来实现系统线程的TLS，在本章线程本地存储一节和第二章详细分析goroutine调度器的时候我们可以分别看到Linux平台下Pthread线程库和go是如何使用fs寄存器的。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;内存&#34;&gt;内存&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;内存中的每个字节都有一个地址&lt;/li&gt;
&lt;li&gt;任何大于一个字节的变量在内存中都存储在相邻连续的的几个内存单元之中；&lt;/li&gt;
&lt;li&gt;大端存储模式指数据的高字节保存在内存的低地址中，低字节保存在内存的高地址中；小端存储模式指数据的高字节保存在内存的高地址中，低字节保存在内存的低地址中。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;函数调用栈&#34;&gt;函数调用栈&lt;/h1&gt;
&lt;p&gt;进程在内存中的布局主要分为4个区域：代码区，数据区，堆和栈。在详细讨论栈之前，先来简单介绍一下其它区域。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;代码区&lt;/strong&gt;，包括能被CPU执行的机器代码（指令）和只读数据比如字符串常量，程序一旦加载完成代码区的大小就不会再变化了。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据区&lt;/strong&gt;，包括程序的全局变量和静态变量（c语言有静态变量，而go没有），与代码区一样，程序加载完毕后数据区的大小也不会发生改变。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;堆&lt;/strong&gt;，程序运行时动态分配的内存都位于堆中，这部分内存由内存分配器负责管理。&lt;strong&gt;该区域的大小会随着程序的运行而变化&lt;/strong&gt;，即当我们向堆请求分配内存但分配器发现堆中的内存不足时，它会向操作系统内核申请向高地址方向扩展堆的大小，而当我们释放内存把它归还给堆时如果内存分配器发现剩余空闲内存太多则又会向操作系统请求向低地址方向收缩堆的大小。从这个内存申请和释放流程可以看出，我们从堆上分配的内存用完之后必须归还给堆，否则内存分配器可能会反复向操作系统申请扩展堆的大小从而导致堆内存越用越多，最后出现内存不足，这就是所谓的内存泄漏。值的一提的是传统的c/c++代码就必须小心处理内存的分配和释放，而在go语言中，有垃圾回收器帮助我们，所以程序员只管申请内存，而不用管内存的释放，这大大降低了程序员的心智负担，这不光是提高了程序员的生产力，更重要的是还会减少很多bug的产生。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;函数调用栈&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;函数调用栈简称栈，在程序运行过程中，不管是函数的执行还是函数调用，栈都起着非常重要的作用，它主要被用来：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;保存函数的局部变量；&lt;/li&gt;
&lt;li&gt;向被调用函数传递参数；&lt;/li&gt;
&lt;li&gt;返回函数的返回值；&lt;/li&gt;
&lt;li&gt;保存函数的返回地址。返回地址是指从被调用函数返回后调用者应该继续执行的指令地址，在汇编指令一节介绍call指令时我们将会对返回地址做更加详细的说明。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每个函数在执行过程中都需要使用一块栈内存用来保存上述这些值，我们称这块栈内存为某函数的栈帧(stack frame)。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;寄存器rbp和rsp始终指向正在执行的函数的栈帧&lt;/strong&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rbp，一般用来指向函数栈帧的起始位置&lt;/li&gt;
&lt;li&gt;rsp，始终指向函数调用栈栈顶&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;说明&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;调用函数时，参数和返回值都是存放在调用者的栈帧之中，而不是在被调函数之中；&lt;/li&gt;
&lt;li&gt;假如目前正在执行C函数，且函数调用链为A()-&amp;gt;B()-&amp;gt;C()，所以以栈帧为单位来看的话，C函数的栈帧目前位于栈顶；&lt;/li&gt;
&lt;li&gt;CPU硬件寄存器rsp指向整个栈的栈顶，当然它也指向C函数的栈帧的栈顶，而rbp寄存器指向的是C函数栈帧的起始位置；&lt;/li&gt;
&lt;li&gt;每个函数的栈帧大小可能都不同，因为不同的函数局部变量的个数以及所占内存的大小都不尽相同；&lt;/li&gt;
&lt;li&gt;有些编译器比如gcc会把参数和返回值放在寄存器中而不是栈中，go语言中函数的参数和返回值都是放在栈上的；&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;汇编指令&#34;&gt;汇编指令&lt;/h1&gt;
&lt;p&gt;里先对AT&amp;amp;T格式的汇编指令格式做一个简要的说明：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;AT&amp;amp;T格式的汇编指令中，寄存器名需要加%作为前缀；&lt;/li&gt;
&lt;li&gt;有2个操作数的指令中，第一个操作数是源操作数，第二个是目的操作数。例如 mov   %eax,%esi，这条指令表示把eax寄存器中的值拷贝给esi；&lt;/li&gt;
&lt;li&gt;立即操作数需要加上$符号做前缀，如  &amp;ldquo;mov $0x1 %rdi&amp;rdquo; 这条指令中第一个操作数不是寄存器，也不是内存地址，而是直接写在指令中的一个常数，这种操作数叫做&lt;strong&gt;立即操作数&lt;/strong&gt;。这条指令表示把数值0x1放入rdi寄存器中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;寄存器间接寻址的格式为  offset(%register)&lt;/strong&gt;，如果offset为0，则可以略去偏移不写直接写成(%register)。何为间接寻址呢？其实就是指指令中的寄存器并不是真正的源操作数或目的操作数，寄存器的值是一个内存地址，这个地址对应的内存才是真正的源或目的操作数，比如 mov   %rax, (%rsp)这条指令，第二个操作数(%rsp)中的寄存器的名字用括号括起来了，表示间接寻址，rsp的值是一个内存地址，这条指令的真实意图是把rax寄存器中的值赋值给rsp寄存器的值（内存地址）对应的内存，rsp寄存器本身的值不会被修改，作为比较，我们看一下 mov   %rax, %rsp 这条指令 ，这里第二个操作数仅仅少了个括号，变成了直接寻址，意思完全不一样了，这条指令的意思是把rax的值赋给rsp，这样rsp寄存器的值被修改为跟rax寄存器一样的值了。offset表示偏移，如-0x8(%rbp)，-0x8就是偏移量，整个表示rbp寄存器里面保存的地址值先减去8（因为偏移是负8）得到的地址对应的内存。&lt;/li&gt;
&lt;li&gt;与内存相关的一些指令的操作码会加上b, w, l和q字母分别表示操作的内存是1，2，4还是8个字节，比如指令 movl  $0x0,-0x8(%rbp) ，这条指令操作码movl的后缀字母l说明我们要把从-0x8(%rbp) 这个地址开始的4个内存单元赋值为0。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;常用指令详解&#34;&gt;常用指令详解&lt;/h2&gt;
&lt;h3 id=&#34;mov指令&#34;&gt;&lt;strong&gt;mov指令&lt;/strong&gt;&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;mov src dst
mov %rsp,%rbp       # 直接寻址，把rsp的值拷贝给rbp，相当于 rbp = rsp
mov -0x8(%rbp),%edx # 源操作数间接寻址，目的操作数直接寻址。从内存中读取4个字节到edx寄存器
mov %rsi,-0x8(%rbp) # 源操作数直接寻址，目的操作数间接寻址。把rsi寄存器中的8字节值写入内存
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;addsub&#34;&gt;add/sub&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;add src dst
sub src dst
sub $0x350,%rsp  # 源操作数是立即操作数，目的操作数直接寻址。rsp = rsp - 0x350
add %rdx,%rax    # 直接寻址。rax = rax + rdx
addl $0x1,-0x8(%rbp) # 源操作数是立即操作数，目的操作数间接寻址。内存中的值加1（addl后缀字母l表示操作内存中的4个字节）
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;callret指令&#34;&gt;call/ret指令&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;call src
ret
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;call指令执行函数调用。CPU执行call指令时首先会把rip寄存器中的值入栈，然后设置rip值为目标地址，又因为&lt;strong&gt;rip寄存器决定了下一条需要执行的指令&lt;/strong&gt;，所以当CPU执行完当前call指令后就会跳转到目标地址去执行。&lt;/p&gt;
&lt;p&gt;ret指令从被调用函数返回调用函数，它的实现原理是把call指令入栈的返回地址弹出给rip寄存器。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;#调用函数片段
0x0000000000400559 : callq 0x400526 &amp;lt;sum&amp;gt;
0x000000000040055e : mov   %eax,-0x4(%rbp)

#被调用函数片段
0x0000000000400526 : push   %rbp
......
0x000000000040053f : retq  
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;上面代码片段中，调用函数使用callq  0x400526指令调用0x400526处的函数，0x400526是被调用函数的第一条指令所在的地址。被调用函数在0x40053f处执行retq指令返回调用函数继续执行0x40055e地址处的指令。注意这两条指令会涉及入栈和出栈操作，所以会影响rsp寄存器的值。&lt;/p&gt;
&lt;h3 id=&#34;jmpjejlejgjge等等j开头的指令&#34;&gt;jmp/je/jle/jg/jge等等j开头的指令&lt;/h3&gt;
&lt;p&gt;这些都属于跳转指令，操作码后面直接跟要跳转到的地址或存有地址的寄存器，这些指令与高级编程语言中的 goto 和 if 等语句对应。用法示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;jmp    0x4005f2
jle    0x4005ee
jl     0x4005b8
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;pushpop&#34;&gt;push/pop&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;push src
pop dst
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;专用于函数调用栈的入栈出栈指令，&lt;strong&gt;这两个指令都会自动修改rsp寄存器&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;push入栈时rsp寄存器的值先减去8把栈位置留出来，然后把操作数复制到rsp所指位置。push指令相当于：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;sub $8,%rsp
mov dst,(%rsp)
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;pop出栈时先把rsp寄存器所指位置的数据复制到目的操作数中，然后rsp寄存器的值加8。pop指令相当于：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;mov (%rsp),dst
add $8,%rsp
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;leave指令&#34;&gt;leave指令&lt;/h3&gt;
&lt;p&gt;leave指令没有操作数，它一般放在函数的尾部ret指令之前，用于调整rsp和rbp，这条指令相当于如下两条指令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;mov %rbp,%rsp
pop %rbp
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h1 id=&#34;go汇编语言&#34;&gt;go汇编语言&lt;/h1&gt;
&lt;h2 id=&#34;寄存器&#34;&gt;寄存器&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;AMD64&lt;/th&gt;
&lt;th&gt;rax&lt;/th&gt;
&lt;th&gt;rbx&lt;/th&gt;
&lt;th&gt;rcx&lt;/th&gt;
&lt;th&gt;rdx&lt;/th&gt;
&lt;th&gt;rsi&lt;/th&gt;
&lt;th&gt;rdi&lt;/th&gt;
&lt;th&gt;rbp&lt;/th&gt;
&lt;th&gt;rsp&lt;/th&gt;
&lt;th&gt;r8~r15&lt;/th&gt;
&lt;th&gt;rip&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GO汇编&lt;/td&gt;
&lt;td&gt;AX&lt;/td&gt;
&lt;td&gt;BX&lt;/td&gt;
&lt;td&gt;CX&lt;/td&gt;
&lt;td&gt;DX&lt;/td&gt;
&lt;td&gt;SI&lt;/td&gt;
&lt;td&gt;DI&lt;/td&gt;
&lt;td&gt;BP&lt;/td&gt;
&lt;td&gt;SP&lt;/td&gt;
&lt;td&gt;R8~R15&lt;/td&gt;
&lt;td&gt;PC&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;fp虚拟寄存器&#34;&gt;FP虚拟寄存器&lt;/h3&gt;
&lt;p&gt;主要用来引用函数参数。go语言规定函数调用时参数都必须放在栈上，比如被调用函数使用 first_arg+0(FP)  来引用调用者传递进来的第一个参数，用second_arg+8(FP)来引用第二个参数 ，以此类推，这里的first_arg和second_arg仅仅是一个帮助我们阅读源代码的符号，对编译器来说无实际意义，+0和+8表示相对于FP寄存器的偏移量。&lt;/p&gt;
&lt;h3 id=&#34;sb虚拟寄存器&#34;&gt;SB虚拟寄存器&lt;/h3&gt;
&lt;p&gt;保存程序地址空间的起始地址。还记得在函数调用栈一节我们看过的进程在内存中的布局那张图吗，这个SB寄存器保存的值就是代码区的起始地址，它主要用来定位全局符号。go汇编中的函数定义、函数调用、全局变量定义以及对其引用会用到这个SB虚拟寄存器。对于这个虚拟寄存器，我们不用过多的关注，在代码中看到它时知道它是一个虚拟寄存器就行了。&lt;/p&gt;
&lt;h3 id=&#34;操作数宽度即操作数的位数&#34;&gt;操作数宽度（即操作数的位数）&lt;/h3&gt;
&lt;p&gt;go汇编中，寄存器的名字没有位数之分，比如AX寄存器没有什么RAX, EAX之类的名字，指令中一律只能使用AX。所以如果指令中有操作数寄存器或是指令需要访问内存，则操作码都需要带上后缀B(8位)、W(16位)、D(32位)或Q(64位)。&lt;/p&gt;
&lt;h3 id=&#34;函数定义&#34;&gt;函数定义&lt;/h3&gt;
&lt;p&gt;以go runtime中的gogo函数为例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;// func gogo(buf *gobuf)
// restore state from Gobuf; longjmp
TEXT runtime·gogo(SB), NOSPLIT, $16-8
// ......
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;TEXT runtime·gogo(SB)：指明在代码区定义了一个名字叫gogo的全局函数（符号），该函数属于runtime包。&lt;/li&gt;
&lt;li&gt;NOSPLIT：指示编译器不要在这个函数中插入检查栈是否溢出的代码。&lt;/li&gt;
&lt;li&gt;$16-8：数字16说明此函数的栈帧大小为16字节，8说明此函数的参数和返回值一共需要占用8字节内存。因为这里的gogo函数没有返回值，只有一个指针参数，对于AMD64平台来说指针就是8字节。go语言中函数调用的参数和函数返回值都是放在栈上的，而且这部分栈内存是由调用者而非被调用函数负责预留，所以在函数定义时需要说明到底需要在调用者的栈帧中预留多少空间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;线程本地存储&#34;&gt;线程本地存储&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;线程本地存储又叫线程局部存储，其英文为Thread Local Storage，简称TLS&lt;/strong&gt;，是线程私有的全局变量。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c&#34; data-lang=&#34;c&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;unistd.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;pthread.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;asm/prctl.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;sys/prctl.h&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;&lt;/span&gt;
&lt;span class=&#34;n&#34;&gt;__thread&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;g&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 1，这里增加了__thread关键字，把g定义成私有的全局变量，每个线程都有一个g变量
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;arg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;n&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;start, g[%p] : %d&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 4，打印本线程私有全局变量g的地址和值
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
		&lt;span class=&#34;n&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 5，修改本线程私有全局变量g的值
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;argc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;char&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;argv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[])&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;n&#34;&gt;pthread_t&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;tid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

		&lt;span class=&#34;n&#34;&gt;g&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;100&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 2，主线程给私有全局变量赋值为100
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
		&lt;span class=&#34;n&#34;&gt;pthread_create&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 3，创建子线程执行start()函数
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;n&#34;&gt;pthread_join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;NULL&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 6，等待子线程运行结束
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
		&lt;span class=&#34;n&#34;&gt;printf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;main, g[%p] : %d&lt;/span&gt;&lt;span class=&#34;se&#34;&gt;\n&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 7，打印主线程的私有全局变量g的地址和值
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;gcc编译器（其实还有线程库以及内核的支持）使用了CPU的fs段寄存器来实现线程本地存储&lt;/strong&gt;，不同的线程中fs段基地址是不一样的，这样看似同一个全局变量但在不同线程中却拥有不同的内存地址，实现了线程私有的全局变量。&lt;/p&gt;
&lt;h1 id=&#34;goroutine调度器概述&#34;&gt;goroutine调度器概述&lt;/h1&gt;
&lt;h2 id=&#34;goroutine简介&#34;&gt;goroutine简介&lt;/h2&gt;
&lt;p&gt;goroutine是Go语言实现的用户态线程，主要用来解决操作系统线程太“重”的问题，所谓的太重，主要表现在以下两个方面：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;创建和切换太重&lt;/strong&gt;：操作系统线程的创建和切换都需要进入内核，而进入内核所消耗的性能代价比较高，开销较大；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内存使用太重&lt;/strong&gt;：一方面，为了尽量避免极端情况下操作系统线程栈的溢出，内核在创建操作系统线程时默认会为其分配一个较大的栈内存（虚拟地址空间，内核并不会一开始就分配这么多的物理内存），然而在绝大多数情况下，系统线程远远用不了这么多内存，这导致了浪费；另一方面，栈内存空间一旦创建和初始化完成之后其大小就不能再有变化，这决定了在某些特殊场景下系统线程栈还是有溢出的风险。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;用户态的goroutine则轻量得多：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;goroutine是用户态线程，其创建和切换都在用户代码中完成而无需进入操作系统内核，所以其开销要远远小于系统线程的创建和切换；&lt;/li&gt;
&lt;li&gt;goroutine启动时默认栈大小只有2k，这在多数情况下已经够用了，即使不够用，goroutine的栈也会自动扩大，同时，如果栈太大了过于浪费它还能自动收缩，这样既没有栈溢出的风险，也不会造成栈内存空间的大量浪费。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;线程模型与调度器&#34;&gt;线程模型与调度器&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;对goroutine的调度，是指程序代码按照一定的算法在适当的时候挑选出合适的goroutine并放到CPU上去运行的过程&lt;/strong&gt;，这些负责对goroutine进行调度的程序代码我们称之为&lt;strong&gt;goroutine调度器&lt;/strong&gt;。用极度简化了的伪代码来描述goroutine调度器的工作流程大概是下面这个样子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 程序启动时的初始化代码
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;N&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 创建N个操作系统线程执行schedule函数
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;create_os_thread&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;schedule&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 创建一个操作系统线程执行schedule函数
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;// 定义一个线程私有全局变量，注意它是一个指向m结构体对象的指针
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ThreadLocal用来定义线程私有全局变量
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ThreadLocal&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;//schedule函数实现调度逻辑
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;schedule&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// 创建和初始化m结构体对象，并赋值给私有全局变量self
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;initm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;  
    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//调度循环
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;          &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;runqueue&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;is&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;empty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                 &lt;span class=&#34;c1&#34;&gt;// 根据某种算法从全局运行队列中找出一个需要运行的goroutine
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;                 &lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;find_a_runnable_goroutine_from_global_runqueue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
           &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                 &lt;span class=&#34;c1&#34;&gt;// 根据某种算法从私有的局部运行队列中找出一个需要运行的goroutine
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;                 &lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;find_a_runnable_goroutine_from_local_runqueue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
           &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
          &lt;span class=&#34;nf&#34;&gt;run_g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// CPU运行该goroutine，直到需要调度其它goroutine才返回
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;          &lt;span class=&#34;nf&#34;&gt;save_status_of_g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 保存goroutine的状态，主要是寄存器的值
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;     &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;关系示意图&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐  
  schedt grq                               
│                                       │  
                                           
│      .─.     .─.     .─.     .─.      │  
  ────( G )───( G )───( G )───( G )────    
│      `─&amp;#39;     `─&amp;#39;     `─&amp;#39;     `─&amp;#39;      │  
                                           
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘  
                                           
    .─.             .─.             .─.    
   ( G )           ( G )           ( G )   
    `┬&amp;#39;             `┬&amp;#39;             `┬&amp;#39;    
     │               │               │     
     Λ               Λ               Λ     
    ╱ ╲             ╱ ╲             ╱ ╲    
   ▕ m ▏           ▕ m ▏           ▕ m ▏   
    ╲ ╱             ╲ ╱             ╲ ╱    
     V               V               V     
     │               │               │     
   ┌─┴─┐           ┌─┴─┐           ┌─┴─┐   
   │ p │           │ p │           │ p │   
   └─┬─┘           └─┬─┘           └─┬─┘   
┌ ─ ─│─ ─ ┐     ┌ ─ ─│─ ─ ┐     ┌ ─ ─│─ ─ ┐
 lrq │           lrq │           lrq │     
│   .┴.   │     │   .┴.   │     │   .┴.   │
   ( G )           ( G )           ( G )   
│   `┬&amp;#39;   │     │   `┬&amp;#39;   │     │   `┬&amp;#39;   │
    .┴.             .┴.             .┴.    
│  ( G )  │     │  ( G )  │     │  ( G )  │
    `┬&amp;#39;             `┬&amp;#39;             `┬&amp;#39;    
│   .┴.   │     │   .┴.   │     │   .┴.   │
   ( G )           ( G )           ( G )   
│   `┬&amp;#39;   │     │   `┬&amp;#39;   │     │   `┬&amp;#39;   │
     │               │               │     
└ ─ ─ ─ ─ ┘     └ ─ ─ ─ ─ ┘     └ ─ ─ ─ ─ ┘
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;重要的结构体&#34;&gt;重要的结构体&lt;/h2&gt;
&lt;p&gt;结构体的定义全部位于Go语言的源代码路径下的&lt;code&gt;runtime/runtime2.go&lt;/code&gt;文件之中。&lt;/p&gt;
&lt;h3 id=&#34;stack结构体&#34;&gt;stack结构体&lt;/h3&gt;
&lt;p&gt;stack结构体主要用来记录goroutine所使用的栈的信息，包括栈顶和栈底位置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Stack describes a Go execution stack.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// The bounds of the stack are exactly [lo, hi),
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// with no implicit data structures on either side.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//用于记录goroutine使用的栈的起始和结束位置
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;stack&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;  
    &lt;span class=&#34;nx&#34;&gt;lo&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 栈顶，指向内存低地址
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;hi&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 栈底，指向内存高地址
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;gobuf结构体&#34;&gt;gobuf结构体&lt;/h3&gt;
&lt;p&gt;gobuf结构体用于保存goroutine的调度信息，主要包括CPU的几个寄存器的值：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;gobuf&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// The offsets of sp, pc, and g are known to (hard-coded in) libmach.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// ctxt is unusual with respect to GC: it may be a
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// heap-allocated funcval, so GC needs to track it, but it
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// needs to be set and cleared from assembly, where it&amp;#39;s
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// difficult to have write barriers. However, ctxt is really a
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// saved, live register, and we only ever exchange it between
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// the real register and the gobuf. Hence, we treat it as a
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// root during stack scanning, which means assembly that saves
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// and restores it doesn&amp;#39;t need write barriers. It&amp;#39;s still
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// typed as a pointer so that any other writes from Go get
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// write barriers.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;sp&lt;/span&gt;   &lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 保存CPU的rsp寄存器的值
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;pc&lt;/span&gt;   &lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 保存CPU的rip寄存器的值
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;guintptr&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 记录当前这个gobuf对象属于哪个goroutine
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;ctxt&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Pointer&lt;/span&gt;
 
    &lt;span class=&#34;c1&#34;&gt;// 保存系统调用的返回值，因为从系统调用返回之后如果p被其它工作线程抢占，
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 则这个goroutine会被放入全局运行队列被其它工作线程调度，其它线程需要知道系统调用的返回值。
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;ret&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Uintreg&lt;/span&gt;  
    &lt;span class=&#34;nx&#34;&gt;lr&lt;/span&gt;   &lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;
 
    &lt;span class=&#34;c1&#34;&gt;// 保存CPU的rbp寄存器的值
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;bp&lt;/span&gt;   &lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// for GOEXPERIMENT=framepointer
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;g结构体&#34;&gt;g结构体&lt;/h3&gt;
&lt;p&gt;g结构体用于代表一个goroutine，该结构体保存了goroutine的所有信息，包括栈，gobuf结构体和其它的一些状态信息。调度器代码可以通过g对象来对goroutine进行调度，当goroutine被调离CPU时，调度器代码负责把CPU寄存器的值保存在g对象的成员变量之中，当goroutine被调度起来运行时，调度器代码又负责把g对象的成员变量所保存的寄存器的值恢复到CPU的寄存器。：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 前文所说的g结构体，它代表了一个goroutine
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// Stack parameters.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// stack describes the actual stack memory: [stack.lo, stack.hi).
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// stackguard0 is the stack pointer compared in the Go stack growth prologue.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// stackguard1 is the stack pointer compared in the C stack growth prologue.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// It is stack.lo+StackGuard on g0 and gsignal stacks.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash).
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt; 
    &lt;span class=&#34;c1&#34;&gt;// 记录该goroutine使用的栈
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;stack&lt;/span&gt;       &lt;span class=&#34;nx&#34;&gt;stack&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// offset known to runtime/cgo
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 下面两个成员用于栈溢出检查，实现栈的自动伸缩，抢占调度也会用到stackguard0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;stackguard0&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// offset known to liblink
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;stackguard1&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// offset known to liblink
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
 
    &lt;span class=&#34;c1&#34;&gt;// 此goroutine正在被哪个工作线程执行
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;              &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;// current m; offset known to arm liblink
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 保存调度信息，主要是几个寄存器的值
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;          &lt;span class=&#34;nx&#34;&gt;gobuf&lt;/span&gt;
 
    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// schedlink字段指向全局运行队列中的下一个g，
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//所有位于全局运行队列中的g形成一个链表
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;schedlink&lt;/span&gt;      &lt;span class=&#34;nx&#34;&gt;guintptr&lt;/span&gt;

    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// 抢占调度标志，如果需要抢占调度，设置preempt为true
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;preempt&lt;/span&gt;        &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;// preemption signal, duplicates stackguard0 = stackpreempt
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
   &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;m结构体&#34;&gt;m结构体&lt;/h3&gt;
&lt;p&gt;m结构体用来代表工作线程，它保存了m自身使用的栈信息，当前正在运行的goroutine以及与m绑定的p等信息，详见下面定义中的注释。：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// g0主要用来记录工作线程使用的栈信息，在执行调度代码时需要使用这个栈
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 执行用户goroutine代码时，使用用户goroutine自己的栈，调度时会发生栈的切换
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;g0&lt;/span&gt;      &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// goroutine with scheduling stack
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// 通过TLS实现m结构体对象与工作线程之间的绑定
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;tls&lt;/span&gt;           &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// thread-local storage (for x86 extern register)
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;mstartfn&lt;/span&gt;      &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// 指向工作线程正在运行的goroutine的g结构体对象
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;curg&lt;/span&gt;          &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;// current running goroutine
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt; 
    &lt;span class=&#34;c1&#34;&gt;// 记录与当前工作线程绑定的p结构体对象
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;             &lt;span class=&#34;nx&#34;&gt;puintptr&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// attached p for executing go code (nil if not executing go code)
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;nextp&lt;/span&gt;         &lt;span class=&#34;nx&#34;&gt;puintptr&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;oldp&lt;/span&gt;          &lt;span class=&#34;nx&#34;&gt;puintptr&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// the p that was attached before executing a syscall
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;   
    &lt;span class=&#34;c1&#34;&gt;// spinning状态：表示当前工作线程正在试图从其它工作线程的本地运行队列偷取goroutine
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;spinning&lt;/span&gt;      &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// m is out of work and is actively looking for work
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;blocked&lt;/span&gt;       &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// m is blocked on a note
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;   
    &lt;span class=&#34;c1&#34;&gt;// 没有goroutine需要运行时，工作线程睡眠在这个park成员上，
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 其它线程通过这个park唤醒该工作线程
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;park&lt;/span&gt;          &lt;span class=&#34;nx&#34;&gt;note&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// 记录所有工作线程的一个链表
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;alllink&lt;/span&gt;       &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// on allm
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;schedlink&lt;/span&gt;     &lt;span class=&#34;nx&#34;&gt;muintptr&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// Linux平台thread的值就是操作系统线程ID
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;thread&lt;/span&gt;        &lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// thread handle
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;freelink&lt;/span&gt;      &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;// on sched.freem
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;p结构体&#34;&gt;p结构体&lt;/h3&gt;
&lt;p&gt;p结构体用于保存工作线程执行go代码时所必需的资源，比如goroutine的运行队列，内存分配用到的缓存等等。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;lock&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mutex&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;       &lt;span class=&#34;kt&#34;&gt;uint32&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// one of pidle/prunning/...
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;link&lt;/span&gt;            &lt;span class=&#34;nx&#34;&gt;puintptr&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;schedtick&lt;/span&gt;   &lt;span class=&#34;kt&#34;&gt;uint32&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// incremented on every scheduler call
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;syscalltick&lt;/span&gt;  &lt;span class=&#34;kt&#34;&gt;uint32&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// incremented on every system call
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;sysmontick&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;sysmontick&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// last tick observed by sysmon
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;                &lt;span class=&#34;nx&#34;&gt;muintptr&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// back-link to associated m (nil if idle)
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// Queue of runnable goroutines. Accessed without lock.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//本地goroutine运行队列
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;runqhead&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// 队列头
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;runqtail&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// 队列尾
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;runq&lt;/span&gt;     &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;256&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;guintptr&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;//使用数组实现的循环队列
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// runnext, if non-nil, is a runnable G that was ready&amp;#39;d by
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// the current G and should be run next instead of what&amp;#39;s in
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// runq if there&amp;#39;s time remaining in the running G&amp;#39;s time
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// slice. It will inherit the time left in the current time
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// slice. If a set of goroutines is locked in a
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// communicate-and-wait pattern, this schedules that set as a
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// unit and eliminates the (potentially large) scheduling
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// latency that otherwise arises from adding the ready&amp;#39;d
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// goroutines to the end of the run queue.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;runnext&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;guintptr&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// Available G&amp;#39;s (status == Gdead)
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;gFree&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;gList&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;schedt结构体&#34;&gt;schedt结构体&lt;/h3&gt;
&lt;p&gt;schedt结构体用来保存调度器的状态信息和goroutine的全局运行队列。因为每个Go程序只有一个调度器，所以在每个Go程序中schedt结构体只有一个实例对象，该实例对象在源代码中被定义成了一个共享的全局变量，这样每个工作线程都可以访问它以及它所拥有的goroutine运行队列，我们称这个运行队列为&lt;strong&gt;全局运行队列&lt;/strong&gt;。：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;schedt&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// accessed atomically. keep at top to ensure alignment on 32-bit systems.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;goidgen&lt;/span&gt;  &lt;span class=&#34;kt&#34;&gt;uint64&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;lastpoll&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint64&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;lock&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mutex&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// When increasing nmidle, nmidlelocked, nmsys, or nmfreed, be
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// sure to call checkdead().
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// 由空闲的工作线程组成链表
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;midle&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;muintptr&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// idle m&amp;#39;s waiting for work
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 空闲的工作线程的数量
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;nmidle&lt;/span&gt;       &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// number of idle m&amp;#39;s waiting for work
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;nmidlelocked&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// number of locked m&amp;#39;s waiting for work
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;mnext&lt;/span&gt;        &lt;span class=&#34;kt&#34;&gt;int64&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// number of m&amp;#39;s that have been created and next M ID
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 最多只能创建maxmcount个工作线程
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;maxmcount&lt;/span&gt;    &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// maximum number of m&amp;#39;s allowed (or die)
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;nmsys&lt;/span&gt;        &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// number of system m&amp;#39;s not counted for deadlock
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;nmfreed&lt;/span&gt;      &lt;span class=&#34;kt&#34;&gt;int64&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// cumulative number of freed m&amp;#39;s
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;ngsys&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// number of system goroutines; updated atomically
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// 由空闲的p结构体对象组成的链表
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;pidle&lt;/span&gt;      &lt;span class=&#34;nx&#34;&gt;puintptr&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// idle p&amp;#39;s
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 空闲的p结构体对象的数量
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;npidle&lt;/span&gt;     &lt;span class=&#34;kt&#34;&gt;uint32&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;nmspinning&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uint32&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// See &amp;#34;Worker thread parking/unparking&amp;#34; comment in proc.go.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// Global runnable queue.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// goroutine全局运行队列
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;runq&lt;/span&gt;     &lt;span class=&#34;nx&#34;&gt;gQueue&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;runqsize&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;

    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// Global cache of dead G&amp;#39;s.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// gFree是所有已经退出的goroutine对应的g结构体对象组成的链表
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 用于缓存g结构体对象，避免每次创建goroutine时都重新分配内存
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;gFree&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;lock&lt;/span&gt;          &lt;span class=&#34;nx&#34;&gt;mutex&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;stack&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;gList&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Gs with stacks
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;noStack&lt;/span&gt;   &lt;span class=&#34;nx&#34;&gt;gList&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Gs without stacks
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;              &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
 
    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;重要的全局变量&#34;&gt;重要的全局变量&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;nx&#34;&gt;allgs&lt;/span&gt;     &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// 保存所有的g
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allm&lt;/span&gt;       &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 所有的m构成的一个链表，包括下面的m0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt;       &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 保存所有的p，len(allp) == gomaxprocs
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;ncpu&lt;/span&gt;             &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 系统中cpu核的数量，程序启动时由runtime代码初始化
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;gomaxprocs&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// p的最大值，默认等于ncpu，但可以通过GOMAXPROCS修改
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;      &lt;span class=&#34;nx&#34;&gt;schedt&lt;/span&gt;     &lt;span class=&#34;c1&#34;&gt;// 调度器结构体对象，记录了调度器的工作状态
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;m0&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;       &lt;span class=&#34;c1&#34;&gt;// 代表进程的主线程
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;g0&lt;/span&gt;   &lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// m0的g0，也就是m0.g0 = &amp;amp;g0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h1 id=&#34;goroutine调度器初始化&#34;&gt;goroutine调度器初始化&lt;/h1&gt;
&lt;p&gt;以下面这个简单的Hello World程序为例，通过跟踪其从启动到退出这一完整的运行流程来分析Go语言调度器的初始化、goroutine的创建与退出、工作线程的调度循环以及goroutine的切换等重要内容。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;

&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Hello World!&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;任何一个由编译型语言（不管是C，C++，go还是汇编语言）所编写的程序在被操作系统加载起来运行时都会顺序经过如下几个阶段：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从磁盘上把可执行程序读入内存；&lt;/li&gt;
&lt;li&gt;创建进程和主线程；&lt;/li&gt;
&lt;li&gt;为主线程分配栈空间；&lt;/li&gt;
&lt;li&gt;把由用户在命令行输入的参数拷贝到主线程的栈；&lt;/li&gt;
&lt;li&gt;把主线程放入操作系统的运行队列等待被调度执起来运行。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;在主线程第一次被调度起来执行第一条指令之前，主线程的函数栈如下图所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;┌────────────────────┐          
│▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
│▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
│▨▨▨▨▨▨▨kernel▨▨▨▨▨▨▨│          
│▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
│▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
├────────────────────┼─────┬─   
│□□□□□□□□□□□□□□□□□□□□│     │    
│□□□□□□□......□□□□□□□│     │main
│□□□□□□□□□□□□□□□□□□□□│    thread
├────────────────────┤     stack
│                    │     │    
│    argv&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;...&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;       │     │    
│                    │     │    
├────────────────────┤     │    
│                    │     │    
│       argc         │&amp;lt;-sp │    
│                    │     │    
├────────────────────┤     ▼    
│                    │          
│                    │          
│                    │          
│                    │          
│                    │          
└────────────────────┘          
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;初始化g0&#34;&gt;初始化g0&lt;/h2&gt;
&lt;p&gt;全局变量g0的主要作用是提供一个栈供runtime代码执行，因此这里主要对g0的几个与栈有关的成员进行了初始化，从这里可以看出g0的栈大约有64K，地址范围为 SP - 64*1024 + 104 ～ SP。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;//runtime/asm_amd64.s : 96

// create istack out of the given (operating system) stack.
// _cgo_init may update stackguard.
//下面这段代码从系统线程的栈空分出一部分当作g0的栈，然后初始化g0的栈信息和stackgard
MOVQ$runtime·g0(SB), DI       //g0的地址放入DI寄存器
LEAQ(-64*1024+104)(SP), BX //BX = SP - 64*1024 + 104
MOVQBX, g_stackguard0(DI) //g0.stackguard0 = SP - 64*1024 + 104
MOVQBX, g_stackguard1(DI) //g0.stackguard1 = SP - 64*1024 + 104
MOVQBX, (g_stack+stack_lo)(DI) //g0.stack.lo = SP - 64*1024 + 104
MOVQSP, (g_stack+stack_hi)(DI) //g0.stack.hi = SP
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;运行完上面这几行指令后g0与栈之间的关系如下图所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;                                       ┌────────────────────┐          
                                       │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
                                       │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
                                       │▨▨▨▨▨▨▨kernel▨▨▨▨▨▨▨│          
                                       │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
                                       │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
                                       ├────────────────────┼─────┬─   
┌───g0───────────────┐                 │□□□□□□□□□□□□□□□□□□□□│     │    
│                    │                 │□□□□□□□......□□□□□□□│     │main
│                    │                 │□□□□□□□□□□□□□□□□□□□□│    thread
│                    │                 ├────────────────────┤     stack
│       ......       │                 │    argv&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;...&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;       │◀─┐  │    
│                    │                 ├────────────────────┤  │  │    
│                    │                 │       argc         │  │  │    
│                    │                 ├────────────────────┤  │  │    
├────────────────────┤                 │       ......       │  │  │    
│    stackguard1     │━━━━━━━━┓        ├────────────────────┤  │  │    
├────────────────────┤        ┃        │       argv         │──┘  │    
│    stackguard0     │━━━━━━━━┫        ├────────────────────┤     ▼    
├────────────────────┤        ┃        │       argc         │          
│     stack.hi       │━━━━┓   ┃        ├────────────────────┤          
├────────────────────┤    ┃   ┃        │                    │          
│     stack.lo       │━━━━╋━━━┫        ├────────────────────┤          
└────────────────────┘    ┃   ┃        │                    │          
                          ┗━━━╋━━━━━━━━▶────────────────────┤&amp;lt;-sp      
                              ┃        │                    │          
                              ┃        │  &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; * &lt;span class=&#34;m&#34;&gt;1024&lt;/span&gt; - &lt;span class=&#34;m&#34;&gt;104&lt;/span&gt;   │          
                              ┃        │      bytes         │          
                              ┃        │                    │          
                              ┃        │                    │          
                              ┗━━━━━━━━▶────────────────────┘          
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;主线程与m0绑定&#34;&gt;主线程与m0绑定&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;runtime/asm_amd64.s : 188

//下面开始初始化tls(thread local storage,线程本地存储)
LEAQruntime·m0+m_tls(SB), DI //DI = &amp;amp;m0.tls，取m0的tls成员的地址到DI寄存器
CALLruntime·settls(SB) //调用settls设置线程本地存储，settls函数的参数在DI寄存器中

// store through it, to make sure it works
//验证settls是否可以正常工作，如果有问题则abort退出程序
get_tls(BX) //获取fs段基地址并放入BX寄存器，其实就是m0.tls[1]的地址，get_tls的代码由编译器生成
MOVQ$0x123, g(BX) //把整型常量0x123拷贝到fs段基地址偏移-8的内存位置，也就是m0.tls[0] = 0x123
MOVQruntime·m0+m_tls(SB), AX //AX = m0.tls[0]
CMPQAX, $0x123 //检查m0.tls[0]的值是否是通过线程本地存储存入的0x123来验证tls功能是否正常
JEQ 2(PC)
CALLruntime·abort(SB) //如果线程本地存储不能正常工作，退出程序
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这段代码首先调用settls函数初始化主线程的线程本地存储(TLS)，&lt;strong&gt;目的是把m0与主线程关联在一起&lt;/strong&gt;。设置了线程本地存储之后接下来的几条指令在于验证TLS功能是否正常，如果不正常则直接abort退出程序。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;runtime/sys_linx_amd64.s : 658

// set tls base to DI
TEXT runtime·settls(SB),NOSPLIT,$32
//......
//DI寄存器中存放的是m.tls[0]的地址，m的tls成员是一个数组，读者如果忘记了可以回头看一下m结构体的定义
//下面这一句代码把DI寄存器中的地址加8，为什么要+8呢，主要跟ELF可执行文件格式中的TLS实现的机制有关
//执行下面这句指令之后DI寄存器中的存放的就是m.tls[1]的地址了
ADDQ$8, DI// ELF wants to use -8(FS)

  //下面通过arch_prctl系统调用设置FS段基址
MOVQDI, SI //SI存放arch_prctl系统调用的第二个参数
MOVQ$0x1002, DI// ARCH_SET_FS //arch_prctl的第一个参数
MOVQ$SYS_arch_prctl, AX //系统调用编号
SYSCALL
CMPQAX, $0xfffffffffffff001
JLS2(PC)
MOVL$0xf1, 0xf1 // crash //系统调用失败直接crash
RET
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这里通过arch_prctl系统调用把m0.tls[1]的地址设置成了fs段的段基址。CPU中有个叫fs的段寄存器与之对应，而每个线程都有自己的一组CPU寄存器值，操作系统在把线程调离CPU运行时会帮我们把所有寄存器中的值保存在内存中，调度线程起来运行时又会从内存中把这些寄存器的值恢复到CPU，这样，在此之后，工作线程代码就可以通过fs寄存器来找到m.tls，&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;// runtime/asm_amd64.s : 194

ok:
   // set the per-goroutine and per-mach &amp;#34;registers&amp;#34;
   get_tls(BX)    //获取fs段基址到BX寄存器
   LEAQ   runtime·g0(SB), CX //CX = g0的地址
   MOVQ   CX, g(BX)  //把g0的地址保存在线程本地存储里面，也就是m0.tls[0]=&amp;amp;g0
   LEAQ   runtime·m0(SB), AX //AX = m0的地址

   // save m-&amp;gt;g0 = g0
   MOVQ   CX, m_g0(AX)
   // save m0 to g0-&amp;gt;m
   MOVQ   AX, g_m(CX)
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;把m0和g0绑定在一起，这样，之后在主线程中通过get_tls可以获取到g0，通过g0的m成员又可以找到m0，于是这里就实现了m0和g0与主线程之间的关联。从这里还可以看到，保存在主线程本地存储中的值是g0的地址，也就是说工作线程的私有全局变量其实是一个指向g的指针而不是指向m的指针，目前这个指针指向g0，表示代码正运行在g0栈。此时，主线程，m0，g0以及g0的栈之间的关系如下图所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;   ┌───m0───────────────┐                  ┌────────────────────┐          
   │                    │                  │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
   │                    │                  │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
   │                    │                  │▨▨▨▨▨▨▨kernel▨▨▨▨▨▨▨│          
   │       ......       │                  │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
   │                    │                  │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
   │                    │                  ├────────────────────┼─────┬─   
   │                    │                  │□□□□□□□□□□□□□□□□□□□□│     │    
   ├────────────────────┤                  │□□□□□□□......□□□□□□□│     │main
   │       tls[1]       │                  │□□□□□□□□□□□□□□□□□□□□│    thread
   ├────────────────────┤&amp;lt;-fs              ├────────────────────┤     stack
┏━━│       tls[0]       │                  │    argv[...]       │◀─┐  │    
┃  ├────────────────────┤                  ├────────────────────┤  │  │    
┣━━│         g0         │◀━┓               │       argc         │  │  │    
┃  └────────────────────┘  ┃               ├────────────────────┤  │  │    
┃                          ┃               │       ......       │  │  │    
┃  ┌───g0───────────────┐  ┃               ├────────────────────┤  │  │    
┃  │                    │  ┃               │       argv         │──┘  │    
┃  │                    │  ┃               ├────────────────────┤     ▼    
┃  │                    │  ┃               │       argc         │          
┃  │       ......       │  ┃               ├────────────────────┤          
┃  │                    │  ┃               │                    │          
┃  │                    │  ┃               ├────────────────────┤          
┃  │                    │  ┃               │                    │          
┃  ├────────────────────┤  ┃ ┏━━━━━━━━━━━━━▶────────────────────┤&amp;lt;-sp      
┃  │         m          │━━┛ ┃             │                    │          
┃  ├────────────────────┤    ┃             │  64 * 1024 - 104   │          
┃  │    stackguard1     │━━━━╋━━━━┓        │      bytes         │          
┃  ├────────────────────┤    ┃    ┃        │                    │          
┃  │    stackguard0     │━━━━╋━━━━┫        │                    │          
┃  ├────────────────────┤    ┃    ┣━━━━━━━━▶────────────────────┘          
┃  │     stack.hi       │━━━━┛    ┃                                        
┃  ├────────────────────┤         ┃                                        
┃  │     stack.lo       │━━━━━━━━━┛                                        
┗━━▶────────────────────┘                                                  
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;初始化m0&#34;&gt;初始化m0&lt;/h2&gt;
&lt;p&gt;osinit函数获取CPU核的数量并保存在全局变量ncpu之中，调度器初始化时需要知道当前系统有多少个CPU核。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;//runtime/asm_amd64.s : 
//准备调用args函数，前面四条指令把参数放在栈上
MOVL16(SP), AX// AX = argc
MOVLAX, 0(SP)       // argc放在栈顶
MOVQ24(SP), AX// AX = argv
MOVQAX, 8(SP)       // argv放在SP + 8的位置
CALLruntime·args(SB)  //处理操作系统传递过来的参数和env，不需要关心

//对于linx来说，osinit唯一功能就是获取CPU的核数并放在global变量ncpu中，
//调度器初始化时需要知道当前系统有多少CPU核
CALLruntime·osinit(SB)  //执行的结果是全局变量 ncpu = CPU核数
CALLruntime·schedinit(SB) //调度系统初始化
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;接下来继续看调度器是如何初始化的。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// runtime/proc.go : 540
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;schedinit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;// raceinit must be the first call to race detector.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// In particular, it must be done before mallocinit below calls racemapshadow.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;   
    &lt;span class=&#34;c1&#34;&gt;//getg函数在源代码中没有对应的定义，由编译器插入类似下面两行代码
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//get_tls(CX)
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//MOVQ g(CX), BX; BX存器里面现在放的是当前g结构体对象的地址
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// _g_ = &amp;amp;g0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;//设置最多启动10000个操作系统线程，也是最多10000个M
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;maxmcount&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10000&lt;/span&gt;

    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
   
    &lt;span class=&#34;nf&#34;&gt;mcommoninit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//初始化m0，因为从前面的代码我们知道g0-&amp;gt;m = &amp;amp;m0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lastpoll&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;uint64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;nanotime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;procs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ncpu&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;//系统中有多少核，就创建和初始化多少个p结构体对象
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;atoi32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;gogetenv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;GOMAXPROCS&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;procs&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//如果环境变量指定了GOMAXPROCS，则创建指定数量的p
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;procresize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;procs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//创建和初始化全局变量allp
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nf&#34;&gt;throw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;unknown runnable goroutine during bootstrap&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;前面我们已经看到，g0的地址已经被设置到了线程本地存储之中，schedinit通过getg函数（getg函数是编译器实现的，我们在源代码中是找不到其定义的）从线程本地存储中获取当前正在运行的g，这里获取出来的是g0，然后调用mcommoninit函数对m0(g0.m)进行必要的初始化，对m0初始化完成之后调用procresize初始化系统需要用到的p结构体对象，按照go语言官方的说法，p就是processor的意思，它的数量决定了最多可以有都少个goroutine同时并行运行。schedinit函数除了初始化m0和p，还设置了全局变量sched的maxmcount成员为10000，限制最多可以创建10000个操作系统线程出来工作。&lt;/p&gt;
&lt;p&gt;这里我们需要重点关注一下mcommoninit如何初始化m0以及procresize函数如何创建和初始化p结构体对象。首先我们深入到mcommoninit函数中一探究竟。这里需要注意的是不只是初始化的时候会执行该函数，在程序运行过程中如果创建了工作线程，也会执行它，所以我们会在函数中看到加锁和检查线程数量是否已经超过最大值等相关的代码。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// runtime/proc.go : 651
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;mcommoninit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//初始化过程中_g_ = g0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// g0 stack won&amp;#39;t make sense for user (and is not necessary unwindable).
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;g0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;//函数调用栈traceback，不需要关心
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nf&#34;&gt;callers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;createstack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:])&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;nf&#34;&gt;lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mnext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mnext&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nf&#34;&gt;throw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;runtime: thread ID overflow&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mnext&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mnext&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;
    &lt;span class=&#34;nf&#34;&gt;checkmcount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//检查已创建系统线程是否超过了数量限制（10000）
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;//random初始化
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fastrand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1597334677&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;uint32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fastrand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;uint32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;cputicks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fastrand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]|&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fastrand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fastrand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;//创建用于信号处理的gsignal，只是简单的从堆上分配一个g结构体对象,然后把栈设置好就返回了
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;mpreinit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;gsignal&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;gsignal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stackguard1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;gsignal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_StackGuard&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;//把m挂入全局链表allm之中
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// Add to allm so garbage collector doesn&amp;#39;t free g-&amp;gt;m
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// when it is just in a register or thread-local storage.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;alllink&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allm&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// NumCgoCall() iterates over allm w/o schedlock,
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// so we need to publish it safely.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;atomicstorep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Pointer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Pointer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
    &lt;span class=&#34;nf&#34;&gt;unlock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// Allocate memory to hold a cgo traceback if the cgo call crashes.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;iscgo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;GOOS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;solaris&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;GOOS&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;windows&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;mp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cgoCallers&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cgoCallers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这个函数把m0放入全局链表allm之中。&lt;/p&gt;
&lt;h2 id=&#34;初始化allp&#34;&gt;初始化allp&lt;/h2&gt;
&lt;p&gt;procresize函数，考虑到初始化完成之后用户代码还可以通过 GOMAXPROCS()函数调用它重新创建和初始化p结构体对象，而在运行过程中再动态的调整p牵涉到的问题比较多，所以这个函数的处理比较复杂，但如果只考虑初始化，相对来说要简单很多，所以这里只保留了初始化时会执行的代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;54
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;55
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;56
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;57
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;58
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;59
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;60
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;61
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;62
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;63
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;64
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;65
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;66
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;67
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;68
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;69
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;70
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;71
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;72
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;73
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;74
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;75
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;76
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;77
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;78
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;79
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;80
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// runtime/proc.go : 4382
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;procresize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;nprocs&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;old&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;gomaxprocs&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//系统初始化时 gomaxprocs = 0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// Grow allp if necessary.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;nprocs&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//初始化时 len(allp) == 0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// Synchronize with retake, which could be running
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// concurrently since it doesn&amp;#39;t run on a P.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nf&#34;&gt;lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allpLock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;nprocs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;nprocs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//初始化时进入此分支，创建allp 切片
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;nx&#34;&gt;nallp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;nprocs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
            &lt;span class=&#34;c1&#34;&gt;// Copy everything up to allp&amp;#39;s cap so we
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;// never lose old allocated Ps.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;nb&#34;&gt;copy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;nallp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;cap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)])&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;nallp&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
        &lt;span class=&#34;nf&#34;&gt;unlock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allpLock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// initialize new P&amp;#39;s
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//循环创建nprocs个p并完成基本初始化
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;nprocs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;pp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;pp&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//调用内存分配器从堆上分配一个struct p
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;nx&#34;&gt;pp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;pp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_Pgcstop&lt;/span&gt;
            &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
            &lt;span class=&#34;nf&#34;&gt;atomicstorep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Pointer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Pointer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

        &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;// _g_ = g0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;nprocs&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//初始化时m0-&amp;gt;p还未初始化，所以不会执行这个分支
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// continue to use the current P
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_Prunning&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mcache&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;prepareForSweep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//初始化时执行这个分支
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// release the current P and acquire allp[0]
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//初始化时这里不执行
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mcache&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_Pidle&lt;/span&gt;
        &lt;span class=&#34;nf&#34;&gt;acquirep&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//把p和m0关联起来，其实是这两个strct的成员相互赋值
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;trace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;enabled&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
            &lt;span class=&#34;nf&#34;&gt;traceGoStart&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
   
    &lt;span class=&#34;c1&#34;&gt;//下面这个for 循环把所有空闲的p放入空闲链表
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;runnablePs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;nprocs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;--&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//allp[0]跟m0关联了，所以是不能放任
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_Pidle&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;runqempty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//初始化时除了allp[0]其它p全部执行这个分支，放入空闲链表
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;            &lt;span class=&#34;nf&#34;&gt;pidleput&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
            &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
   
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;runnablePs&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这个函数代码比较长，但并不复杂，这里总结一下这个函数的主要流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用make([]*p, nprocs)初始化全局变量allp，即allp = make([]*p, nprocs)&lt;/li&gt;
&lt;li&gt;循环创建并初始化nprocs个p结构体对象并依次保存在allp切片之中&lt;/li&gt;
&lt;li&gt;把m0和allp[0]绑定在一起，即m0.p = allp[0], allp[0].m = m0&lt;/li&gt;
&lt;li&gt;把除了allp[0]之外的所有p放入到全局变量sched的pidle空闲队列之中&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;到此m0, g0, 和m需要的p完全关联在一起了。这时整个调度器相关的各组成部分之间的联系如下图所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;   ┌────┬────allp───────┐            ┌───m0───────────────┐                                             
┏━━│[0] │    ......     │            │                    │                                             
┃  └────┴───────────────┘            │                    │                                             
┃                                    │                    │                                             
┃                                    │       ......       │             ┌────────────────────┐          
┃                                    │                    │             │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
┃                                    │                    │             │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
┃  ┌───p────────────────┐            │                    │             │▨▨▨▨▨▨▨kernel▨▨▨▨▨▨▨│          
┃  │                    │            ├────────────────────┤             │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
┃  │                    │      ┏━━━━━│         p          │             │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
┃  │                    │      ┃     ├────────────────────┤             ├────────────────────┼─────┬─   
┃  │                    │      ┃     │       ......       │             │□□□□□□□□□□□□□□□□□□□□│     │    
┃  │                    │      ┃     ├────────────────────┤             │□□□□□□□......□□□□□□□│     │main
┃  │      ......        │      ┃     │       tls[1]       │             │□□□□□□□□□□□□□□□□□□□□│    thread
┃  │                    │      ┃     ├────────────────────┤&amp;lt;-fs         ├────────────────────┤     stack
┃  │                    │      ┃  ┏━━│       tls[0]       │             │    argv[...]       │◀─┐  │    
┃  │                    │      ┃  ┃  ├────────────────────┤             ├────────────────────┤  │  │    
┃  │                    │      ┃  ┣━━│         g0         │◀━┓          │       argc         │  │  │    
┃  │                    │     ┏╋━━╋━▶└────────────────────┘  ┃          ├────────────────────┤  │  │    
┃  │                    │     ┃┃  ┃                          ┃          │       ......       │  │  │    
┃  ├────────────────────┤     ┃┃  ┃  ┌───g0───────────────┐  ┃          ├────────────────────┤  │  │    
┃  │         m          │━━━━━┛┃  ┃  │                    │  ┃          │       argv         │──┘  │    
┃  ├────────────────────┤      ┃  ┃  │                    │  ┃          ├────────────────────┤     ▼    
┃  │      ......        │      ┃  ┃  │                    │  ┃          │       argc         │          
┗━━▶────────────────────◀━━━━━━┛  ┃  │       ......       │  ┃          ├────────────────────┤          
                                  ┃  │                    │  ┃          │                    │          
                                  ┃  │                    │  ┃          ├────────────────────┤          
                                  ┃  │                    │  ┃          │                    │          
                                  ┃  ├────────────────────┤  ┃ ┏━━━━━━━━▶────────────────────┤&amp;lt;-sp      
                                  ┃  │         m          │━━┛ ┃        │                    │          
                                  ┃  ├────────────────────┤    ┃        │  64 * 1024 - 104   │          
                                  ┃  │    stackguard1     │━━━━╋━┓      │      bytes         │          
                                  ┃  ├────────────────────┤    ┃ ┃      │                    │          
                                  ┃  │    stackguard0     │━━━━╋━┫      │                    │          
                                  ┃  ├────────────────────┤    ┃ ┣━━━━━━▶────────────────────┘          
                                  ┃  │     stack.hi       │━━━━┛ ┃                                      
                                  ┃  ├────────────────────┤      ┃                                      
                                  ┃  │     stack.lo       │━━━━━━┛                                      
                                  ┗━━▶────────────────────┘                                             
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h1 id=&#34;创建main-goroutine&#34;&gt;创建main goroutine&lt;/h1&gt;
&lt;p&gt;schedinit完成调度系统初始化后，返回到rt0_go函数中开始调用newproc() 创建一个新的goroutine用于执行mainPC所对应的runtime·main函数，看下面的代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;// runtime/asm_amd64.s : 219
// create a new goroutine to start program
MOVQ  $runtime·mainPC(SB), AX // entry，mainPC是runtime.main
// newproc的第二个参数入栈，也就是新的goroutine需要执行的函数
PUSHQ  AX          // AX = &amp;amp;funcval{runtime·main},

// newproc的第一个参数入栈，该参数表示runtime.main函数需要的参数大小，因为runtime.main没有参数，所以这里是0
PUSHQ  $0
CALL  runtime·newproc(SB) // 创建main goroutine
POPQ  AX
POPQ  AX

// start this M
CALL  runtime·mstart(SB)  // 主线程进入调度循环，运行刚刚创建的goroutine

// 上面的mstart永远不应该返回的，如果返回了，一定是代码逻辑有问题，直接abort
CALL  runtime·abort(SB)// mstart should never return
RET

DATA  runtime·mainPC+0(SB)/8,$runtime·main(SB)
GLOB  Lruntime·mainPC(SB),RODATA,$8
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;newproc函数用于创建新的goroutine，它有两个参数，先说第二个参数fn，新创建出来的goroutine将从fn这个函数开始执行，而这个fn函数可能也会有参数，newproc的第一个参数正是fn函数的参数以字节为单位的大小。比如有如下go代码片段：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;go&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;编译器在编译上面的go语句时，就会把其替换为对newproc函数的调用，编译后的代码逻辑上等同于下面的伪代码&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x3&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x2&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x1&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;runtime&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;newproc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;24&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;start&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;那为什么需要传递fn函数的参数大小给newproc函数呢？原因就在于&lt;strong&gt;newproc函数将创建一个新的goroutine来执行fn函数&lt;/strong&gt;，而这个&lt;strong&gt;新创建的goroutine与当前这个goroutine会使用不同的栈&lt;/strong&gt;，因此就&lt;!-- raw HTML omitted --&gt;需要在创建goroutine的时候把fn需要用到的参数先从当前goroutine的栈上拷贝到新的goroutine的栈上之后才能让其开始执行&lt;!-- raw HTML omitted --&gt;，而newproc函数本身并不知道需要拷贝多少数据到新创建的goroutine的栈上去，所以需要用参数的方式指定拷贝多少数据。&lt;/p&gt;
&lt;p&gt;newproc函数是对newproc1的一个包装，这里最重要的准备工作有两个，一个是获取fn函数第一个参数的地址（代码中的argp），另一个是使用systemstack函数切换到g0栈，当然，对于我们这个初始化场景来说现在本来就在g0栈，所以不需要切换，然而这个函数是通用的，在用户的goroutine中也会创建goroutine，这时就需要进行栈的切换。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// runtime/proc.go : 3570
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;// Create a new g running fn with siz bytes of arguments.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Put it on the queue of g&amp;#39;s waiting to run.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// The compiler turns a go statement into a call to this.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Cannot split the stack because it assumes that the arguments
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// are available sequentially after &amp;amp;fn; they would not be
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// copied if a stack split occurred.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;//go:nosplit
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;newproc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;siz&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;funcval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;//函数调用参数入栈顺序是从右向左，而且栈是从高地址向低地址增长的
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//注意：argp指向fn函数的第一个参数，而不是newproc函数的参数
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//参数fn在栈上的地址+8的位置存放的是fn函数的第一个参数
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;argp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Pointer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;PtrSize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;gp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;//获取正在运行的g，初始化时是m0.g0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;   
    &lt;span class=&#34;c1&#34;&gt;//getcallerpc()返回一个地址，也就是调用newproc时由call指令压栈的函数返回地址，
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//对于我们现在这个场景来说，pc就是CALLruntime·newproc(SB)指令后面的POPQ AX这条指令的地址
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;pc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getcallerpc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
   
    &lt;span class=&#34;c1&#34;&gt;//systemstack的作用是切换到g0栈执行作为参数的函数
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//我们这个场景现在本身就在g0栈，因此什么也不做，直接调用作为参数的函数
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;systemstack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nf&#34;&gt;newproc1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;argp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;siz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;gp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;newproc1函数的第一个参数fn是新创建的goroutine需要执行的函数，注意这个fn的类型是funcval结构体类型，其定义如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;funcval&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// variable-size, fn-specific data here
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;newproc1的第二个参数argp是fn函数的第一个参数的地址，第三个参数是fn函数的参数以字节为单位的大小，后面两个参数我们不用关心。这里需要注意的是，newproc1是在g0的栈上执行的。该函数很长也很重要，所以我们分段来看。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// runtime/proc.go : 3601
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;// Create a new g running fn with narg bytes of arguments starting
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// at argp. callerpc is the address of the go statement that created
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// this. The new g is put on the queue of g&amp;#39;s waiting to run.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;newproc1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;funcval&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;argp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;uint8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;narg&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int32&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;callergp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;callerpc&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;uintptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;//因为已经切换到g0栈，所以无论什么场景都有 _g_ = g0，当然这个g0是指当前工作线程的g0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//对于我们这个场景来说，当前工作线程是主线程，所以这里的g0 = m0.g0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;

    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;_p_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_g_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//初始化时_p_ = g0.m.p，从前面的分析可以知道其实就是allp[0]
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;gfget&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_p_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//从p的本地缓冲里获取一个没有使用的g，初始化时没有，返回nil
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
         &lt;span class=&#34;c1&#34;&gt;//new一个g结构体对象，然后从堆上为其分配栈，并设置g的stack成员和两个stackgard成员
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;malg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_StackMin&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;nf&#34;&gt;casgstatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_Gidle&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_Gdead&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//初始化g的状态为_Gdead
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;         &lt;span class=&#34;c1&#34;&gt;//放入全局变量allgs切片中
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nf&#34;&gt;allgadd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// publishes with a g-&amp;gt;status of Gdead so GC scanner doesn&amp;#39;t look at uninitialized stack.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
   
    &lt;span class=&#34;o&#34;&gt;......&lt;/span&gt;
   
    &lt;span class=&#34;c1&#34;&gt;//调整g的栈顶置针，无需关注
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;totalSize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;RegSize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;uintptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;siz&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;MinFrameSize&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// extra space in case of reads slightly beyond frame
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;totalSize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;totalSize&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;SpAlign&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;                  &lt;span class=&#34;c1&#34;&gt;// align to spAlign
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;sp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;hi&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;totalSize&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;spArg&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sp&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;//......
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;   
    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;narg&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
         &lt;span class=&#34;c1&#34;&gt;//把参数从执行newproc函数的栈（初始化时是g0栈）拷贝到新g的栈
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;        &lt;span class=&#34;nf&#34;&gt;memmove&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Pointer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;spArg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Pointer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;argp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;uintptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;narg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
        &lt;span class=&#34;c1&#34;&gt;// ......
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这段代码主要从堆上分配一个g结构体对象并为这个newg分配一个大小为2048字节的栈，并设置好newg的stack成员，然后把newg需要执行的函数的参数从执行newproc函数的栈（初始化时是g0栈）拷贝到newg的栈，完成这些事情之后newg的状态如下图所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;   ┌────┬────allp───────┐            ┌───m0───────────────┐                                             
┏━━│[0] │    ......     │            │                    │                                             
┃  └────┴───────────────┘            │                    │                                             
┃                                    │                    │                                             
┃                                    │       ......       │             ┌────────────────────┐          
┃                                    │                    │             │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
┃                                    │                    │             │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
┃  ┌───p────────────────┐            │                    │             │▨▨▨▨▨▨▨kernel▨▨▨▨▨▨▨│          
┃  │                    │            ├────────────────────┤             │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
┃  │                    │      ┏━━━━━│         p          │             │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│          
┃  │                    │      ┃     ├────────────────────┤             ├────────────────────┼─────┬─   
┃  │                    │      ┃     │       ......       │             │□□□□□□□□□□□□□□□□□□□□│     │    
┃  │                    │      ┃     ├────────────────────┤             │□□□□□□□......□□□□□□□│     │main
┃  │      ......        │      ┃     │       tls[1]       │             │□□□□□□□□□□□□□□□□□□□□│    thread
┃  │                    │      ┃     ├────────────────────┤&amp;lt;-fs         ├────────────────────┤     stack
┃  │                    │      ┃  ┏━━│       tls[0]       │             │    argv[...]       │◀─┐  │    
┃  │                    │      ┃  ┃  ├────────────────────┤             ├────────────────────┤  │  │    
┃  │                    │      ┃  ┣━━│         g0         │◀━┓          │       argc         │  │  │    
┃  │                    │     ┏╋━━╋━▶└────────────────────┘  ┃          ├────────────────────┤  │  │    
┃  │                    │     ┃┃  ┃                          ┃          │       ......       │  │  │    
┃  ├────────────────────┤     ┃┃  ┃  ┌───g0───────────────┐  ┃          ├────────────────────┤  │  │    
┃  │         m          │━━━━━┛┃  ┃  │                    │  ┃          │       argv         │──┘  │    
┃  ├────────────────────┤      ┃  ┃  │                    │  ┃          ├────────────────────┤     ▼    
┗━▶│      ......        │      ┃  ┃  │                    │  ┃          │       argc         │          
   └────────────────────◀━━━━━━┛  ┃  │       ......       │  ┃          ├────────────────────┤          
                                  ┃  │                    │  ┃          │                    │          
                                  ┃  │                    │  ┃          ├────────────────────┤          
                                  ┃  │                    │  ┃          │                    │          
                                  ┃  ├────────────────────┤  ┃ ┏━━━━━━━━▶────────────────────┤          
                                  ┃  │         m          │━━┛ ┃        │   &amp;amp;funcval{        │          
   ┌───newg─────────────┐         ┃  ├────────────────────┤    ┃        │    runtime·main}   │          
   │                    │         ┃  │    stackguard1     │━━━━╋━┓      ├────────────────────┤          
   │                    │         ┃  ├────────────────────┤    ┃ ┃      │         0          │          
   │                    │         ┃  │    stackguard0     │━━━━╋━┫      ├────────────────────┤          
   │       ......       │         ┃  ├────────────────────┤    ┃ ┃      │   return address   │          
   │                    │         ┃  │     stack.hi       │━━━━┛ ┃      │   of newproc       │          
   │                    │         ┃  ├────────────────────┤      ┃      ├────────────────────┤&amp;lt;-sp      
   │                    │         ┗━▶│     stack.lo       │━━━━━━┫      │                    │          
   ├────────────────────┤            └────────────────────┘      ┃      │       ......       │          
   │       m=nil        │                                        ┃      │                    │          
   ├────────────────────┤                                        ┗━━━━━━▶────────────────────┤          
   │    stackguard1     │                                                       ......                 
   ├────────────────────┤               ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━▶────────────────────┤     ▲    
   │    stackguard0     │━━━━━━━━━━━━━━━╋━━━━━━━┓                       │                    │     │    
   ├────────────────────┤               ┃       ┃                       │                    │     │    
   │     stack.hi       │━━━━━━━━━━━━━━━┛       ┃                       │     newg stack     │   heap   
   ├────────────────────┤                       ┣━━━━━━━━━━━━━━━━━━━━━━━▶────────────────────┤     │    
   │     stack.lo       │━━━━━━━━━━━━━━━━━━━━━━━┛                       │▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨▨│     │    
   └────────────────────┘                                               └────────────────────┴─────┴─   
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;程序中多了一个我们称之为newg的g结构体对象，该对象也已经获得了从堆上分配而来的2k大小的栈空间，newg的stack.hi和stack.lo分别指向了其栈空间的起止位置。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;//把newg.sched结构体成员的所有成员设置为0
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nf&#34;&gt;memclrNoHeapPointers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Pointer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Sizeof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
   
    &lt;span class=&#34;c1&#34;&gt;//设置newg的sched成员，调度器需要依靠这些字段才能把goroutine调度到CPU上运行。
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sp&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sp&lt;/span&gt;  &lt;span class=&#34;c1&#34;&gt;//newg的栈顶
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stktopsp&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sp&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;//newg.sched.pc表示当newg被调度起来运行时从这个地址开始执行指令
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//把pc设置成了goexit这个函数偏移1（sys.PCQuantum等于1）的位置，
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;//至于为什么要这么做需要等到分析完gostartcallfn函数才知道
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pc&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;funcPC&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;goexit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sys&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;PCQuantum&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// +PCQuantum so that previous instruction is in same function
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;g&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;guintptr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;unsafe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Pointer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;

    &lt;span class=&#34;nf&#34;&gt;gostartcallfn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sched&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;//调整sched成员和newg的栈
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&amp;hellip;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;schedule()-&amp;gt;execute()-&amp;gt;gogo()-&amp;gt;g2()-&amp;gt;goexit()-&amp;gt;goexit1()-&amp;gt;mcall()-&amp;gt;goexit0()-&amp;gt;schedule()
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;每个工作线程的执行流程和调度循环都一样，如下图所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;                       ┌──────────────────┐                                               
                       │     mstart()     │                                               
                       └──────────────────┘                                               
                                 │                                                        
                                 ▼                                                        
                       ┌──────────────────┐                                               
                       │     mstart1()    │                                               
                       └──────────────────┘                                               
                                 │                                                        
                                 │                                                        
                                 │                                                        
┌────────────scheduling loop─────┼──────────────────┐              ┌──────g0 stack────┐   
│                                ▼                  │              │     ......       │   
│                      ┌──────────────────┐         │              ├──────────────────┤   
│          ┌──────────▶│    schedule()    │─ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ─▶│ mstart stack     │   
│          │           └──────────────────┘         │              ├──────────────────┤   
│          │                     │                  │              │ mstart1 stack    │◀─┐
│          │                     ▼                  │              ├──────────────────┤  │
│          │           ┌──────────────────┐         │              │ schedule stack   │  │
│          │           │     execule()    │         │              ├──────────────────┤  │
│          │           └──────────────────┘         │              │ execute stack    │  │
│          │                     │                  │              ├──────────────────┤  │
│          │                     │                  │              │ gogo stack       │  │
│          │                     ▼                  │              └──────────────────┘  │
│ ┌────────────────┐   ┌──────────────────┐         │                                    │
│ │ other runtime  │   │      gogo()      │         │                                    │
│ │  functions     │   └──────────────────┘         │                                    │
│ └────────────────┘             │                  │                                    │
│          ▲                     │                  │              ┌────────g0────────┐  │
│          │                     ▼                  │              │                  │  │
│          │           ┌──────────────────┐         │              ├──────────────────┤  │
│          │           │ user goroutine   │         │              │     ......       │  │
│          │           └──────────────────┘         │              ├──────────────────┤  │
│          │                     │                  │              │     sched.sp     │──┘
│          │                     │                  │              ├──────────────────┤   
│          │                     ▼                  │              │     ......       │   
│          │           ┌──────────────────┐         │              └──────────────────┘   
│          └───────────│     mcall()      │         │                                     
│                      └──────────────────┘         │                                     
│                                                   │                                     
└───────────────────────────────────────────────────┘                                     
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;工作线程的执行流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;初始化，调用mstart函数；&lt;/li&gt;
&lt;li&gt;调用mstart1函数，在该函数中调用save函数设置g0.sched.sp和g0.sched.pc等调度信息，其中g0.sched.sp指向mstart函数栈帧的栈顶；&lt;/li&gt;
&lt;li&gt;依次调用schedule-&amp;gt;execute-&amp;gt;gogo函数执行调度；&lt;/li&gt;
&lt;li&gt;运行用户的goroutine代码；&lt;/li&gt;
&lt;li&gt;用户goroutine代码执行过程中调用runtime中的某些函数，然后这些函数调用mcall切换到g0.sched.sp所指的栈并最终再次调用schedule函数进入新一轮调度，之后工作线程一直循环执行着3～5这一调度循环直到进程退出为止。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id=&#34;gpm-的状态流转&#34;&gt;GPM 的状态流转&lt;/h1&gt;
&lt;p&gt;G 的状态流转：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;┌──────────┐                                                      
│  _Gidle  │                                                      
└──────────┘                                                      
      │                             ┌───────────────────────┐     
   newproc                          │                       │     
      │                             ▼                       │     
      ▼                       ┏━━━━━━━━━━┓──────────┐       │     
┌──────────┐                  ┃          ┃          │       │     
│  _Gdead  │◀──────goexit─────┃_Grunning ┃◀─┐       │       │     
└──────────┘                  ┃          │  │       │       │     
      │                       ┗━━━━━━━━━━┫  │       │       │     
      │                             │    │  │       │       │     
  newproc       ┌──────────┐    park_m   │  │       │       │     
      │         │_Gwaiting │◀───────┘    │  │     enter    exit   
      ▼         └──────────┘             │  │    syscall syscall  
┏━━━━━━━━━━┓          │                  │  │       │       │     
┃          ┃          │                  │  │       │       │     
┃_Grunnable┃◀───ready─┘                  │  │       │       │     
┃          ┃                             │  │       │       │     
┗▲━━━━━━━━━┛◀─────────────Gosched────────┘  │       │       │     
 │    │                                     │       │       │     
 │    │                                     │       │       │     
 │    └──────────────execute────────────────┘       │       │     
 │                                                  ▼       │     
 │              exit                          ┌──────────┐  │     
 └────────────syscall0────────────────────────│_Gsyscall │──┘     
                                              └──────────┘        
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;P 的状态流转：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;	                                                            
     ┌──────────────startTheWorld─────────────┐             
     │                                        │             
     │                                        ▼             
┌────────┐                               ┌─────────┐        
│        │◀ ─ ─ ─ ─ ─ ─GC ─ ─ ─ ─ ─ ─ ─ ─│         │        
│_Pgcstop│                               │_Prunning│◀─┐     
│        │◀ ─ ─ ─GC ─ ─ ─   ┌─acquirep──▶│         │  │     
└────────┘               │  │            └┬────────┘  │     
     ▲  │                   │             │   │       │     
        │              ┌─┴──┴─┐           │   │       │     
     │  │              │      │  releasep │ enter    exit   
        └─procresize──▶│_Pidle│◀──retake──┘syscall syscall  
     │                 │      │               │       │     
                       └──────┘               │       │     
     │                     ▲                  ▼       │     
                           │             ┌─────────┐  │     
     │                     └───retake────┤         │  │     
      ─ ─ ─ ─GC ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─│_Psyscall│──┘     
                                         │         │        
                                         └─────────┘        
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;通常情况下（在程序运行时不调整 P 的个数），P 只会在上图中的四种状态下进行切换。当程序刚开始运行进行初始化时，所有的 P 都处于 &lt;code&gt;_Pgcstop&lt;/code&gt; 状态， 随着 P 的初始化（ &lt;code&gt;runtime.procresize&lt;/code&gt;），会被置于 &lt;code&gt;_Pidle&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;当 M 需要运行时，会 &lt;code&gt;runtime.acquirep&lt;/code&gt; 来使 P 变成 &lt;code&gt;Prunning&lt;/code&gt; 状态，并通过 &lt;code&gt;runtime.releasep&lt;/code&gt; 来释放。&lt;/p&gt;
&lt;p&gt;当 G 执行时需要进入系统调用，P 会被设置为 &lt;code&gt;_Psyscall&lt;/code&gt;， 如果这个时候被系统监控抢占（ &lt;code&gt;runtime.retake&lt;/code&gt;），则 P 会被重新修改为 &lt;code&gt;_Pidle&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;如果在程序运行中发生 &lt;code&gt;GC&lt;/code&gt;，则 P 会被设置为 &lt;code&gt;_Pgcstop&lt;/code&gt;， 并在 &lt;code&gt;runtime.startTheWorld&lt;/code&gt; 时重新调整为 &lt;code&gt;_Prunning&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;M 的状态变化：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt; ┌────────┐                   ┌────────────┐
 │spinning│───G not found────▶│non-spinning│
 └────────┘       GC          └────────────┘
      ▲                              │      
      │                              │      
      │                              │      
      │                              ▼      
┌──────────┐                    ┌─────────┐ 
│notewakeup│◀────by other M─────│notesleep│ 
└──────────┘                    └─────────┘ 
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h1 id=&#34;参考资料&#34;&gt;参考资料&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://mp.weixin.qq.com/s/n6LLhtGwVcCurgk8wP4q8Q&#34;&gt;源码游记-goroutine调度器系列&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      
    </item>
    
    <item>
      <title>英语学习规划</title>
      <link>https://kiyonlin.github.io/post/plans/%E8%8B%B1%E8%AF%AD%E8%A7%84%E5%88%92/</link>
      <pubDate>Thu, 12 Nov 2020 22:06:16 +0800</pubDate>
      
      <guid>https://kiyonlin.github.io/post/plans/%E8%8B%B1%E8%AF%AD%E8%A7%84%E5%88%92/</guid>
      
        <description>&lt;h2 id=&#34;7-9岁全面发展阶段&#34;&gt;7-9岁全面发展阶段&lt;/h2&gt;
&lt;h2 id=&#34;7岁&#34;&gt;7岁&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;报班学一套综合教材&lt;/li&gt;
&lt;li&gt;开始正式学习语法、精读、拼写、写作&lt;/li&gt;
&lt;li&gt;泛读分级读物300-1000本&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;精读&#34;&gt;精读&lt;/h3&gt;
&lt;p&gt;一句一句朗读出来，学习词汇、练习拼写、学习语法、做阅读理解、分析作者意图。可以从短篇开始。需要老师领着读，不建议家长操作。&lt;/p&gt;
&lt;h3 id=&#34;习得&#34;&gt;习得&lt;/h3&gt;
&lt;p&gt;大量听大量读&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Case Closed 名侦探柯南&lt;/li&gt;
&lt;li&gt;Word Girl 单词女孩&lt;/li&gt;
&lt;li&gt;Avatar 降世神通&lt;/li&gt;
&lt;li&gt;Ben 10 少年骇客&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Phonics推荐&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;国外
&lt;ul&gt;
&lt;li&gt;Scholastic公司的Accelerated Reader、Guided Reading&lt;/li&gt;
&lt;li&gt;Reading A to Z 网站&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;国内
&lt;ul&gt;
&lt;li&gt;朗文机灵狗故事乐园和开心小读者&lt;/li&gt;
&lt;li&gt;国家地理儿童百科&lt;/li&gt;
&lt;li&gt;典范英语（牛津阅读树）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;8岁&#34;&gt;8岁&lt;/h2&gt;
&lt;p&gt;继续课程，重点学习口语、语法、精读和写作。&lt;/p&gt;
&lt;p&gt;阅读推荐&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Junie B. Jones系列&lt;/li&gt;
&lt;li&gt;Magic Tree House系列&lt;/li&gt;
&lt;li&gt;Arthur系列&lt;/li&gt;
&lt;li&gt;Judy Moody系列和Stink系列&lt;/li&gt;
&lt;li&gt;Geronimo Stilton系列&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;9岁&#34;&gt;9岁&lt;/h2&gt;
&lt;p&gt;阅读推荐&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Goosebumps系列&lt;/li&gt;
&lt;li&gt;The Spiderwick Chronicles系列&lt;/li&gt;
&lt;li&gt;Diary of a Wimpy Kid系列&lt;/li&gt;
&lt;li&gt;Roald Dahl作品系列
&lt;ul&gt;
&lt;li&gt;Charlie and the Chocolate Factory&lt;/li&gt;
&lt;li&gt;James and the Giant Peach&lt;/li&gt;
&lt;li&gt;Matilda&lt;/li&gt;
&lt;li&gt;The Witches&lt;/li&gt;
&lt;li&gt;The BFG&lt;/li&gt;
&lt;li&gt;Fantastic Mr Fox&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Beverly Cleary作品
&lt;ul&gt;
&lt;li&gt;Ramona系列&lt;/li&gt;
&lt;li&gt;Henry系列&lt;/li&gt;
&lt;li&gt;Mouse系列&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Judy Blume作品
&lt;ul&gt;
&lt;li&gt;Fudge系列&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Andrew Clements的学校生活系列&lt;/li&gt;
&lt;li&gt;The 39 CLUES系列&lt;/li&gt;
&lt;li&gt;Guardians of Ga&amp;rsquo;Hoole系列&lt;/li&gt;
&lt;li&gt;The underland Chronicles系列&lt;/li&gt;
&lt;li&gt;Percy Jackson and the Olympians系列&lt;/li&gt;
&lt;li&gt;The Heroes of Olympus&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;10岁&#34;&gt;10岁&lt;/h2&gt;
&lt;h3 id=&#34;学得&#34;&gt;学得&lt;/h3&gt;
&lt;p&gt;加大学习读写的力度，系统复习语法知识，提高读写综合能力，并开始向学科英语和学术英语过渡。同时备考FCE证书。&lt;/p&gt;
&lt;h3 id=&#34;习得-1&#34;&gt;习得&lt;/h3&gt;
&lt;p&gt;扩大知识面，广泛阅读历史、地理、自然科学、社会科学的non-fiction书籍、杂志、报刊，并多看科普节目、访谈节目、电影。&lt;/p&gt;
&lt;p&gt;阅读推荐&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How to Train Your Dragon 驯龙高手&lt;/li&gt;
&lt;li&gt;Alex Rider 亚力克斯骑士&lt;/li&gt;
&lt;li&gt;Warriors 猫武士&lt;/li&gt;
&lt;li&gt;Harry Potter&lt;/li&gt;
&lt;li&gt;Pendragon&lt;/li&gt;
&lt;li&gt;The Indian in the Cupboard&lt;/li&gt;
&lt;li&gt;Charlie Bone&lt;/li&gt;
&lt;li&gt;Horrible Histories&lt;/li&gt;
&lt;li&gt;The Secret Series&lt;/li&gt;
&lt;li&gt;xxx照片&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;11-12&#34;&gt;11-12&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Sammy Keyes&lt;/li&gt;
&lt;li&gt;Artemis Fowl&lt;/li&gt;
&lt;li&gt;A Series of Unfortunate Events&lt;/li&gt;
&lt;li&gt;The Hobbie和The Lord of the Rings三部曲&lt;/li&gt;
&lt;li&gt;The Inheritance Cycle&lt;/li&gt;
&lt;li&gt;The Hunger Games&lt;/li&gt;
&lt;li&gt;His Dark Materials&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每天花半小时到1小时听读，听多读少，特别是小说类。听力比阅读先行，听的比读的高两个年级。一套听，一套读。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>在Golang中轻松使用ipset</title>
      <link>https://kiyonlin.github.io/post/work/gonetx/ipset/</link>
      <pubDate>Tue, 15 Sep 2020 10:26:01 +0800</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/gonetx/ipset/</guid>
      
        <description>&lt;h1 id=&#34;前言&#34;&gt;前言&lt;/h1&gt;
&lt;p&gt;因为工作关系，需要使用&lt;code&gt;ipset&lt;/code&gt;，但是截止目前，&lt;code&gt;golang&lt;/code&gt;社区中对&lt;code&gt;ipset&lt;/code&gt;的支持并不好。之前用的是&lt;a href=&#34;https://github.com/janeczku/go-ipset&#34;&gt;go-ipset&lt;/a&gt;，只能满足基础的使用，而且只支持&lt;code&gt;hash&lt;/code&gt;类型以及部分选项。所以萌生了全面封装&lt;code&gt;golang&lt;/code&gt;版本&lt;code&gt;ipset&lt;/code&gt;的想法，才有了&lt;a href=&#34;https://github.com/kiyonlin/gonetx/blob/master/ipset/README.md&#34;&gt;gonetx/ipset&lt;/a&gt;的诞生。&lt;/p&gt;
&lt;h1 id=&#34;功能&#34;&gt;功能&lt;/h1&gt;
&lt;p&gt;支持所有的&lt;code&gt;ispet&lt;/code&gt;常用命令、类型以及实用的选项。&lt;/p&gt;
&lt;h2 id=&#34;常用命令&#34;&gt;常用命令&lt;/h2&gt;
&lt;p&gt;支持增删改查导入导出等常用命令，用法可以参见&lt;a href=&#34;#%E5%BF%AB%E9%80%9F%E5%BC%80%E5%A7%8B&#34;&gt;快速开始&lt;/a&gt;和&lt;a href=&#34;#%E8%BF%9B%E9%98%B6%E4%BD%BF%E7%94%A8&#34;&gt;进阶使用&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;全类型支持&#34;&gt;全类型支持&lt;/h2&gt;
&lt;p&gt;支持所有&lt;code&gt;ipset&lt;/code&gt;的方法类型&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bitmap&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hash&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;list&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;和数据类型&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ip&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;net&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mac&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;port&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;iface&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;灵活的选项&#34;&gt;灵活的选项&lt;/h2&gt;
&lt;p&gt;每种&lt;code&gt;set&lt;/code&gt;类型都会有各自的选项，主要集中在创建和新增操作上。目前支持&lt;code&gt;22&lt;/code&gt;个选项，常用的有&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;func Exist(exist bool) Option&lt;/code&gt;：创建&lt;code&gt;set&lt;/code&gt;或添加&lt;code&gt;entry&lt;/code&gt;时，若数据已存在报不报错；删除&lt;code&gt;entry&lt;/code&gt;时不存在数据报不报错；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;func Timeout(timeout time.Duration) Option&lt;/code&gt;：创建&lt;code&gt;set&lt;/code&gt;或者添加&lt;code&gt;entry&lt;/code&gt;时可以指定超时时间；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;func MaxElem(maxElem uint) Option&lt;/code&gt;：指定&lt;code&gt;hash&lt;/code&gt;方法类型的&lt;code&gt;set&lt;/code&gt;容量；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;func Family(family NetFamily) Option&lt;/code&gt;：指定&lt;code&gt;hash&lt;/code&gt;方法类型的&lt;code&gt;set&lt;/code&gt;网络协议簇；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;func HashSize(hashSize uint) Option&lt;/code&gt;：指定&lt;code&gt;hash&lt;/code&gt;方法类型的&lt;code&gt;set&lt;/code&gt;初始化&lt;code&gt;hash&lt;/code&gt;大小。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更多选项详情可以查看&lt;a href=&#34;https://pkg.go.dev/github.com/kiyonlin/gonetx/ipset?tab=doc&#34;&gt;文档&lt;/a&gt;。&lt;/p&gt;
&lt;h1 id=&#34;示例&#34;&gt;示例&lt;/h1&gt;
&lt;p&gt;通过示例可以更快地了解该包的使用。需要注意的是，使用该包之前，必须调用&lt;code&gt;ispet.Check&lt;/code&gt;检查系统是否支持&lt;code&gt;ispet&lt;/code&gt;操作以及版本是否大于&lt;code&gt;v6.0&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;快速开始&#34;&gt;快速开始&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;

&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
	&lt;span class=&#34;s&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
	&lt;span class=&#34;s&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;

	&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/kiyonlin/gonetx/ipset&amp;#34;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Check&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;nb&#34;&gt;panic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// create test set even it&amp;#39;s exist
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;HashIp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Exist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Hour&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// output: test
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Flush&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1.1.1.1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Hour&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1.1.1.1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// output: true
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Test&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1.1.1.2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// output: false
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// output: &amp;amp;{test hash:ip 4 family inet hashsize 1024 maxelem 65536 timeout 3600 216 0 [1.1.1.1 timeout 3599]}
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Del&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;1.1.1.1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Destroy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;进阶使用&#34;&gt;进阶使用&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;

&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
	&lt;span class=&#34;s&#34;&gt;&amp;#34;bytes&amp;#34;&lt;/span&gt;
	&lt;span class=&#34;s&#34;&gt;&amp;#34;io/ioutil&amp;#34;&lt;/span&gt;
	&lt;span class=&#34;s&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
	&lt;span class=&#34;s&#34;&gt;&amp;#34;time&amp;#34;&lt;/span&gt;

	&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/kiyonlin/gonetx/ipset&amp;#34;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Check&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;nb&#34;&gt;panic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// create test set even it&amp;#39;s exist
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;HashIp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Exist&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Timeout&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Hour&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Family&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Inet&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;HashSize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1024&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;ipset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;MaxElem&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;100000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;c1&#34;&gt;// Saved content:
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ioutil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WriteFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;saved&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
		&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;add test 1.1.1.1 timeout 3600 -exist\nadd test 1.1.1.2 timeout 3600 -exist\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
		&lt;span class=&#34;mo&#34;&gt;0600&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;RestoreFromFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;saved&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WriteString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;add test 1.1.1.3 timeout 3600 -exist\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;WriteString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;add test 1.1.1.4 timeout 3600 -exist\n&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Restore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// output: &amp;amp;{test hash:ip 4 family inet hashsize 1024 maxelem 100000 timeout 10800 504 0 [1.1.1.3 timeout 3599 1.1.1.2 timeout 3599 1.1.1.1 timeout 3599 1.1.1.4 timeout 3599]}
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;info&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SaveToFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;saved&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// cat saved:
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;//create test hash:ip family inet hashsize 1024 maxelem 100000 timeout 10800
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;//add test 1.1.1.3 timeout 3599
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;//add test 1.1.1.2 timeout 3599
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;//add test 1.1.1.1 timeout 3599
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;//add test 1.1.1.4 timeout 3599
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h1 id=&#34;最后&#34;&gt;最后&lt;/h1&gt;
&lt;p&gt;有任何疑问或者改进，请提交&lt;a href=&#34;https://github.com/kiyonlin/gonetx/issues&#34;&gt;issue&lt;/a&gt;或者&lt;a href=&#34;https://github.com/kiyonlin/gonetx/pulls&#34;&gt;PR&lt;/a&gt;。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>Fasthttp系列——解析请求头</title>
      <link>https://kiyonlin.github.io/post/work/fasthttp/02fh%E8%A7%A3%E6%9E%90%E8%AF%B7%E6%B1%82%E5%A4%B4/</link>
      <pubDate>Fri, 21 Aug 2020 19:16:54 +0800</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/fasthttp/02fh%E8%A7%A3%E6%9E%90%E8%AF%B7%E6%B1%82%E5%A4%B4/</guid>
      
        <description>&lt;blockquote&gt;
&lt;p&gt;本系列通过解析&lt;code&gt;fasthttp&lt;/code&gt;(&lt;a href=&#34;https://github.com/valyala/fasthttp/tree/v1.16.0&#34;&gt;v1.16.0&lt;/a&gt;)源码学习&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html&#34;&gt;HTTP/1.1&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://kiyonlin.github.io/post/work/fasthttp/01fh%E8%AF%B7%E6%B1%82%E8%A1%8C/&#34;&gt;上一篇文章&lt;/a&gt;我们学习了&lt;code&gt;HTTP/1.1&lt;/code&gt;的请求行(&lt;code&gt;Request Line&lt;/code&gt;)，下面我们继续学习请求头的解析。&lt;/p&gt;
&lt;h2 id=&#34;请求头格式&#34;&gt;请求头格式&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;HTTP/1.1&lt;/code&gt;的&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#http.message&#34;&gt;消息格式&lt;/a&gt;如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;  HTTP-message   = start-line
                   *( header-field CRLF )
                   CRLF
                   [ message-body ]
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;start-line: 起始行&lt;/li&gt;
&lt;li&gt;*( header-field CRLF ): 头字段+分隔符(&lt;code&gt;\r\n&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;CRLF: 空行&lt;/li&gt;
&lt;li&gt;[ message-body ]: 可选的消息体&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所有&lt;code&gt;HTTP/1.1&lt;/code&gt;消息均包含一个起始行，其后是一系列头字段，由分隔符(&lt;code&gt;\r\n&lt;/code&gt;)隔开，接着是一个&lt;strong&gt;空行&lt;/strong&gt;(&lt;code&gt;\r\n&lt;/code&gt;)，最后是可选的消息体。&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://kiyonlin.github.io/post/work/fasthttp/01fh%E8%AF%B7%E6%B1%82%E8%A1%8C/&#34;&gt;上一篇文章&lt;/a&gt;学习的请求行就是起始行的一种，还有一种是&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#status.line&#34;&gt;状态行(Status Line)&lt;/a&gt;，我们会在之后讲解。&lt;/p&gt;
&lt;p&gt;我们可以发现，每一个头字段后面都需要跟随一个分隔符(&lt;code&gt;\r\n&lt;/code&gt;)，&lt;strong&gt;当某一行只有分隔符(&lt;code&gt;\r\n&lt;/code&gt;)时&lt;/strong&gt;，就意味着请求头数据已发送完毕。使用&lt;a href=&#34;https://httpie.org/&#34;&gt;httpie&lt;/a&gt;就可以轻松查看一个真实的请求头数据：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;$ http &lt;span class=&#34;s2&#34;&gt;&amp;#34;www.baidu.com&amp;#34;&lt;/span&gt; -p H
&lt;span class=&#34;c1&#34;&gt;# 请求行&lt;/span&gt;
GET / HTTP/1.1
&lt;span class=&#34;c1&#34;&gt;# 请求头&lt;/span&gt;
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: www.baidu.com
User-Agent: HTTPie/1.0.3

&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;解析请求头&#34;&gt;解析请求头&lt;/h2&gt;
&lt;p&gt;我们再来看看&lt;code&gt;fasthttp&lt;/code&gt;是如何解析请求头的。当&lt;code&gt;fasthttp&lt;/code&gt;读取到完整的请求头数据后，开始解析请求头，并返回请求头的字节数：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;RequestHeader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;parse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;parseFirstLine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

	&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rawHeaders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;readRawHeaders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rawHeaders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:])&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;parseHeaders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:])&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们看到了熟悉的&lt;code&gt;parseFirstLine&lt;/code&gt;，解析请求行，接着继续读取并保存请求头的原始数据&lt;code&gt;h.rawHeaders, _, err = readRawHeaders(h.rawHeaders[:0], buf[m:])&lt;/code&gt;。在&lt;code&gt;readRawHeaders&lt;/code&gt;中我们可以看到(&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L1760-L1786&#34;&gt;完整源码&lt;/a&gt;)：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;readRawHeaders&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;([]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;IndexByte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39;\r&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// empty headers
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;IndexByte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39;\r&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
			&lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;			&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;dst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
		&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;目的就是找到单独的&lt;code&gt;\r\n&lt;/code&gt;或者&lt;code&gt;\n&lt;/code&gt;，一旦找到，就表示没有更多的请求头数据了。从源码中我们可以发现&lt;em&gt;&lt;code&gt;fasthttp&lt;/code&gt;兼容了以&lt;code&gt;\n&lt;/code&gt;作为分隔符的数据格式&lt;/em&gt;。&lt;/p&gt;
&lt;p&gt;最后是解析请求头信息&lt;code&gt;n, err = h.parseHeaders(buf[m:])&lt;/code&gt;，解析成功后，返回第一行数据和请求头的总字节数。&lt;code&gt;parseHeaders&lt;/code&gt;的&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L1868-L1952&#34;&gt;代码&lt;/a&gt;比较长，就不再全部贴出来。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fasthttp&lt;/code&gt;在解析请求头时，使用了&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L1985-L2005&#34;&gt;headerScanner&lt;/a&gt;这个结构体，调用&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L2007-L2114&#34;&gt;next&lt;/a&gt;方法一行一行解析。&lt;/p&gt;
&lt;p&gt;针对指定的头字段：&lt;a href=&#34;#Host&#34;&gt;Host&lt;/a&gt;，&lt;a href=&#34;#UserAgent&#34;&gt;UserAgent&lt;/a&gt;，&lt;a href=&#34;#ContentType&#34;&gt;Content-Type&lt;/a&gt;，&lt;a href=&#34;#ContentLength&#34;&gt;Content-Length&lt;/a&gt;，&lt;a href=&#34;#Connection&#34;&gt;Connection&lt;/a&gt;和&lt;a href=&#34;#TransferEncoding&#34;&gt;Transfer-Encoding&lt;/a&gt;，&lt;code&gt;fasthttp&lt;/code&gt;会做一些特殊操作，我们会一一进行解析。其他头字段信息会存入&lt;code&gt;h.h&lt;/code&gt;(&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L70&#34;&gt;源码&lt;/a&gt;)中：&lt;code&gt;h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们先深入分析&lt;code&gt;headerScanner.next()&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;头字段&#34;&gt;头字段&lt;/h3&gt;
&lt;p&gt;首先我们看看&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#header.fields&#34;&gt;头字段&lt;/a&gt;的结构：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;  header-field   = field-name &amp;#34;:&amp;#34; OWS field-value OWS

  field-name     = token
  field-value    = *( field-content / obs-fold )
  field-content  = field-vchar [ 1*( SP / HTAB ) field-vchar ]
  field-vchar    = VCHAR / obs-text

  obs-fold       = CRLF 1*( SP / HTAB )
                 ; obsolete line folding
                 ; see Section 3.2.4
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;每个头字段均由不区分大小写的字段名，后跟冒号(&lt;code&gt;:&lt;/code&gt;)，可选的前导空白(&lt;code&gt;OWS&lt;/code&gt;)，字段值和可选的尾随空白(&lt;code&gt;OWS&lt;/code&gt;)组成。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注：&lt;code&gt;OWS&lt;/code&gt;指的是&lt;code&gt;optional whitespace&lt;/code&gt;，&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#whitespace&#34;&gt;详情链接&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;fasthttp&lt;/code&gt;的&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L1985-L2005&#34;&gt;headerScanner&lt;/a&gt;就是头部扫描器，负责扫描请求头数据，解析出头字段的字段名(&lt;code&gt;key&lt;/code&gt;)和字段值(&lt;code&gt;value&lt;/code&gt;)，每次调用&lt;code&gt;next()&lt;/code&gt;都解析一行数据。&lt;/p&gt;
&lt;p&gt;根据标准，头字段名称紧跟着一个&lt;code&gt;:&lt;/code&gt;，所以需要根据&lt;code&gt;:&lt;/code&gt;的位置确认头字段名称的值。这里&lt;code&gt;fasthttp&lt;/code&gt;加了一个&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L2039-L2043&#34;&gt;处理&lt;/a&gt;：&lt;code&gt;:&lt;/code&gt;之前不能有&lt;code&gt;\n&lt;/code&gt;，因为此时这个头字段肯定是非法的。&lt;/p&gt;
&lt;p&gt;解析出头字段名称后，&lt;code&gt;fasthttp&lt;/code&gt;根据&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/server.go#L315-L331&#34;&gt;配置&lt;/a&gt;看需不需要标准化头名称，&lt;code&gt;normalizeHeaderKey(s.key, s.disableNormalizing)&lt;/code&gt;，这是为了统一客户端发送的请求头字段名称格式，防止出现类似&lt;code&gt;cONTENT-lenGTH&lt;/code&gt;的字段名而在使用时无法匹配的情况。&lt;/p&gt;
&lt;p&gt;一般情况下，每个头字段的值在一行内，但是也有可能放在多行里(obs-fold)，举个例子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;# 一行
Header: value1, value2

# 多行
Header: value1,
        value2
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这两种格式的值是等价的，想深入了解的可以查看&lt;a href=&#34;https://stackoverflow.com/questions/31237198/is-it-possible-to-include-multiple-crlfs-in-a-http-header-field&#34;&gt;这里&lt;/a&gt;。&lt;code&gt;fasthttp&lt;/code&gt;也&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L2071-L2112&#34;&gt;兼容&lt;/a&gt;了这种情况，然后对多行值进行格式化&lt;code&gt;s.value, s.b, s.hLen = normalizeHeaderValue(s.value, oldB, s.hLen)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;最终获取到&lt;code&gt;s.key&lt;/code&gt;和&lt;code&gt;s.value&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;处理头字段键值对&#34;&gt;处理头字段键值对&lt;/h3&gt;
&lt;p&gt;获取到头字段键值对后，对于一般的头字段，&lt;code&gt;fasthttp&lt;/code&gt;将他们存储在&lt;code&gt;h.h []argsKV&lt;/code&gt;切片中(&lt;code&gt;h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)&lt;/code&gt;)，&lt;code&gt;argsKV&lt;/code&gt;具体结构如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;argsKV&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;     &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;noValue&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;为什么不直接存在&lt;code&gt;map&lt;/code&gt;中呢？我们之后会讲解，主要是为了&lt;strong&gt;延迟解析&lt;/strong&gt;以及&lt;strong&gt;内存复用&lt;/strong&gt;。现在就想知道原因的可以查看&lt;a href=&#34;https://youtu.be/ghKCFvSNQkQ?t=822&#34;&gt;youtu.be 13:42&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;接下来我们讲讲几个特殊的头字段，以及&lt;code&gt;fasthttp&lt;/code&gt;对它们的处理。不过在此之前，我们需要看懂这行代码：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;mh&#34;&gt;0x20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;翻译一下就是将&lt;code&gt;s.key[0]&lt;/code&gt;，即头字段名称的首字母，转为小写。原理也很简单，用一段代码解释：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b01000001&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b01100001&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b00100000&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 0x20
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;A&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// true
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;println&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// true
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id=&#34;host&#34;&gt;Host&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Host&lt;/code&gt;头字段(&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#header.host&#34;&gt;详情&lt;/a&gt;)提供了目标&lt;code&gt;URI&lt;/code&gt;的主机和端口信息，使服务器能够区分资源，同时为单个&lt;code&gt;IP&lt;/code&gt;地址上的多个主机名的请求提供服务。&lt;code&gt;Host&lt;/code&gt;具有指定的格式：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;  Host = uri-host [ &amp;#34;:&amp;#34; port ] ; Section 2.7.1
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;RFC7230&lt;/code&gt;中指明&lt;code&gt;Host&lt;/code&gt;是必须字段&lt;/strong&gt;。若&lt;code&gt;Host&lt;/code&gt;不合法(未提供，提供多个，格式非法)，服务端必须响应&lt;code&gt;400 Bad Request&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;通过&lt;code&gt;caseInsensitiveCompare(s.key, strHost)&lt;/code&gt;比较头字段名称和常量&lt;code&gt;strHost&lt;/code&gt;，确定是否将其存入请求头的&lt;code&gt;host&lt;/code&gt;字段：&lt;code&gt;h.host = append(h.host[:0], s.value...)&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注：&lt;code&gt;caseInsensitiveCompare&lt;/code&gt;也用到了&lt;code&gt;0x20&lt;/code&gt;的技巧(&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/cookie.go#L538-L550&#34;&gt;源码&lt;/a&gt;)。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&#34;useragent&#34;&gt;UserAgent&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;User-Agent&lt;/code&gt;头字段(&lt;a href=&#34;https://httpwg.org/specs/rfc7231.html#header.user-agent&#34;&gt;详情&lt;/a&gt;)包含有关发起请求的用户代理的信息，服务器通常使用该信息来帮助识别报告的互操作性问题的范围，解决或调整响应以避免特定的用户代理限制以及进行分析有关浏览器或操作系统的使用。用户代理应该在每个请求中发送一个&lt;code&gt;User-Agent&lt;/code&gt;字段。&lt;/p&gt;
&lt;p&gt;它的结构如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;  User-Agent = product *( RWS ( product / comment ) )
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;该字段的值直接存入请求头的&lt;code&gt;userAgent&lt;/code&gt;字段中。&lt;/p&gt;
&lt;h4 id=&#34;contenttype&#34;&gt;ContentType&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Content-Type&lt;/code&gt;头字段(&lt;a href=&#34;https://httpwg.org/specs/rfc7231.html#header.content-type&#34;&gt;详情&lt;/a&gt;)指定了请求的&lt;a href=&#34;https://httpwg.org/specs/rfc7231.html#media.type&#34;&gt;媒体类型&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;常见的例子有：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;  Content-Type: text/html; charset=ISO-8859-4
  Content-Type: application/json; charset=utf-8
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;该字段的值直接存入请求头的&lt;code&gt;contentType&lt;/code&gt;字段中。&lt;/p&gt;
&lt;h4 id=&#34;contentlength&#34;&gt;ContentLength&lt;/h4&gt;
&lt;p&gt;当请求中没有&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#header.transfer-encoding&#34;&gt;Transfer-Encoding&lt;/a&gt;头字段时，可以设置&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#header.content-length&#34;&gt;Content-Length&lt;/a&gt;头字段，为潜在的消息体提供预期的大小（八位字节的十进制数）；若包含&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#header.transfer-encoding&#34;&gt;Transfer-Encoding&lt;/a&gt;头字段，则&lt;strong&gt;无法&lt;/strong&gt;设置&lt;code&gt;Content-Length&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fasthttp&lt;/code&gt;使用&lt;code&gt;parseContentLength&lt;/code&gt;(&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L1974-L1983&#34;&gt;源码&lt;/a&gt;)解析具体的数值，并&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/bytesconv.go#L189-L192&#34;&gt;处理&lt;/a&gt;了溢出的情况，避免了&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#attack.protocol.element.length&#34;&gt;通过协议元素长度产生的攻击&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;若解析成功，&lt;code&gt;fasthttp&lt;/code&gt;也会将原始的字节数据保存在&lt;code&gt;h.contentLengthBytes = append(h.contentLengthBytes[:0], s.value...)&lt;/code&gt;中。&lt;/p&gt;
&lt;h4 id=&#34;connection&#34;&gt;Connection&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Connection&lt;/code&gt;头字段(&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#header.connection&#34;&gt;详情&lt;/a&gt;)允许客户端控制当前连接。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;code&gt;Connection&lt;/code&gt;值是不区分大小写的&lt;/em&gt;，所以&lt;code&gt;fasthttp&lt;/code&gt;使用&lt;code&gt;bytes.Equal(s.value, strClose)&lt;/code&gt;来比较连接值是否为常量&lt;code&gt;strClose(&amp;quot;close&amp;quot;)&lt;/code&gt;，并保存到&lt;code&gt;connectionClose&lt;/code&gt;字段中，同时将原始的字节数据保存在&lt;code&gt;h.h = appendArgBytes(h.h, s.key, s.value, argsHasValue)&lt;/code&gt;中。&lt;/p&gt;
&lt;h4 id=&#34;transferencoding&#34;&gt;TransferEncoding&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;Transfer-Encoding&lt;/code&gt;头字段(&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#header.transfer-encoding&#34;&gt;详情&lt;/a&gt;)列出与已经（或将要）应用于有效载荷主体以形成消息主体的传输编码序列相对应的传输编码名称。传输编码在&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#transfer.codings&#34;&gt;这里&lt;/a&gt;定义，有兴趣的读者可以自行了解。&lt;/p&gt;
&lt;p&gt;我们看看&lt;code&gt;fasthttp&lt;/code&gt;的处理逻辑：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Equal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strIdentity&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;contentLength&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;setArgBytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strTransferEncoding&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strChunked&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;argsHasValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;因为&lt;code&gt;identity&lt;/code&gt;已经被&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#changes.from.rfc.2616&#34;&gt;移除&lt;/a&gt;，所以&lt;code&gt;fasthttp&lt;/code&gt;忽略了&lt;code&gt;identity&lt;/code&gt;，且根据我们在&lt;a href=&#34;#ContentLength&#34;&gt;Content-Length&lt;/a&gt;中学到的知识，此时应该忽略&lt;code&gt;contentLength&lt;/code&gt;，所以将其设为&lt;code&gt;-1&lt;/code&gt;，对应了&lt;code&gt;Content-Length&lt;/code&gt;头字段的&lt;code&gt;if h.contentLength != -1 {...}&lt;/code&gt;(&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L1901-L1911&#34;&gt;源码&lt;/a&gt;)处理。最后把&lt;code&gt;Transfer-Encoding&lt;/code&gt;头字段的值设为&lt;code&gt;chunked&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id=&#34;收尾操作&#34;&gt;收尾操作&lt;/h3&gt;
&lt;p&gt;解析完所有的头字段后，&lt;code&gt;fasthttp&lt;/code&gt;进行了一些收尾操作：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;contentLength&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;contentLengthBytes&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;contentLengthBytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;noHTTP11&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;connectionClose&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// close connection for non-http/1.1 request unless &amp;#39;Connection: keep-alive&amp;#39; is set.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;peekArgBytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strConnection&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;connectionClose&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;hasHeaderValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strKeepAlive&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ol&gt;
&lt;li&gt;未设置&lt;code&gt;h.contentLength&lt;/code&gt;时，清空&lt;code&gt;h.contentLengthBytes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;不是&lt;code&gt;HTTP/1.1&lt;/code&gt;的情况下重新设置&lt;code&gt;h.connectionClose&lt;/code&gt;的值(排除&lt;code&gt;Connection: keep-alive&lt;/code&gt;的情况)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;请求头解析部分到这里就告一段落了，我们学习了不少请求头相关的知识，稍微总结：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个请求头是根据分隔符(&lt;code&gt;\r\n&lt;/code&gt;)分隔的，但可以包含多行值&lt;/li&gt;
&lt;li&gt;空行代表请求头数据的终止&lt;/li&gt;
&lt;li&gt;一些具体的请求头概念&lt;/li&gt;
&lt;li&gt;使用&lt;code&gt;scanner&lt;/code&gt;解析数据流&lt;/li&gt;
&lt;li&gt;字母字符比较时使用&lt;code&gt;0x20&lt;/code&gt;的小技巧&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;敬请期待之后的系列文章！&lt;/em&gt; 👋&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>Fasthttp系列——请求行</title>
      <link>https://kiyonlin.github.io/post/work/fasthttp/01fh%E8%AF%B7%E6%B1%82%E8%A1%8C/</link>
      <pubDate>Tue, 18 Aug 2020 14:08:37 +0800</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/fasthttp/01fh%E8%AF%B7%E6%B1%82%E8%A1%8C/</guid>
      
        <description>&lt;blockquote&gt;
&lt;p&gt;本系列通过解析&lt;code&gt;fasthttp&lt;/code&gt;(&lt;a href=&#34;https://github.com/valyala/fasthttp/tree/v1.16.0&#34;&gt;v1.16.0&lt;/a&gt;)源码学习&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html&#34;&gt;HTTP/1.1&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;第一行&#34;&gt;第一行&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;WEB&lt;/code&gt;服务接收&lt;code&gt;HTTP&lt;/code&gt;请求要做的第一件事，就是解析请求行(&lt;a href=&#34;https://httpwg.org/specs/rfc7230.html#request.line&#34;&gt;Request Line&lt;/a&gt;)。它的格式如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;request-line   = method SP request-target SP HTTP-version CRLF
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;method: &lt;a href=&#34;#%E8%AF%B7%E6%B1%82%E6%96%B9%E6%B3%95&#34;&gt;请求方法&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SP: 空格&lt;/li&gt;
&lt;li&gt;request-target: &lt;a href=&#34;#%E8%AF%B7%E6%B1%82%E7%9B%AE%E6%A0%87&#34;&gt;请求目标&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;HTTP-version: &lt;code&gt;HTTP&lt;/code&gt;版本&lt;/li&gt;
&lt;li&gt;CRLF: 分隔符&lt;code&gt;\r\n&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对应到&lt;code&gt;fasthttp&lt;/code&gt;中的&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L1696-L1727&#34;&gt;代码&lt;/a&gt;是：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;RequestHeader&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;parseFirstLine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;bNext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;
	&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;
	&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bNext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;nextLine&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bNext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
			&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;
		&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

	&lt;span class=&#34;c1&#34;&gt;// parse method
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;IndexByte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Errorf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cannot find http request method in %q&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:]&lt;/span&gt;

	&lt;span class=&#34;c1&#34;&gt;// parse requestURI
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;LastIndexByte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39; &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;noHTTP11&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Errorf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;requestURI cannot be empty in %q&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Equal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:],&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strHTTP11&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;noHTTP11&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;requestURI&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;h&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;requestURI&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;b&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[:&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;bNext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们可以看到&lt;code&gt;fasthttp&lt;/code&gt;先调用&lt;a href=&#34;https://github.com/valyala/fasthttp/blob/v1.16.0/header.go#L2158-L2167&#34;&gt;nextLine&lt;/a&gt;方法，根据分隔符(&lt;code&gt;\r\n&lt;/code&gt;)获取出第一行的所有字节数据&lt;code&gt;b&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;再根据&lt;code&gt;b&lt;/code&gt;中第一个空格字节的位置解析出&lt;code&gt;method&lt;/code&gt;，存入请求头对象中&lt;code&gt;h.method = append(h.method[:0], b[:n]...)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;再根据&lt;code&gt;b&lt;/code&gt;中最后一个空格的位置，解析出&lt;code&gt;request-target&lt;/code&gt;和&lt;code&gt;HTTP-version&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;其中&lt;code&gt;HTTP-version&lt;/code&gt;由&lt;code&gt;h.noHTTP11&lt;/code&gt;保存一个&lt;code&gt;bool&lt;/code&gt;值，不存在&lt;code&gt;HTTP&lt;/code&gt;版本数据或者它不等于&lt;code&gt;HTTP/1.1&lt;/code&gt;时，这个字段都为&lt;code&gt;false&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;而&lt;code&gt;request-target&lt;/code&gt;也是直接保存到请求头对象中&lt;code&gt;h.requestURI = append(h.requestURI[:0], b[:n]...)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;最后返回第一行的字节数，done！&lt;/p&gt;
&lt;h2 id=&#34;请求方法&#34;&gt;请求方法&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The method token indicates the request method to be performed on the target resource. The request method is case-sensitive.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们注意到&lt;code&gt;RFC&lt;/code&gt;中有提到请求&lt;code&gt;method&lt;/code&gt;是大小写敏感的(&lt;code&gt;The request method is case-sensitive&lt;/code&gt;)。&lt;/p&gt;
&lt;p&gt;而且根据上面的解析过程，我们可以看出&lt;code&gt;fasthttp&lt;/code&gt;获取到&lt;code&gt;method&lt;/code&gt;时，直接保存在请求头中，并没有做任何检查。所以当请求端发送了不正确(比如用了小写)的&lt;code&gt;method&lt;/code&gt;值时，服务端可以拒绝这次请求(&lt;code&gt;400 Bad Request&lt;/code&gt;)。&lt;/p&gt;
&lt;p&gt;所有的请求&lt;code&gt;method&lt;/code&gt;可以在&lt;a href=&#34;https://httpwg.org/specs/rfc7231.html#methods&#34;&gt;RFC7231&lt;/a&gt;中查看，之后也会细讲。&lt;/p&gt;
&lt;h2 id=&#34;请求目标&#34;&gt;请求目标&lt;/h2&gt;
&lt;p&gt;客户端发送&lt;code&gt;HTTP&lt;/code&gt;请求消息时，其中包含从目标&lt;code&gt;URI&lt;/code&gt;派生的请求目标。请求目标有四种不同的格式，具体取决于请求的方法以及请求是否针对代理：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;  request-target = origin-form
                 / absolute-form
                 / authority-form
                 / asterisk-form
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;之后的文章会针对请求目标做详细的分析，这里不再赘述。&lt;/p&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;本系列的第一篇文章就这样结束了。我们了解了请求行的结构，并剖析了&lt;code&gt;fasthttp&lt;/code&gt;是如何从数据流中解析这第一行请求数据。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;敬请期待之后的系列文章！&lt;/em&gt; 👋&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>解析Fiber路由管理</title>
      <link>https://kiyonlin.github.io/post/work/gofiber/fiber-router/</link>
      <pubDate>Tue, 23 Jun 2020 13:18:48 +0800</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/gofiber/fiber-router/</guid>
      
        <description>&lt;p&gt;本文主要通过阅读&lt;a href=&#34;https://github.com/gofiber/fiber&#34;&gt;Fiber&lt;/a&gt;源码，解析&lt;code&gt;Fiber&lt;/code&gt;是如何管理路由的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;路由注册&lt;/li&gt;
&lt;li&gt;路由匹配&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;环境：macos 10.15.4 + go 1.14.1 + fiber &lt;a href=&#34;https://github.com/gofiber/fiber/tree/v1.12.1&#34;&gt;1.12.1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;路由注册&#34;&gt;路由注册&lt;/h2&gt;
&lt;h3 id=&#34;路由结构和路由器接口&#34;&gt;路由结构和路由器接口&lt;/h3&gt;
&lt;p&gt;首先看一下路由的结构(&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/router.go#L37-L53&#34;&gt;源码&lt;/a&gt;)：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Route 存放了注册处理器的元数据
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;pos&lt;/span&gt;         &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;         &lt;span class=&#34;c1&#34;&gt;// 路由栈中的位置
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;use&lt;/span&gt;         &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// 匹配路由前缀（中间件）
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;star&lt;/span&gt;        &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// 路由路径为&amp;#39;*&amp;#39;
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt;        &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;        &lt;span class=&#34;c1&#34;&gt;// 路由路径为&amp;#39;/&amp;#39;
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;        &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;      &lt;span class=&#34;c1&#34;&gt;// 美化的路由路径
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;routeParser&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;routeParser&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 路由解析器
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;routeParams&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 大小写敏感的参数 key
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;Name&lt;/span&gt;     &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 路由第一个处理器的名称
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;Path&lt;/span&gt;     &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 原始注册路由路径
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;Method&lt;/span&gt;   &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// HTTP method
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;Handlers&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// 所有处理器
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;以及路由器接口的定义(&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/router.go#L16-L35&#34;&gt;源码&lt;/a&gt;)：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Router&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 注册中间件
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nf&#34;&gt;Use&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;args&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;

	&lt;span class=&#34;nf&#34;&gt;Get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;
	&lt;span class=&#34;nf&#34;&gt;Head&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;
	&lt;span class=&#34;nf&#34;&gt;Post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;
	&lt;span class=&#34;nf&#34;&gt;Put&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;
	&lt;span class=&#34;nf&#34;&gt;Delete&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;
	&lt;span class=&#34;nf&#34;&gt;Connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;
	&lt;span class=&#34;nf&#34;&gt;Options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;
	&lt;span class=&#34;nf&#34;&gt;Trace&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;
	&lt;span class=&#34;nf&#34;&gt;Patch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;

	&lt;span class=&#34;nf&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;
	&lt;span class=&#34;nf&#34;&gt;Static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;config&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;
	&lt;span class=&#34;nf&#34;&gt;All&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 路由组，有处理器时注册成中间件
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nf&#34;&gt;Group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prefix&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Group&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/app.go#L45-L58&#34;&gt;App&lt;/a&gt;和&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/group.go#L12-L16&#34;&gt;Group&lt;/a&gt;结构都实现了&lt;code&gt;Router&lt;/code&gt;接口，它们的&lt;code&gt;Get&lt;/code&gt;、&lt;code&gt;Head&lt;/code&gt;、&lt;code&gt;Post&lt;/code&gt;、&lt;code&gt;Put&lt;/code&gt;、&lt;code&gt;Delete&lt;/code&gt;、&lt;code&gt;Connect&lt;/code&gt;、&lt;code&gt;Options&lt;/code&gt;、&lt;code&gt;Trace&lt;/code&gt;和&lt;code&gt;Patch&lt;/code&gt;方法是对&lt;code&gt;Add&lt;/code&gt;方法的封装。&lt;code&gt;App&lt;/code&gt;的&lt;code&gt;Add&lt;/code&gt;方法其实也是对其&lt;code&gt;register&lt;/code&gt;方法的封装，而&lt;code&gt;Group&lt;/code&gt;的实现只是组装了路由路径，再直接调用&lt;code&gt;App&lt;/code&gt;的相关方法。&lt;/p&gt;
&lt;h3 id=&#34;app的注册方法&#34;&gt;&lt;code&gt;App&lt;/code&gt;的注册方法&lt;/h3&gt;
&lt;p&gt;我们先看&lt;code&gt;func (app *App) register(method, pathRaw string, handlers ...Handler) *Route&lt;/code&gt;方法(&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/router.go#L138-L216&#34;&gt;源码&lt;/a&gt;)：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;App&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pathRaw&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 一些检测工作
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;// pathPretty 会根据 pathRaw 做一些额外的路径处理：大小写转换，清除后缀&amp;#39;/&amp;#39;等
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;pathPretty&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pathRaw&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;// 是不是中间件
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;isUse&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;USE&amp;#34;&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 是否通配符
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;isStar&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pathPretty&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/*&amp;#34;&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 是否根路径
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;isRoot&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pathPretty&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 解析路由路径
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parsedRaw&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;parseRoute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pathRaw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parsedPretty&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;parseRoute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pathPretty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;c1&#34;&gt;// 增加全局的路由位置
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mutex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;routes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;mutex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Unlock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 创建路由元数据
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;pos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;routes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;isUse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;star&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;isStar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;isRoot&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;        &lt;span class=&#34;nx&#34;&gt;pathPretty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;routeParser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parsedPretty&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;routeParams&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parsedRaw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;Path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;     &lt;span class=&#34;nx&#34;&gt;pathRaw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;Method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;   &lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;Handlers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 假如是中间件，则为每种 HTTP methods 栈都追加路由
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;isUse&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;range&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;methodINT&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
			&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;addRoute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
		&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

	&lt;span class=&#34;c1&#34;&gt;// 假如是 GET 方法，则补上 HEAD 方法
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MethodGet&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;addRoute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;MethodHead&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

	&lt;span class=&#34;c1&#34;&gt;// 追加到路由栈
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;addRoute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们稍微分析一下&lt;code&gt;register&lt;/code&gt;做了什么事情：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一些检测工作，&lt;code&gt;method&lt;/code&gt;参数是否合法，&lt;code&gt;handlers&lt;/code&gt;是否为空，补全路由路径等&lt;/li&gt;
&lt;li&gt;创建&lt;code&gt;pathPretty&lt;/code&gt;，它是&lt;code&gt;pathRaw&lt;/code&gt;的副本，然后做一些额外的路径处理：根据配置转换大小写；根据严格模式清除后缀&lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;生成路由元数据，其中解析路由路径这个操作会在&lt;a href=&#34;#%E8%A7%A3%E6%9E%90%E8%B7%AF%E7%94%B1%E8%B7%AF%E5%BE%84&#34;&gt;后面&lt;/a&gt;详细分析&lt;/li&gt;
&lt;li&gt;根据&lt;code&gt;isUse&lt;/code&gt;判断是否为中间件，是的话，给每个&lt;code&gt;HTTP Methods&lt;/code&gt;栈追加路由，并直接返回路由对象&lt;/li&gt;
&lt;li&gt;若&lt;code&gt;method&lt;/code&gt;是&lt;code&gt;GET&lt;/code&gt;方法，则为&lt;code&gt;HEAD&lt;/code&gt;路由栈追加同样的路由&lt;/li&gt;
&lt;li&gt;给指定的&lt;code&gt;method&lt;/code&gt;路由栈追加路由&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样，一个路由注册工作就完成了，非常直接了当。&lt;/p&gt;
&lt;h3 id=&#34;group的add方法&#34;&gt;&lt;code&gt;Group&lt;/code&gt;的&lt;code&gt;Add&lt;/code&gt;方法&lt;/h3&gt;
&lt;p&gt;接下来我们看一下&lt;code&gt;Group&lt;/code&gt;的&lt;code&gt;Add&lt;/code&gt;方法实现：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;grp&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;grp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;register&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getGroupPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;grp&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;handlers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;非常简单，使用&lt;code&gt;getGroupPath&lt;/code&gt;重新组装&lt;code&gt;path&lt;/code&gt;，调用&lt;code&gt;App&lt;/code&gt;的&lt;code&gt;register&lt;/code&gt;方法。&lt;code&gt;getGroupPath&lt;/code&gt;方法的实现也很简单：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getGroupPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;prefix&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 清除 prefix 的&amp;#39;/&amp;#39;后缀，再拼接 path
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;utils&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;TrimRight&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;prefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;sc&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;解析路由路径&#34;&gt;解析路由路径&lt;/h3&gt;
&lt;p&gt;解析路由路径的工作，单独放在了&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/path.go&#34;&gt;path&lt;/a&gt;文件中，我们先来看看核心的&lt;code&gt;routeParser&lt;/code&gt;和&lt;code&gt;paramSeg&lt;/code&gt;结构：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// routeParser 保存了路径 segments 和参数名称
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;routeParser&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;segs&lt;/span&gt;   &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;paramSeg&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;params&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;// paramsSeg 保存了每个段的元数据
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;paramSeg&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;Param&lt;/span&gt;      &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;// 参数名
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;Const&lt;/span&gt;      &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;// 常量字符串
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;IsParam&lt;/span&gt;    &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;		&lt;span class=&#34;c1&#34;&gt;// 是否参数
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;IsOptional&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;		&lt;span class=&#34;c1&#34;&gt;// 是否可选
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;IsLast&lt;/span&gt;     &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;		&lt;span class=&#34;c1&#34;&gt;// 是否最后一个段
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;EndChar&lt;/span&gt;    &lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;		&lt;span class=&#34;c1&#34;&gt;// 终止字符，是&amp;#39;/&amp;#39;， &amp;#39;-&amp;#39;， &amp;#39;.&amp;#39;的其中一个，默认为&amp;#39;/&amp;#39;
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们从上述注册方法中使用的&lt;code&gt;func parseRoute(pattern string) (p routeParser)&lt;/code&gt;方法(&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/path.go#L38-L95&#34;&gt;源码&lt;/a&gt;)开始分析，再以&lt;code&gt;/api/v1/:year-:month.:day/*/:param?&lt;/code&gt;当参数，作为例子讲解：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;找到匹配串&lt;code&gt;/&lt;/code&gt;，这是一个特殊情况，&lt;code&gt;pattern&lt;/code&gt;变为&lt;code&gt;api/v1/:year-:month.:day/*/:param?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;找到匹配串&lt;code&gt;api/&lt;/code&gt;，这是一个常量匹配串，所以生成一个常量&lt;code&gt;segment&lt;/code&gt;，常量&lt;code&gt;Const&lt;/code&gt;字段为&lt;code&gt;api&lt;/code&gt;，终止字符为&lt;code&gt;/&lt;/code&gt;，&lt;code&gt;pattern&lt;/code&gt;变为&lt;code&gt;v1/:year-:month.:day/*/:param?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;找到匹配串&lt;code&gt;v1/&lt;/code&gt;，这是一个常量匹配串，且上一个&lt;code&gt;segment&lt;/code&gt;是常量类型，所以追加&lt;code&gt;/v1&lt;/code&gt;到上一个&lt;code&gt;segment&lt;/code&gt;的&lt;code&gt;Const&lt;/code&gt;字段中，&lt;code&gt;pattern&lt;/code&gt;变为&lt;code&gt;:year-:month.:day/*/:param?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;找到匹配串&lt;code&gt;:year-&lt;/code&gt;，&lt;code&gt;:&lt;/code&gt;开头，所以这个&lt;code&gt;segment&lt;/code&gt;是一个参数，参数名为&lt;code&gt;year&lt;/code&gt;，终止字符为&lt;code&gt;-&lt;/code&gt;，&lt;code&gt;pattern&lt;/code&gt;变为&lt;code&gt;:month.:day/*/:param?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;找到匹配串&lt;code&gt;:month.&lt;/code&gt;，&lt;code&gt;:&lt;/code&gt;开头，所以这个&lt;code&gt;segment&lt;/code&gt;是一个参数，参数名为&lt;code&gt;month&lt;/code&gt;，终止字符为&lt;code&gt;.&lt;/code&gt;，&lt;code&gt;pattern&lt;/code&gt;变为&lt;code&gt;:day/*/:param?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;找到匹配串&lt;code&gt;:day/&lt;/code&gt;，&lt;code&gt;:&lt;/code&gt;开头，所以这个&lt;code&gt;segment&lt;/code&gt;是一个参数，参数名为&lt;code&gt;day&lt;/code&gt;，终止字符为&lt;code&gt;/&lt;/code&gt;，&lt;code&gt;pattern&lt;/code&gt;变为&lt;code&gt;*/:param?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;找到匹配串&lt;code&gt;*/&lt;/code&gt;，&lt;code&gt;*&lt;/code&gt;开头，所以这个&lt;code&gt;segment&lt;/code&gt;是一个参数，而且可选(&lt;code&gt;*&lt;/code&gt;是通配符)，参数名为&lt;code&gt;*&lt;/code&gt;，终止字符为&lt;code&gt;/&lt;/code&gt;，&lt;code&gt;pattern&lt;/code&gt;变为&lt;code&gt;:param?&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;找不到分隔符，剩余的&lt;code&gt;pattern&lt;/code&gt;作为匹配串，即&lt;code&gt;:param?&lt;/code&gt;，&lt;code&gt;:&lt;/code&gt;开头，所以这个&lt;code&gt;segment&lt;/code&gt;是一个参数，&lt;code&gt;?&lt;/code&gt;结尾，所以这个&lt;code&gt;segment&lt;/code&gt;也是可选的，参数名为&lt;code&gt;param&lt;/code&gt;，因为接下来&lt;code&gt;pattern&lt;/code&gt;已经没有了，所以终止字符使用默认值&lt;code&gt;/&lt;/code&gt;，且这个&lt;code&gt;segment&lt;/code&gt;的&lt;code&gt;IsLast&lt;/code&gt;字段标记为&lt;code&gt;true&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最终的&lt;code&gt;routeParser&lt;/code&gt;结果为：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;segs: [
{Param:&amp;#34;&amp;#34; Const:&amp;#34;api/v1&amp;#34; IsParam:false IsOptional:false IsLast:false EndChar:&amp;#39;/&amp;#39;},
{Param:&amp;#34;year&amp;#34; Const:&amp;#34;&amp;#34; IsParam:true IsOptional:false IsLast:false EndChar:&amp;#39;-&amp;#39;},
{Param:&amp;#34;month&amp;#34; Const:&amp;#34;&amp;#34; IsParam:true IsOptional:false IsLast:false EndChar:&amp;#39;.&amp;#39;},
{Param:&amp;#34;day&amp;#34; Const:&amp;#34;&amp;#34; IsParam:true IsOptional:false IsLast:false EndChar:&amp;#39;/&amp;#39;},
{Param:&amp;#34;*&amp;#34; Const&amp;#34;&amp;#34;: IsParam:true IsOptional:true IsLast:false EndChar:&amp;#39;/&amp;#39;},
{Param:&amp;#34;param&amp;#34; Const&amp;#34;&amp;#34;: IsParam:true IsOptional:true IsLast:true EndChar:&amp;#39;/}
]
params:[&amp;#34;year&amp;#34; &amp;#34;month&amp;#34; &amp;#34;day&amp;#34; &amp;#34;*&amp;#34; &amp;#34;param&amp;#34;]
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;小结&#34;&gt;小结&lt;/h3&gt;
&lt;p&gt;路由注册到这里就差不多了，我们可以看到主要的工作就是解析路由路径，保存好元数据，供路由匹配使用。其实还剩下静态资源路由注册&lt;code&gt;registerStatic&lt;/code&gt;没讲，它封装了&lt;code&gt;fasthttp&lt;/code&gt;的相关方法，有兴趣的同学可以自行查看&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/router.go#L218-L320&#34;&gt;源码&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;路由匹配&#34;&gt;路由匹配&lt;/h2&gt;
&lt;p&gt;每当&lt;code&gt;Http&lt;/code&gt;请求到达后，会由&lt;code&gt;fasthttp&lt;/code&gt;解析，&lt;code&gt;Fiber&lt;/code&gt;将&lt;code&gt;rctx *fasthttp.RequestCtx&lt;/code&gt;包装为&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/ctx.go#L33-L46&#34;&gt;Fiber.Ctx&lt;/a&gt;对象，由&lt;code&gt;app.next&lt;/code&gt;处理（详见&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/router.go#L119-L125&#34;&gt;源码&lt;/a&gt;）。没有匹配的路由，则尝试设置&lt;code&gt;Method Not Allowed 405&lt;/code&gt;状态码（详见&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/router.go#L130-L133&#34;&gt;源码&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;app.next&lt;/code&gt;是处理的核心：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;App&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// TODO set unique INT within handler(), not here over and over again
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;methodINT&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 路由栈长度
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;lenr&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;])&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 从上一个索引开始循环路由栈
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;indexRoute&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lenr&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// 增加路由索引，初始为-1
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;indexRoute&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;++&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;][&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;indexRoute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// 检查路由是否匹配请求路径
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;nx&#34;&gt;match&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;match&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pathOriginal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// 未匹配则继续查找
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
			&lt;span class=&#34;k&#34;&gt;continue&lt;/span&gt;
		&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// 设置路由对象和路由路径参数对应的值
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// 执行路由的第一个处理器
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;indexHandler&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Handlers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;](&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// 停止扫描路由栈
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 默认设置 404 状态码
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SendStatus&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;404&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SendString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Cannot &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;method&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pathOriginal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们看看&lt;code&gt;next&lt;/code&gt;做了什么事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;找到请求的&lt;code&gt;method&lt;/code&gt;方法对应的路由栈&lt;/li&gt;
&lt;li&gt;遍历路由栈，检查路由是否匹配请求路径，详细匹配过程见&lt;a href=&#34;#%E5%8C%B9%E9%85%8D%E8%AF%B7%E6%B1%82%E8%B7%AF%E5%BE%84&#34;&gt;下文&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;匹配成功，为&lt;code&gt;ctx&lt;/code&gt;设置路由对象和路由路径参数对应的值&lt;/li&gt;
&lt;li&gt;执行该路由的第一个处理器&lt;/li&gt;
&lt;li&gt;处理器中调用了&lt;code&gt;ctx.Next()&lt;/code&gt;的话，可能&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/ctx.go#L615&#34;&gt;继续&lt;/a&gt;遍历路由栈&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;匹配请求路径&#34;&gt;匹配请求路径&lt;/h3&gt;
&lt;p&gt;接下来让我们瞅瞅路由如何去匹配请求路径：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;match&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;original&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 根路径匹配
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// 通配符匹配
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;star&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;getAllocFreeParams&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;original&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:]&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 路由包含参数时
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;routeParams&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// 匹配参数
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;paramPos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;routeParser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getMatch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;match&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
			&lt;span class=&#34;c1&#34;&gt;// 从源请求路径中解析出参数值
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;			&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;match&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;routeParser&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;paramsForPos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;original&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;paramPos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
		&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 是否中间件
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// 根路由或者路由路径是请求路径的前缀
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;HasPrefix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
			&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt;
		&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
		&lt;span class=&#34;c1&#34;&gt;// 路由路径和请求路径相同
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;len&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// 不匹配
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;路由匹配函数的匹配过程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;匹配根路径&lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;通配符路径&lt;code&gt;/*&lt;/code&gt;，参数&lt;code&gt;*&lt;/code&gt;的值就是源请求路径的去掉首字符之后的字符串&lt;code&gt;original[1:]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;路由包含参数的话，使用路由解析器&lt;a href=&#34;#%E8%B7%AF%E7%94%B1%E5%8F%82%E6%95%B0%E5%8C%B9%E9%85%8D&#34;&gt;匹配&lt;/a&gt;参数位置，匹配时，从源请求路径获取参数对应的值&lt;/li&gt;
&lt;li&gt;中间件路由的情况下，根路由或者路由路径是请求路径的前缀也算匹配，例如&lt;code&gt;/api&lt;/code&gt;匹配&lt;code&gt;/api/v1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;最后检查路径是否相等&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;路由参数匹配&#34;&gt;路由参数匹配&lt;/h3&gt;
&lt;p&gt;带参数的路由匹配&lt;code&gt;func (p *routeParser) getMatch(s string, partialCheck bool) ([][2]int, bool)&lt;/code&gt;方法，是最复杂的部分，我们单独拎出来讲，代码不贴了，详见&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/path.go#L109-L169&#34;&gt;源码&lt;/a&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;根据参数数量，获取预分配内存的参数位置切片&lt;/li&gt;
&lt;li&gt;遍历路由解析器&lt;code&gt;routeParser&lt;/code&gt;中所有的&lt;code&gt;segment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;partLen = len(s)&lt;/code&gt;获取需要匹配的请求路径长度&lt;/li&gt;
&lt;li&gt;先看常量类型的&lt;code&gt;segment&lt;/code&gt;匹配：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;i = len(segment.Const)&lt;/code&gt;获取常量字符串长度&lt;/li&gt;
&lt;li&gt;&lt;code&gt;partLen &amp;lt; i || (i == 0 &amp;amp;&amp;amp; partLen &amp;gt; 0)&lt;/code&gt;先匹配长度&lt;/li&gt;
&lt;li&gt;&lt;code&gt;s[:i] != segment.Const&lt;/code&gt;再匹配常量字符串值&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(partLen &amp;gt; i &amp;amp;&amp;amp; s[i] != segment.EndChar)&lt;/code&gt;最后匹配终止符&lt;/li&gt;
&lt;li&gt;若以上有不匹配的，则直接返回&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;再看参数类型的&lt;code&gt;segment&lt;/code&gt;匹配，目标是计算出参数长度：
&lt;ul&gt;
&lt;li&gt;通配符参数
&lt;ul&gt;
&lt;li&gt;当前&lt;code&gt;segment&lt;/code&gt;是最后一个时，参数长度为剩余请求路径长度&lt;/li&gt;
&lt;li&gt;从右向左贪婪匹配(&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/path.go#L186-L213&#34;&gt;源码&lt;/a&gt;)最长的字符串长度，当作参数长度&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;非通配符参数，根据当前&lt;code&gt;segment&lt;/code&gt;的终止符查找，没找到的话，参数长度为剩余请求路径长度&lt;/li&gt;
&lt;li&gt;&lt;code&gt;segment&lt;/code&gt;不是可选参数且参数长度为0，直接返回&lt;/li&gt;
&lt;li&gt;终止符不匹配，也直接返回&lt;/li&gt;
&lt;li&gt;记录当前&lt;code&gt;segment&lt;/code&gt;参数对应的位置，自增参数位置下标迭代器&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;根据匹配串的长度，调整请求路径切片和下一个参数起始位置&lt;/li&gt;
&lt;li&gt;若请求路径未匹配完全，且不是部分匹配（中间件前缀匹配），则直接返回&lt;/li&gt;
&lt;li&gt;成功返回参数切片位置和匹配成功标识&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;取得参数位置后，我们就可以在源请求路径中截取出参数对应的值(&lt;a href=&#34;https://github.com/gofiber/fiber/blob/v1.12.1/path.go#L171-L184&#34;&gt;源码&lt;/a&gt;)。&lt;/p&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Fiber&lt;/code&gt;的路由管理使用了很简单的数据结构——&lt;code&gt;slice&lt;/code&gt;，每种&lt;code&gt;Http method&lt;/code&gt;对应一组路由栈，而不是像&lt;a href=&#34;https://github.com/gin-gonic/gin&#34;&gt;gin&lt;/a&gt;一样使用&lt;a href=&#34;https://en.wikipedia.org/wiki/Radix_tree&#34;&gt;基数树&lt;/a&gt;这种数据结构，详细原因见&lt;a href=&#34;#%E5%85%B6%E4%BB%96&#34;&gt;文末&lt;/a&gt;。最后作一下总结：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;注册路由时，分析路径，将其分段，保存元数据到路由栈中&lt;/li&gt;
&lt;li&gt;匹配路由时，逐个匹配请求&lt;code&gt;method&lt;/code&gt;对应的路由栈，匹配时执行相应的处理器&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;其他&#34;&gt;其他&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Fiber&lt;/code&gt;作者&lt;a href=&#34;https://github.com/Fenny&#34;&gt;Fenny&lt;/a&gt;关于为什么不使用&lt;a href=&#34;https://en.wikipedia.org/wiki/Radix_tree&#34;&gt;基数树&lt;/a&gt;管理路由的回答：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We discussed using a radix tree, but we would lose the stack order flexibility (like express has).&lt;/p&gt;
&lt;p&gt;Another thing I would like to mention which is misconception by many, the performance of decent routers will not impact your application.&lt;/p&gt;
&lt;p&gt;When you have 10,000 routes, you still talking about nano seconds. If you have a database query in one of your handlers, the performance of the router would not matter anymore.&lt;/p&gt;
&lt;p&gt;However, we made sure our router is 💯 regarding allocation and performance. And &lt;a href=&#34;https://github.com/ReneWerner87&#34;&gt;@René&lt;/a&gt; did an awesome job to replicate most key features of the expressjs path behaviour.&lt;/p&gt;
&lt;p&gt;Which makes routes like /api/*/:param/:param2 ~&amp;gt; /api/joker/batman/robin/2/1 possible, only in Fiber.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
      
    </item>
    
    <item>
      <title>基于延迟计算令牌桶的gofiber频率限制中间件实现</title>
      <link>https://kiyonlin.github.io/post/work/gofiber/fiber-limiter/</link>
      <pubDate>Sat, 20 Jun 2020 10:53:18 +0800</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/gofiber/fiber-limiter/</guid>
      
        <description>&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;
&lt;p&gt;后端的&lt;code&gt;api&lt;/code&gt;接口一般都需要限制访问频率，一般的实现算法有&lt;a href=&#34;https://en.wikipedia.org/wiki/Token_bucket&#34;&gt;令牌桶&lt;/a&gt;，&lt;a href=&#34;https://en.wikipedia.org/wiki/Leaky_bucket&#34;&gt;漏桶&lt;/a&gt;等等。其中令牌桶支持突发流量，更合适访问流量整形。&lt;/p&gt;
&lt;p&gt;关于令牌桶算法这里不再赘述，目前有两个&lt;code&gt;golang&lt;/code&gt;限流器库，&lt;a href=&#34;https://github.com/juju/ratelimit&#34;&gt;github.com/juju/ratelimit&lt;/a&gt;和&lt;a href=&#34;https://github.com/golang/time&#34;&gt;golang.org/x/time/rate&lt;/a&gt;，采用了延迟计算的方式实现令牌桶算法。本文主要是基于&lt;a href=&#34;https://github.com/golang/time&#34;&gt;golang.org/x/time/rate&lt;/a&gt;的限流器进行实现，有两篇相关的文章《&lt;a href=&#34;https://zhuanlan.zhihu.com/p/89820414&#34;&gt;Golang限流器time/rate使用介绍&lt;/a&gt;》和《&lt;a href=&#34;https://zhuanlan.zhihu.com/p/90206074&#34;&gt;Golang限流器time/rate实现剖析&lt;/a&gt;》，有兴趣的同学可以查看一下。&lt;/p&gt;
&lt;h2 id=&#34;限流响应头&#34;&gt;限流响应头&lt;/h2&gt;
&lt;p&gt;限流响应头主要涉及四个字段：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;未超出频率限制时：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;X-RateLimit-Limit&lt;/code&gt;：请求限制总量，对应了令牌桶算法中的突发值&lt;code&gt;Burst&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X-RateLimit-Remaining&lt;/code&gt;：目前还可以请求的次数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X-RateLimit-Reset&lt;/code&gt;：多少秒才能恢复到满桶的状态&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;超出频率限制时：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Retry-After&lt;/code&gt;：多少秒之后可以重试&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/golang/time&#34;&gt;golang.org/x/time/rate&lt;/a&gt;的&lt;a href=&#34;https://github.com/golang/time/blob/master/rate/rate.go#L192-L193&#34;&gt;Reserve&lt;/a&gt;方法返回一个&lt;code&gt;*Reservation&lt;/code&gt;对象，根据它的&lt;a href=&#34;https://github.com/golang/time/blob/master/rate/rate.go#L121-L122&#34;&gt;Delay&lt;/a&gt;方法，我们可以知道本次请求是否超出了频率限制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;delay&lt;/code&gt;大于0，表示需要等待，即超出了频率限制。此时，我们根据&lt;code&gt;delay&lt;/code&gt;转换成秒数(至少一秒)即可。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;delay&lt;/code&gt;等于0，表示不需要等待，即未超出频率限制。此时，我们除了&lt;code&gt;Burst&lt;/code&gt;，无法获取更多的有效信息。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;为reservation增加信息&#34;&gt;为&lt;code&gt;Reservation&lt;/code&gt;增加信息&lt;/h2&gt;
&lt;p&gt;因为&lt;code&gt;Reservation&lt;/code&gt;缺少可以转化为&lt;code&gt;X-RateLimit-Remaining&lt;/code&gt;和&lt;code&gt;X-RateLimit-Reset&lt;/code&gt;的信息，我们需要在调用&lt;code&gt;Reserve&lt;/code&gt;时，保存一些相关数据(&lt;a href=&#34;https://github.com/kiyonlin/rate/blob/master/rate.go#L366-L369&#34;&gt;源码位置&lt;/a&gt;)：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tokens&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;timeToAct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;now&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;waitDuration&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// store remaining tokens as integer
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// 1e-9 used to solve the problem of missing precision
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;remainedTokens&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;math&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Floor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tokens&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1e-9&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;reset&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;limit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;durationFromTokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;float64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;burst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;tokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们先看&lt;code&gt;r.remainedTokens = int(math.Floor(tokens + 1e-9))&lt;/code&gt;，它保存了调用&lt;code&gt;Reserve&lt;/code&gt;时，&lt;code&gt;limiter&lt;/code&gt;剩余的整数&lt;code&gt;token&lt;/code&gt;值。因为&lt;code&gt;tokens&lt;/code&gt;是&lt;code&gt;float64&lt;/code&gt;类型，会丢失一点精度，我们需要补全精度后，转为&lt;code&gt;int&lt;/code&gt;类型。&lt;/p&gt;
&lt;p&gt;再看&lt;code&gt;r.reset = r.limit.durationFromTokens(float64(r.lim.burst) - tokens)&lt;/code&gt;，&lt;code&gt;float64(r.lim.burst) - tokens&lt;/code&gt;是已经消耗掉的&lt;code&gt;token&lt;/code&gt;数量，我们根据这个数量，转换为时长，即恢复这么多&lt;code&gt;token&lt;/code&gt;还需要多长时间。&lt;/p&gt;
&lt;h2 id=&#34;实现fiber频率限制中间件&#34;&gt;实现&lt;code&gt;fiber&lt;/code&gt;频率限制中间件&lt;/h2&gt;
&lt;h3 id=&#34;config&#34;&gt;Config&lt;/h3&gt;
&lt;p&gt;根据&lt;a href=&#34;https://gofiber.io&#34;&gt;fiber&lt;/a&gt;中间件的实现规则，我们先创建一个配置结构：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Config&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// Filter 定义了是否跳过中间件的方法，默认是nil
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;Filter&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fiber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;bool&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// Limit 定义了请求频率的最大值，表示每秒Limit个请求，默认值是 10
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;Limit&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// Burst 是最大突发值，默认值是 10
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;Burst&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// Message 响应消息，默认值是 &amp;#34;Too many requests, please try again later.&amp;#34;
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;Message&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// StatusCode 状态码，默认值是 429
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;StatusCode&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// Key 允许用户使用自定义handler生成自定义的 key，默认值是 
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;// func(c *fiber.Ctx) string {
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;//   return c.IP()
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;Key&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fiber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// Handler 触发频率限制时调用的 handler， 默认值是
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;// func(c *fiber.Ctx) {
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;//   c.Status(cfg.StatusCode).Format(cfg.Message)
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;// }
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fiber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;利用这些配置，用户可以根据自己的需求使用中间件。&lt;/p&gt;
&lt;h3 id=&#34;new&#34;&gt;New&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;func New(config ...Config) func(*fiber.Ctx)&lt;/code&gt;是中间件的工厂函数，根据用户传入的配置，返回一个&lt;code&gt;fiber&lt;/code&gt;中间件。&lt;/p&gt;
&lt;p&gt;我们需要缓存下所有的限制器对象，存放在&lt;code&gt;limiters&lt;/code&gt;变量中，并用&lt;code&gt;mu&lt;/code&gt;控制并发访问：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;limiters&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;make&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Limiter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;mu&lt;/span&gt;       &lt;span class=&#34;nx&#34;&gt;sync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Mutex&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;当&lt;code&gt;api&lt;/code&gt;接口收到请求时，获取限制器的&lt;code&gt;key&lt;/code&gt;，再根据&lt;code&gt;key&lt;/code&gt;获取限制器，没找到的话根据配置为&lt;code&gt;key&lt;/code&gt;新建一个限制器：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cfg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;nx&#34;&gt;mu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;lim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;limiters&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ok&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// Get a default limiter
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;lim&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;rate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;NewLimiter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;rate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Limit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;cfg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Limit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;cfg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Burst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;limiters&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lim&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;mu&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Unlock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;然后，我们调用&lt;code&gt;lim.Reserve&lt;/code&gt;获取一个&lt;code&gt;*Reservation&lt;/code&gt;对象，根据其&lt;code&gt;Delay()&lt;/code&gt;返回值确定有没有超出频率限制：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Try to request
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Reserve&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;// Check reservation&amp;#39;s delay
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Delay&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;cfg&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// Return response with Retry-After header
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// https://tools.ietf.org/html/rfc7231#section-7.1.3
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// Set second value(at least one) to Retry-After header
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fiber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;HeaderRetryAfter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strconv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;FormatInt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;int64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;math&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Ceil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Seconds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())),&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;// We can continue, update RateLimit headers
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;X-RateLimit-Limit&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strconv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Itoa&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lim&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Burst&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()))&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;X-RateLimit-Remaining&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strconv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Itoa&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;RemainedTokens&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()))&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Set&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;X-RateLimit-Reset&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strconv&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;FormatInt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;int64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;math&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Ceil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Reset&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Seconds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())),&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;// Bye!
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;其中&lt;code&gt;X-RateLimit-Reset&lt;/code&gt;和&lt;code&gt;Retry-After&lt;/code&gt;的值都转成秒为单位的整数，这是一个相对当前调用时间的值，客户端可以根据这个值作出相应的操作。&lt;/p&gt;
&lt;h3 id=&#34;set&#34;&gt;Set&lt;/h3&gt;
&lt;p&gt;最后，我们提供&lt;code&gt;func Set(key string, lim *rate.Limiter)&lt;/code&gt;方法，用户可以根据自己的需求提前设置不同的限制器。比如针对不同的&lt;code&gt;api&lt;/code&gt;用户，设置不同的访问频率以及突发值。&lt;/p&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;本文主要针对&lt;code&gt;api&lt;/code&gt;接口的频率限制需求，在&lt;a href=&#34;https://github.com/golang/time&#34;&gt;golang.org/x/time/rate&lt;/a&gt;的基础上为&lt;code&gt;Reservation&lt;/code&gt;对象增加方法，完善响应头中的信息，并应用到&lt;a href=&#34;https://gofiber.io&#34;&gt;fiber&lt;/a&gt;框架中。&lt;/p&gt;
&lt;p&gt;最后，我们可以发现基于令牌桶延迟计算实现频率限制的好处：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不需要定时器&lt;/li&gt;
&lt;li&gt;不需要后台&lt;code&gt;goroutine&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;不需要队列&lt;/li&gt;
&lt;li&gt;支持突发流量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;仓库资源：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基于&lt;a href=&#34;https://github.com/golang/time&#34;&gt;golang.org/x/time/rate&lt;/a&gt;的&lt;a href=&#34;https://github.com/kiyonlin/rate&#34;&gt;rate&lt;/a&gt;包&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/kiyonlin/fiber_limiter&#34;&gt;fiber limiter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>fiber&#43;angular开发web</title>
      <link>https://kiyonlin.github.io/post/work/gofiber/fiblar-demo/</link>
      <pubDate>Wed, 17 Jun 2020 13:57:18 +0800</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/gofiber/fiblar-demo/</guid>
      
        <description>&lt;p&gt;&lt;a href=&#34;https://docs.gofiber.io/&#34;&gt;fiber&lt;/a&gt;是最近出道的一款基于&lt;a href=&#34;https://github.com/valyala/fasthttp&#34;&gt;fasthttp&lt;/a&gt;的&lt;code&gt;web&lt;/code&gt;框架。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Fiber&lt;/em&gt; is an Express inspired &lt;em&gt;web framework&lt;/em&gt; build on top of Fasthttp, the &lt;em&gt;fastest&lt;/em&gt; HTTP engine for Go. Designed to &lt;em&gt;ease&lt;/em&gt; things up for &lt;em&gt;fast&lt;/em&gt; development with &lt;em&gt;zero memory allocation&lt;/em&gt; and &lt;em&gt;performance&lt;/em&gt; in mind.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们可以用&lt;code&gt;fiber&lt;/code&gt;快速开发&lt;code&gt;api&lt;/code&gt;接口，再配合&lt;code&gt;angular&lt;/code&gt;实现一个&lt;code&gt;web&lt;/code&gt;项目。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;环境：macos 10.15.4 + go 1.14.1 + fiber 1.11.1 + angular 9.1.6&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;新建项目&#34;&gt;新建项目&lt;/h2&gt;
&lt;p&gt;我们创建一个新的目录叫&lt;code&gt;fiblar-demo&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;md fiblar-demo &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; fiblar-demo
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;再使用&lt;code&gt;go mod&lt;/code&gt;初始化项目：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;go mod init github.com/kiyonlin/fiblar-demo
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;新建一个&lt;code&gt;main.go&lt;/code&gt;文件，内容如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kn&#34;&gt;package&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;main&lt;/span&gt;

&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
	&lt;span class=&#34;s&#34;&gt;&amp;#34;github.com/gofiber/fiber&amp;#34;&lt;/span&gt;
	&lt;span class=&#34;s&#34;&gt;&amp;#34;log&amp;#34;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;c1&#34;&gt;// Create new Fiber instance
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fiber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;

	&lt;span class=&#34;c1&#34;&gt;// serve Single Page application on &amp;#34;/public&amp;#34;
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;c1&#34;&gt;// assume static file at dist folder
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Static&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;public&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

	&lt;span class=&#34;c1&#34;&gt;// intercept api routes
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;apiGroup&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/api&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;apiGroup&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/user&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fiber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
			&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;JSON&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fiber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;kiyon&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
		&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

	&lt;span class=&#34;c1&#34;&gt;// other routes just return `public/index.html`, angular will handle them
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;/*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fiber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SendFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;public/index.html&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
			&lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fiber&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ErrInternalServerError&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
		&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;

	&lt;span class=&#34;c1&#34;&gt;// Start server on http://localhost:3000
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;	&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Listen&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;创建新的angular项目&#34;&gt;创建新的&lt;code&gt;angular&lt;/code&gt;项目&lt;/h2&gt;
&lt;p&gt;使用&lt;a href=&#34;https://angular.cn/cli&#34;&gt;angular-cli&lt;/a&gt;创建一个新的&lt;code&gt;angular&lt;/code&gt;项目：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ng new web
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;编辑&lt;code&gt;web/angular.json&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nt&#34;&gt;&amp;#34;other-configs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{},&lt;/span&gt;
  &lt;span class=&#34;nt&#34;&gt;&amp;#34;build&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nt&#34;&gt;&amp;#34;builder&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;@angular-devkit/build-angular:browser&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nt&#34;&gt;&amp;#34;options&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;nt&#34;&gt;&amp;#34;outputPath&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;../public&amp;#34;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这样就可以把编译后的&lt;code&gt;angular&lt;/code&gt;项目输出到跟&lt;code&gt;web&lt;/code&gt;同一级的&lt;code&gt;public&lt;/code&gt;目录，上述基于&lt;code&gt;fiber&lt;/code&gt;的程序会读取&lt;code&gt;public&lt;/code&gt;目录下的文件。&lt;/p&gt;
&lt;p&gt;接下来我们给&lt;code&gt;web/src/app/app.component.ts&lt;/code&gt;加一下&lt;code&gt;api&lt;/code&gt;请求：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// ...
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AppComponent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;web&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;any&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;HttpClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/api/user&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这样进入首页后就会触发&lt;code&gt;/api/user&lt;/code&gt;的请求，我们把获取到的&lt;code&gt;user&lt;/code&gt;展示到页面&lt;code&gt;web/src/app/app.component.html&lt;/code&gt;上：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;  &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- Resources --&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;h1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;User&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;h1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;{{user | json}}&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;h2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Resources&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;h2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Here are some links to help you get started:&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;c&#34;&gt;&amp;lt;!-- ... --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;再在&lt;code&gt;web/package.json&lt;/code&gt;中添加一个脚本，供开发时使用：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nt&#34;&gt;&amp;#34;scripts&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nt&#34;&gt;&amp;#34;build&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;ng build --prod&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nt&#34;&gt;&amp;#34;build:watch&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;ng build --watch&amp;#34;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;运行项目&#34;&gt;运行项目&lt;/h2&gt;
&lt;p&gt;我们使用&lt;a href=&#34;https://github.com/cosmtrek/air&#34;&gt;air&lt;/a&gt;在开发过程中热更新服务，按照文档安装即可，以下是&lt;code&gt;.air.conf&lt;/code&gt;的核心内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;nx&#34;&gt;root&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;.&amp;#34;&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;tmp_dir&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tmp&amp;#34;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;cmd&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;go build -o ./tmp/main ./main.go&amp;#34;&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;bin&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tmp/main&amp;#34;&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;include_ext&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;go&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;toml&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;exclude_dir&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;assets&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;tmp&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;vendor&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;web&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;最后，我们执行&lt;code&gt;air&lt;/code&gt;监听后端项目，执行&lt;code&gt;cd web &amp;amp;&amp;amp; yarn run build:watch&lt;/code&gt;监听前端项目，访问&lt;code&gt;localhost:3000&lt;/code&gt;查看结果：
&lt;img src=&#34;https://kiyonlin.github.io/image/gofiber/fiblar-demo.png&#34; alt=&#34;demo&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;总结&#34;&gt;总结&lt;/h2&gt;
&lt;p&gt;本文展示了综合&lt;code&gt;fiber&lt;/code&gt;和&lt;code&gt;angular&lt;/code&gt;开发&lt;code&gt;web&lt;/code&gt;项目的一个小例子，权当抛砖引玉，有兴趣的同学可以继续深入研究。最后，我们稍微做下总结：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;通过&lt;code&gt;fiber&lt;/code&gt;的静态文件服务，我们可以直接提供&lt;code&gt;angular&lt;/code&gt;输出的&lt;code&gt;html&lt;/code&gt;页面&lt;/li&gt;
&lt;li&gt;服务端拦截&lt;code&gt;api&lt;/code&gt;路由，前端调用接口时，加上&lt;code&gt;api&lt;/code&gt;前缀即可，无跨域问题，其他路由交给&lt;code&gt;angular&lt;/code&gt;处理&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;完整&lt;a href=&#34;http://github.com/kiyonlin/fiblar-demo&#34;&gt;demo&lt;/a&gt;，以上。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>(译)Angular Core 01 - Angular Templates 完全指南</title>
      <link>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angularcore01-angulartemplates%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97/</link>
      <pubDate>Wed, 24 Oct 2018 08:25:08 +0000</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angularcore01-angulartemplates%E5%AE%8C%E6%95%B4%E6%8C%87%E5%8D%97/</guid>
      
        <description>&lt;p&gt;在这篇文章(&lt;a href=&#34;https://blog.angular-university.io/angular-ng-template-ng-container-ngtemplateoutlet/&#34;&gt;原文链接&lt;/a&gt;)中，我们将深入探讨&lt;code&gt;Angular Core&lt;/code&gt;的一些高级功能！&lt;/p&gt;
&lt;p&gt;您可能已经遇到过&lt;code&gt;ng-template&lt;/code&gt;这个&lt;code&gt;Angular&lt;/code&gt;核心指令，例如在使用&lt;code&gt;ngIf/else&lt;/code&gt;或&lt;code&gt;ngSwitch&lt;/code&gt;时。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ng-template&lt;/code&gt;指令和相关的&lt;code&gt;ngTemplateOutlet&lt;/code&gt;指令是非常强大的&lt;code&gt;Angular&lt;/code&gt;功能，支持各种高级用例。&lt;/p&gt;
&lt;p&gt;这些指令经常与&lt;code&gt;ng-container&lt;/code&gt;一起使用，因为这些指令被设计为一起使用，所以如果我们一次性学习它们将会有所帮助，因为我们将围绕每个指令有更多的上下文。&lt;/p&gt;
&lt;p&gt;然后让我们看看这些指令启用的一些更高级的用例。 注意：本文的所有代码都可以在此&lt;a href=&#34;https://github.com/angular-university/ng-template-example&#34;&gt;Github repository&lt;/a&gt;中找到。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;ng-template指令简介&#34;&gt;&lt;code&gt;ng-template&lt;/code&gt;指令简介&lt;/h2&gt;
&lt;p&gt;与名称一样，&lt;code&gt;ng-template&lt;/code&gt;指令表示&lt;code&gt;Angular&lt;/code&gt;模板：这意味着此标记的内容将包含模板的一部分，然后可以与其他模板一起组合以形成最终的组件模板。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Angular&lt;/code&gt;已经在我们一直使用的许多结构指令中使用了&lt;code&gt;ng-template&lt;/code&gt;：&lt;code&gt;ngIf&lt;/code&gt;，&lt;code&gt;ngFor&lt;/code&gt;和&lt;code&gt;ngSwitch&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;让我们开始学习&lt;code&gt;ng-template&lt;/code&gt;并举例说明。 这里我们定义一个选项卡组件的两个选项卡按钮（稍后会详细介绍）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;app-root&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`      
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;      &amp;lt;ng-template&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          &amp;lt;button class=&amp;#34;tab-button&amp;#34; 
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;                  (click)=&amp;#34;login()&amp;#34;&amp;gt;{{loginText}}&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          &amp;lt;button class=&amp;#34;tab-button&amp;#34; 
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;                  (click)=&amp;#34;signUp()&amp;#34;&amp;gt;{{signUpText}}&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;      &amp;lt;/ng-template&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;  `&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AppComponent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;loginText&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Login&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;signUpText&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Sign Up&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt; 
    &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Lesson 1&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Lessons 2&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;login() {&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Login&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;signUp() {&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Sign Up&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;你会注意到关于ng-template的第一件事&#34;&gt;你会注意到关于&lt;code&gt;ng-template&lt;/code&gt;的第一件事&lt;/h2&gt;
&lt;p&gt;如果您尝试上面的示例，您可能会惊讶地发现此示例不会向屏幕&lt;em&gt;呈现任何内容&lt;/em&gt;！&lt;/p&gt;
&lt;p&gt;这是正常的，这是预期的行为。 这是因为使用&lt;code&gt;ng-template&lt;/code&gt;标签我们只是定义一个模板，但我们还没有使用它。&lt;/p&gt;
&lt;p&gt;然后让我们找一个示例，我们可以使用一些最常用的&lt;code&gt;Angular&lt;/code&gt;指令来渲染输出。&lt;/p&gt;
&lt;h3 id=&#34;ng-template指令和ngif&#34;&gt;&lt;code&gt;ng-template&lt;/code&gt;指令和&lt;code&gt;ngIf&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;您可能在实现&lt;code&gt;if/else&lt;/code&gt;方案时第一次遇到&lt;code&gt;ng-template&lt;/code&gt;，例如这个：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lessons-list&amp;#34;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngIf&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lessons else loading&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  ... 
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ng-template&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;loading&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ng-template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这是&lt;code&gt;ngIf/else&lt;/code&gt;功能的一种非常常见的用法：我们在等待数据从后端到达时显示备用&lt;code&gt;loading&lt;/code&gt;模板。&lt;/p&gt;
&lt;p&gt;我们可以看到，&lt;code&gt;else&lt;/code&gt;子句指向一个名称为&lt;code&gt;loading&lt;/code&gt;的模板。 使用&lt;code&gt;#loading&lt;/code&gt;语法通过模板引用为其分配了名称。&lt;/p&gt;
&lt;p&gt;但除了那个模板之外，使用&lt;code&gt;ngIf&lt;/code&gt;还会创建第二个隐式&lt;code&gt;ng&lt;/code&gt;模板！ 让我们来看看幕后发生的事情：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ng-template&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngIf&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;]=&amp;#34;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngIfElse&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;]=&amp;#34;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;loading&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
   &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lessons-list&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
     ... 
   &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ng-template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ng-template&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;loading&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ng-template&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这就是内部发生的事情，因为&lt;code&gt;Angular&lt;/code&gt;解析了简洁的&lt;code&gt;*ngIf&lt;/code&gt;结构指令语法糖。 让我们分解一下语法糖解析期间发生了什么：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;应用结构指令&lt;code&gt;ngIf&lt;/code&gt;的元素已移至&lt;code&gt;ng-template&lt;/code&gt;中&lt;/li&gt;
&lt;li&gt;使用&lt;code&gt;[ngIf]&lt;/code&gt;和&lt;code&gt;[ngIfElse]&lt;/code&gt;模板输入变量语法将&lt;code&gt;*ngIf&lt;/code&gt;的表达式拆分并应用于两个单独的指令&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这只是&lt;code&gt;ngIf&lt;/code&gt;特定情况的一个例子。 但是使用&lt;code&gt;ngFor&lt;/code&gt;和&lt;code&gt;ngSwitch&lt;/code&gt;也会发生类似的过程。&lt;/p&gt;
&lt;p&gt;这些指令都是非常常用的，因此这意味着这些模板在&lt;code&gt;Angular&lt;/code&gt;中无处不在，无论是隐式还是显式。&lt;/p&gt;
&lt;p&gt;但基于这个例子，可能会想到一个问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果有多个结构指令应用于同一个元素，这是如何工作的？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;多种结构指令&#34;&gt;多种结构指令&lt;/h2&gt;
&lt;p&gt;让我们看看如果我们尝试在同一元素中使用&lt;code&gt;ngIf&lt;/code&gt;和&lt;code&gt;ngFor&lt;/code&gt;会发生什么：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lesson&amp;#34;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngIf&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lessons&amp;#34;&lt;/span&gt; 
       &lt;span class=&#34;err&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngFor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;let lesson of lessons&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lesson-detail&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
        {{lesson | json}}
    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这行不通！ 相反，我们会收到以下错误消息：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;Uncaught Error: Template parse errors:
Can&lt;span class=&#34;s1&#34;&gt;&amp;#39;t have multiple template bindings on one element. Use only one attribute 
&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;named &amp;#39;&lt;/span&gt;template&lt;span class=&#34;err&#34;&gt;&amp;#39;&lt;/span&gt; or prefixed with *
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这意味着它不可能将两个结构指令应用于同一个元素。 为了做到这一点，我们必须做类似的事情：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngIf&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lessons&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lesson&amp;#34;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngFor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;let lesson of lessons&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lesson-detail&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
            {{lesson | json}}
        &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在这个例子中，我们已经将&lt;code&gt;ngIf&lt;/code&gt;指令移动到外部包装&lt;code&gt;div&lt;/code&gt;，但为了使其工作，我们必须创建额外的&lt;code&gt;div&lt;/code&gt;元素。&lt;/p&gt;
&lt;p&gt;这个解决方案已经可以工作了，但有没有办法将结构指令应用到页面的一部分而不必创建额外的元素？&lt;/p&gt;
&lt;p&gt;这正是&lt;code&gt;ng-container&lt;/code&gt;结构指令允许我们做的！&lt;/p&gt;
&lt;h2 id=&#34;ng-container指令&#34;&gt;&lt;code&gt;ng-container&lt;/code&gt;指令&lt;/h2&gt;
&lt;p&gt;为了避免创建额外的&lt;code&gt;div&lt;/code&gt;，我们可以改为使用&lt;code&gt;ng-container&lt;/code&gt;指令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ng-container&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngIf&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lessons&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lesson&amp;#34;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngFor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;let lesson of lessons&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;lesson-detail&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
            {{lesson | json}}
        &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ng-container&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;正如我们所看到的，&lt;code&gt;ng-container&lt;/code&gt;指令为我们提供了一个元素，我们可以将结构指令附加到页面的一部分，而不必为此创建额外的元素。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ng-container&lt;/code&gt;指令还有另一个主要用途：它还可以提供一个占位符，用于动态地将模板注入页面。&lt;/p&gt;
&lt;h2 id=&#34;使用ngtemplateoutlet指令创建动态模板&#34;&gt;使用&lt;code&gt;ngTemplateOutlet&lt;/code&gt;指令创建动态模板&lt;/h2&gt;
&lt;p&gt;能够创建模板引用并将它们指向其他指令（例如&lt;code&gt;ngIf&lt;/code&gt;）只是一个开始。&lt;/p&gt;
&lt;p&gt;我们也可以使用模板本身并使用&lt;code&gt;ngTemplateOutlet&lt;/code&gt;指令在页面的任何位置实例化它：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ng-container&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngTemplateOutlet&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;loading&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ng-container&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们可以在这里看到&lt;code&gt;ng-container&lt;/code&gt;如何帮助这个例子：我们使用它来在页面上实例化我们在上面定义的加载模板。&lt;/p&gt;
&lt;p&gt;我们通过其模板引用&lt;code&gt;#loading&lt;/code&gt;引用加载模板，我们使用&lt;code&gt;ngTemplateOutlet&lt;/code&gt;结构指令来实例化模板。&lt;/p&gt;
&lt;p&gt;我们可以根据需要向页面添加尽可能多的&lt;code&gt;ngTemplateOutlet&lt;/code&gt;标记，并实例化许多不同的模板。 传递给该指令的值可以是任何计算模板引用的表达式，稍后将详细介绍。&lt;/p&gt;
&lt;p&gt;现在我们知道了如何实例化模板，让我们来谈谈模板可以访问的内容。&lt;/p&gt;
&lt;h2 id=&#34;模板上下文&#34;&gt;模板上下文&lt;/h2&gt;
&lt;p&gt;关于模板的一个关键问题是，它们内部可见什么？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;模板是否有自己独立的变量范围，模板可以看到哪些变量？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在&lt;code&gt;ng-template&lt;/code&gt;标签主体内部，我们可以访问外部模板中可见的相同上下文变量，例如&lt;code&gt;lessons&lt;/code&gt;变量。&lt;/p&gt;
&lt;p&gt;这是因为所有&lt;code&gt;ng-template&lt;/code&gt;实例也可以访问嵌入它们的相同上下文。&lt;/p&gt;
&lt;p&gt;但是每个模板也可以定义自己的输入变量集！ 实际上，每个模板都关联一个包含所有模板特定输入变量的上下文对象。&lt;/p&gt;
&lt;p&gt;我们来看一个例子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;app-root&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`      
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;ng-template #estimateTemplate let-lessonsCounter=&amp;#34;estimate&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;div&amp;gt; Approximately {{lessonsCounter}} lessons ...&amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;/ng-template&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;ng-container 
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;   *ngTemplateOutlet=&amp;#34;estimateTemplate;context:ctx&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;/ng-container&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AppComponent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;totalEstimate&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;estimate&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;this.totalEstimate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
  
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;以下是此示例的解释：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这个模板，与以前的模板不同，还有一个输入变量（也可能有几个）&lt;/li&gt;
&lt;li&gt;输入变量名为&lt;code&gt;lessonsCounter&lt;/code&gt;，它是通过&lt;code&gt;ng-template&lt;/code&gt;属性使用前缀&lt;code&gt;let-&lt;/code&gt;定义的&lt;/li&gt;
&lt;li&gt;变量&lt;code&gt;lessonsCounter&lt;/code&gt;在&lt;code&gt;ng-template&lt;/code&gt;主体内部可见，但外部不可见&lt;/li&gt;
&lt;li&gt;此变量的内容由其分配给属性&lt;code&gt;let-lessonsCounter&lt;/code&gt;的表达式确定&lt;/li&gt;
&lt;li&gt;该表达式针对上下文对象进行评估，与模板一起传递给&lt;code&gt;ngTemplateOutlet&lt;/code&gt;以进行实例化&lt;/li&gt;
&lt;li&gt;然后，对于要在模板内显示的任何值，此上下文对象必须具有名为&lt;code&gt;estimate&lt;/code&gt;的属性&lt;/li&gt;
&lt;li&gt;&lt;code&gt;context&lt;/code&gt;对象通过&lt;code&gt;context&lt;/code&gt;属性传递给&lt;code&gt;ngTemplateOutlet&lt;/code&gt;，该属性可以接收任何求值为&lt;em&gt;对象&lt;/em&gt;的表达式&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;鉴于上面的示例，这将呈现给屏幕：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;Approximately 10 lessons ...
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这为我们提供了如何定义和实例化我们自己的模板的良好概述。&lt;/p&gt;
&lt;p&gt;我们还可以做的另一件事是在组件本身的层次上以编程方式与模板交互：让我们看看我们如何做到这一点。&lt;/p&gt;
&lt;h2 id=&#34;template-references&#34;&gt;Template References&lt;/h2&gt;
&lt;p&gt;与我们使用模板引用引用加载模板的方式相同，我们也可以使用&lt;code&gt;ViewChild&lt;/code&gt;装饰器将模板直接注入到我们的组件中：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;app-root&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`      
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;      &amp;lt;ng-template #defaultTabButtons&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          &amp;lt;button class=&amp;#34;tab-button&amp;#34; (click)=&amp;#34;login()&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;            {{loginText}}
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          &amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          &amp;lt;button class=&amp;#34;tab-button&amp;#34; (click)=&amp;#34;signUp()&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;            {{signUpText}}
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          &amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;      &amp;lt;/ng-template&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AppComponent&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OnInit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

    &lt;span class=&#34;kd&#34;&gt;@ViewChild&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;defaultTabButtons&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;defaultTabButtonsTpl&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;TemplateRef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;any&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;;&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;ngOnInit() {&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;defaultTabButtonsTpl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;正如我们所看到的，通过向&lt;code&gt;ViewChild&lt;/code&gt;装饰器提供模板引用名称&lt;code&gt;defaultTabButtons&lt;/code&gt;，可以像任何其他&lt;code&gt;DOM&lt;/code&gt;元素或组件一样注入模板。&lt;/p&gt;
&lt;p&gt;这意味着模板也可以在组件类的级别访问，我们可以做一些事情，例如将它们传递给子组件！&lt;/p&gt;
&lt;p&gt;我们想要这样做的一个例子是创建一个更可定制的组件，其中不仅可以传递配置参数或配置对象：我们还可以&lt;em&gt;将模板作为输入参数传递&lt;/em&gt;。&lt;/p&gt;
&lt;h2 id=&#34;具有模板部分inputs的可配置组件&#34;&gt;具有模板部分&lt;code&gt;@Inputs&lt;/code&gt;的可配置组件&lt;/h2&gt;
&lt;p&gt;让我们以&lt;code&gt;tab&lt;/code&gt;容器为例，我们希望为组件的用户提供配置&lt;code&gt;tab&lt;/code&gt;按钮外观的可能性。&lt;/p&gt;
&lt;p&gt;下面是这样的，我们首先要为父组件中的按钮定义自定义模板：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;app-root&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`      
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;ng-template #customTabButtons&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;div class=&amp;#34;custom-class&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        &amp;lt;button class=&amp;#34;tab-button&amp;#34; (click)=&amp;#34;login()&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          {{loginText}}
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        &amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        &amp;lt;button class=&amp;#34;tab-button&amp;#34; (click)=&amp;#34;signUp()&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          {{signUpText}}
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        &amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;/ng-template&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;tab-container [headerTemplate]=&amp;#34;customTabButtons&amp;#34;&amp;gt;&amp;lt;/tab-container&amp;gt;      
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AppComponent&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OnInit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;然后在&lt;code&gt;tab&lt;/code&gt;容器组件上，我们可以定义一个&lt;code&gt;input&lt;/code&gt;属性，它也是一个名为&lt;code&gt;headerTemplate&lt;/code&gt;的模板：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;tab-container&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;ng-template #defaultTabButtons&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;div class=&amp;#34;default-tab-buttons&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        ...
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;/ng-template&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;ng-container 
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;  *ngTemplateOutlet=&amp;#34;headerTemplate ? headerTemplate: defaultTabButtons&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;/ng-container&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;... rest of tab container component ...
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;TabContainerComponent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;kd&#34;&gt;@Input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;headerTemplate&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;TemplateRef&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;any&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在最后的组合示例中，这里有几件事情正在发生。 让我们逐一来看：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为选项卡按钮定义了一个默认模板，名为&lt;code&gt;defaultTabButtons&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;仅当&lt;code&gt;input&lt;/code&gt;属性&lt;code&gt;headerTemplate&lt;/code&gt;未定义时，才会使用此模板&lt;/li&gt;
&lt;li&gt;如果定义了属性，则通过&lt;code&gt;headerTemplate&lt;/code&gt;传递的自定义输入模板将用于显示按钮&lt;/li&gt;
&lt;li&gt;&lt;code&gt;headerTemplate&lt;/code&gt;使用&lt;code&gt;ngTemplateOutlet&lt;/code&gt;属性在&lt;code&gt;ng-container&lt;/code&gt;占位符中实例化&lt;/li&gt;
&lt;li&gt;决定使用哪个模板（默认或自定义）是使用三元表达式，但如果该逻辑很复杂，我们也可以将其委托给控制器方法&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;此设计的最终结果是，如果未提供自定义模板，则&lt;code&gt;tab&lt;/code&gt;容器将显示&lt;code&gt;tab&lt;/code&gt;按钮的默认外观，但如果自定义模板可用，它将使用自定义模板。&lt;/p&gt;
&lt;h2 id=&#34;总结和结论&#34;&gt;总结和结论&lt;/h2&gt;
&lt;p&gt;核心指令&lt;code&gt;ng-container&lt;/code&gt;，&lt;code&gt;ng-template&lt;/code&gt;和&lt;code&gt;ngTemplateOutlet&lt;/code&gt;结合在一起，使我们能够创建高度动态和可定制的组件。&lt;/p&gt;
&lt;p&gt;我们甚至可以根据输入模板完全改变组件的外观和感觉，我们可以定义模板并在应用程序的多个位置实例化。&lt;/p&gt;
&lt;p&gt;这只是这些功能可以组合的一种可能方式！&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>(译)Angular架构04-Redux，RxJs和NgrxStore-何时使用Store以及为什么？</title>
      <link>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angular%E6%9E%B6%E6%9E%8404-reduxrxjs%E5%92%8Cngrxstore-%E4%BD%95%E6%97%B6%E4%BD%BF%E7%94%A8store%E4%BB%A5%E5%8F%8A%E4%B8%BA%E4%BB%80%E4%B9%88/</link>
      <pubDate>Wed, 17 Oct 2018 13:27:04 +0000</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angular%E6%9E%B6%E6%9E%8404-reduxrxjs%E5%92%8Cngrxstore-%E4%BD%95%E6%97%B6%E4%BD%BF%E7%94%A8store%E4%BB%A5%E5%8F%8A%E4%B8%BA%E4%BB%80%E4%B9%88/</guid>
      
        <description>&lt;p&gt;这篇文章是正在进行的&lt;code&gt;Angular&lt;/code&gt;架构系列的一部分，我们将在视图层和服务层一级介绍常见的设计问题和解决方案。 这是完整系列：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;视图层架构-智能组件与展示组件&lt;/li&gt;
&lt;li&gt;视图层架构-容器与展示组件常见的设计缺陷&lt;/li&gt;
&lt;li&gt;服务层架构-如何使用&lt;code&gt;Observable&lt;/code&gt;数据服务构建&lt;code&gt;Angular&lt;/code&gt;应用程序&lt;/li&gt;
&lt;li&gt;服务层架构-&lt;code&gt;Redux&lt;/code&gt;和&lt;code&gt;Ngrx Store&lt;/code&gt;-何时使用&lt;code&gt;Store&lt;/code&gt;？为什么？&lt;/li&gt;
&lt;li&gt;服务层架构-&lt;code&gt;Ngrx Store&lt;/code&gt;-架构指南&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;angular服务层---store架构&#34;&gt;&lt;code&gt;Angular&lt;/code&gt;服务层 - &lt;code&gt;store&lt;/code&gt;架构&lt;/h2&gt;
&lt;p&gt;当前版本的&lt;code&gt;Angular&lt;/code&gt;是对以前版本（&lt;code&gt;AngularJs&lt;/code&gt;）的改进。 视图层比以往更容易学习和使用。&lt;/p&gt;
&lt;p&gt;但是服务层（也称为数据层）实际上是应用程序的功能核心提供了许多选项：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我们应该如何构建服务层？&lt;/li&gt;
&lt;li&gt;我们应该使用&lt;code&gt;store&lt;/code&gt;吗？&lt;/li&gt;
&lt;li&gt;我们应该使用&lt;code&gt;Redux&lt;/code&gt;吗？&lt;/li&gt;
&lt;li&gt;我们应该使用普通的&lt;code&gt;RxJs&lt;/code&gt;吗？&lt;/li&gt;
&lt;li&gt;那么&lt;code&gt;NgRx Store&lt;/code&gt;呢？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在&lt;code&gt;Angular&lt;/code&gt;世界中非常受欢迎的一件事是&lt;code&gt;store&lt;/code&gt;解决方案。&lt;/p&gt;
&lt;p&gt;它们起源于&lt;code&gt;React&lt;/code&gt;世界并经历了通常的技术采用曲线：大规模采用，认识到它不是一切的最终解决方案，然后在某些情况下使用它而不是其他情况。&lt;/p&gt;
&lt;h2 id=&#34;为什么store在react中如此受欢迎&#34;&gt;为什么&lt;code&gt;store&lt;/code&gt;在&lt;code&gt;React&lt;/code&gt;中如此受欢迎？&lt;/h2&gt;
&lt;p&gt;为什么&lt;code&gt;store&lt;/code&gt;在&lt;code&gt;React&lt;/code&gt;世界变得如此受欢迎，是否有特定原因或是由于多种原因？ 这些原因是否也适用于&lt;code&gt;Angular&lt;/code&gt;世界，还是有替代解决方案？ &lt;code&gt;store&lt;/code&gt;解决了哪些问题？&lt;/p&gt;
&lt;p&gt;您是否注意到有很多关于&lt;code&gt;store&lt;/code&gt;解决方案的信息，但关于我们何时应该使用它们以及为什么使用它们的信息有限？ 让我们回顾一下这些问题。&lt;/p&gt;
&lt;h2 id=&#34;目录&#34;&gt;目录&lt;/h2&gt;
&lt;p&gt;在这篇文章中，我们将讨论以下主题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;何时使用&lt;code&gt;Redux&lt;/code&gt;或&lt;code&gt;store&lt;/code&gt;？&lt;/li&gt;
&lt;li&gt;我们经常需要&lt;code&gt;store&lt;/code&gt;吗？&lt;/li&gt;
&lt;li&gt;为什么&lt;code&gt;Redux&lt;/code&gt;在&lt;code&gt;React&lt;/code&gt;世界中如此受欢迎？&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Redux&lt;/code&gt;解决的问题是否也出现在&lt;code&gt;Angular&lt;/code&gt;世界中？&lt;/li&gt;
&lt;li&gt;&lt;code&gt;store&lt;/code&gt;解决了什么问题？&lt;/li&gt;
&lt;li&gt;什么类型的应用程序受益于&lt;code&gt;store&lt;/code&gt;解决方案？&lt;/li&gt;
&lt;li&gt;什么类型的工具与&lt;code&gt;store&lt;/code&gt;解决方案相关联？&lt;/li&gt;
&lt;li&gt;&lt;code&gt;React&lt;/code&gt;和&lt;code&gt;Angular&lt;/code&gt;中的单向数据流&lt;/li&gt;
&lt;li&gt;&lt;code&gt;store&lt;/code&gt;和可测试性&lt;/li&gt;
&lt;li&gt;&lt;code&gt;store&lt;/code&gt;和表现&lt;/li&gt;
&lt;li&gt;&lt;code&gt;store&lt;/code&gt;和工具&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Redux&lt;/code&gt; vs &lt;code&gt;Mobx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;与&lt;code&gt;Mobx&lt;/code&gt;和&lt;code&gt;CycleJs&lt;/code&gt;进行工具比较&lt;/li&gt;
&lt;li&gt;关于方法的提案&lt;/li&gt;
&lt;li&gt;结论和建议&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意：下面有一个带有&lt;code&gt;Ngrx DevTools&lt;/code&gt;演示的视频。 此外，您可能希望在此另一篇文章中介绍集中存储模式和&lt;code&gt;Ngrx Store&lt;/code&gt; - &lt;code&gt;Angular Ngrx&lt;/code&gt;速成课程第1部分：&lt;a href=&#34;https://blog.angular-university.io/angular-ngrx-store-and-effects-crash-course/&#34;&gt;Ngrx Store - Learn It By Understanding The Original Facebook Counter Bug&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;何时使用redux或store&#34;&gt;何时使用&lt;code&gt;Redux&lt;/code&gt;或&lt;code&gt;store&lt;/code&gt;？&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;store&lt;/code&gt;起源于&lt;code&gt;Redux&lt;/code&gt;世界，所以这将是最先看的知识，然后从那里学习。&lt;/p&gt;
&lt;p&gt;让我们来看看&lt;code&gt;React&lt;/code&gt;生态系统的&lt;a href=&#34;https://github.com/petehunt/react-howto&#34;&gt;&lt;code&gt;react-howto&lt;/code&gt;&lt;/a&gt;指南，有什么建议？ 这是一个重要的引用：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你可能听说过&lt;code&gt;Flux&lt;/code&gt;。 那里有关于&lt;code&gt;Flux&lt;/code&gt;的大量错误信息。 许多人坐下来构建一个应用程序，并希望定义他们的数据模型，他们认为他们需要使用&lt;code&gt;Flux&lt;/code&gt;来完成它。 这是采用&lt;code&gt;Flux&lt;/code&gt;的错误方法。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;Redux&lt;/code&gt;的创建者也有这个著名的文章——&lt;a href=&#34;https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367#.z9abvda1k&#34;&gt;You Might Not Need Redux&lt;/a&gt;，它可以应用于任何&lt;code&gt;store&lt;/code&gt;解决方案。&lt;/p&gt;
&lt;p&gt;然后在&lt;code&gt;React How-To&lt;/code&gt;中还有另一个声明，它似乎同样适用于原始的&lt;code&gt;Flux&lt;/code&gt;，&lt;code&gt;Redux&lt;/code&gt;，&lt;code&gt;Ngrx Store&lt;/code&gt;或任何&lt;code&gt;store&lt;/code&gt;解决方案：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;你会知道什么时候需要&lt;code&gt;Flux&lt;/code&gt;。 如果您不确定是否需要它，则不需要它。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;基于此，看起来&lt;code&gt;store&lt;/code&gt;不建议由一些原创作者系统使用。 在这些文章中，我们得到的印象是创作者似乎担心&lt;code&gt;store&lt;/code&gt;被视为一种适合所有解决方案的&lt;code&gt;store&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但后来我们遇到了像这样的文章——&lt;a href=&#34;https://medium.com/@silvenon/i-always-seem-to-need-redux-f37686c23e45#.etd9b2gnr&#34;&gt;I Always Seem to Need Redux&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不知何故，即使&lt;code&gt;store&lt;/code&gt;被他们自己的创作者谨慎推荐，他们仍然在&lt;code&gt;React&lt;/code&gt;世界中大规模采用。&lt;/p&gt;
&lt;p&gt;为什么会这样？ 我们试着回答这个问题。&lt;/p&gt;
&lt;h2 id=&#34;什么时候建议使用flux或redux&#34;&gt;什么时候建议使用&lt;code&gt;Flux&lt;/code&gt;或&lt;code&gt;Redux&lt;/code&gt;？&lt;/h2&gt;
&lt;p&gt;如果我们深入研究&lt;code&gt;React How-To&lt;/code&gt;的文档，我们会得到一些关于何时从&lt;code&gt;Flux&lt;/code&gt;中受益的迹象：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;React&lt;/code&gt;组件按层次结构排列。 大多数情况下，您的数据模型也遵循层次结构。 在这些情况下，&lt;code&gt;Flux&lt;/code&gt;不会给你带来太多帮助。 但是，有时您的数据模型不是分层的。 当您的&lt;code&gt;React&lt;/code&gt;组件开始接收感觉无关的道具，或者您有少量组件开始变得非常复杂时，您可能想要查看&lt;code&gt;Flux&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;此外，如果我们深入研究问题，我们也会得到这些建议。 如果符合以下情况，建议使用类似于&lt;code&gt;store&lt;/code&gt;：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;您有一段数据需要在您的应用中的多个位置使用，并通过属性传递它会使您的组件违反单一责任原则（即使他们的界面变得不那么有意义）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但也有这种情况：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;有多个独立的参与者（通常是服务器和最终用户）可能会改变这些数据&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因此，有几种情况建议将&lt;code&gt;store&lt;/code&gt;解决方案与&lt;code&gt;React&lt;/code&gt;一起使用。 那么让我们看看它如何适合&lt;code&gt;Angular&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;具有并发更新的store和应用程序&#34;&gt;具有并发更新的&lt;code&gt;store&lt;/code&gt;和应用程序&lt;/h2&gt;
&lt;p&gt;如果我们仅基于最后一部分，只有少数应用程序即具有服务器推送要求的应用程序将受益于&lt;code&gt;Flux&lt;/code&gt;。 因为通常这时候我们有多个角色更新相同的数据，这就是源自&lt;code&gt;Flux&lt;/code&gt;的原始&lt;code&gt;Facebook&lt;/code&gt;计数问题。&lt;/p&gt;
&lt;p&gt;有关原始计数器问题的更多详细信息，请查看最初的&lt;code&gt;Flux&lt;/code&gt;&lt;a href=&#34;https://youtu.be/nYkdrAPrdcw&#34;&gt;演讲&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;请注意，我们不需要让服务器推送到这种情况，使用&lt;code&gt;setInterval&lt;/code&gt;进行长轮询或修改&lt;code&gt;setTimeout&lt;/code&gt;中的数据会导致我们遇到相同的情况：多个角色同时编辑相同的数据。&lt;/p&gt;
&lt;p&gt;我们可以肯定地说很多应用都没有这个问题，对吧？ 这是一个我们需要设计的重要问题，如果存在，但大多数应用程序都有它吗？ 可能不是，只有某类应用程序。&lt;/p&gt;
&lt;p&gt;但是为什么&lt;code&gt;Redux&lt;/code&gt;在&lt;code&gt;React&lt;/code&gt;世界中被普遍采用呢？ 这留下了另一个原因。&lt;/p&gt;
&lt;h2 id=&#34;redux解决的最常见问题是什么&#34;&gt;&lt;code&gt;Redux&lt;/code&gt;解决的最常见问题是什么？&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Redux&lt;/code&gt;还解决了&lt;code&gt;extraneous props&lt;/code&gt;问题。 这一直是&lt;code&gt;Redux&lt;/code&gt;在&lt;code&gt;React&lt;/code&gt;世界中如此受欢迎的主要原因之一。&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;Angular&lt;/code&gt;中，&lt;code&gt;props feels extraneous&lt;/code&gt;是什么意思？ &lt;code&gt;Props&lt;/code&gt;相当于&lt;code&gt;Angular&lt;/code&gt;组件的&lt;code&gt;@Input()&lt;/code&gt;成员变量。&lt;/p&gt;
&lt;p&gt;因此，这意味着&lt;code&gt;Redux&lt;/code&gt;可以帮助我们应对使用&lt;code&gt;@Input()&lt;/code&gt;将输入传递到组件树上的组件的情况，但这些输入感觉无关紧要，因为此时不是应用程序的一部分。&lt;/p&gt;
&lt;p&gt;例如，我们在组件树上传递了5或10个级别。 树的叶子知道如何处理它，但对于中间的所有组件，这个输入是无关紧要的，并使该组件可重用性降低，而且更加依赖于应用程序。 但这只是一个例子。&lt;/p&gt;
&lt;h2 id=&#34;extraneous-props还有什么意思&#34;&gt;&lt;code&gt;extraneous props&lt;/code&gt;，还有什么意思？&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;extraneous props&lt;/code&gt;问题似乎是一个普遍问题。&lt;/p&gt;
&lt;p&gt;在某些情况下，组件在组件树中的完全不同的点上彼此依赖，并且将输入10级向上传递到树上，并且回调函数10级向下传递到树上，然后5级到另一个分支，这在复杂性上不可缩放。&lt;/p&gt;
&lt;p&gt;发生这种情况时，还有其他情况：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在树的深处传递数据，并对组件树上的几个级别的事件做出反应&lt;/li&gt;
&lt;li&gt;另一个问题是，我们在树中具有相互依赖的兄弟组件，它们代表屏幕上相同数据的不同视图，例如具有未读消息的文件夹列表，以及页眉上的未读消息计数器。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还有更多的例子。 如果我们只有&lt;code&gt;props&lt;/code&gt;或&lt;code&gt;@Input()&lt;/code&gt;作为组件通信机制，我们会很快遇到麻烦。 仅传递组件的输入不会复杂化。&lt;/p&gt;
&lt;p&gt;这些场景实际上很常见，所以有我们的答案。&lt;/p&gt;
&lt;h2 id=&#34;为什么redux在react世界中如此受欢迎&#34;&gt;为什么&lt;code&gt;Redux&lt;/code&gt;在&lt;code&gt;React&lt;/code&gt;世界中如此受欢迎？&lt;/h2&gt;
&lt;p&gt;可能是因为它也解决了&lt;code&gt;extraneous props&lt;/code&gt;问题：这意味着它为更复杂的组件交互场景提供了解决方案。&lt;/p&gt;
&lt;p&gt;这是一个基本问题，没有它我们就无法构建更大的应用程序，而&lt;code&gt;Redux&lt;/code&gt;解决了它。&lt;/p&gt;
&lt;p&gt;几乎所有非平凡的应用程序都有这些场景，它确实不占用大型应用程序，大多数典型的企业应用程序都会有某种复杂的组件互通场景。&lt;/p&gt;
&lt;h2 id=&#34;为什么redux在这些情况下运行良好&#34;&gt;为什么&lt;code&gt;Redux&lt;/code&gt;在这些情况下运行良好？&lt;/h2&gt;
&lt;p&gt;如果我们尝试使用像&lt;code&gt;AngularJs&lt;/code&gt;的&lt;code&gt;$scope.broadcast()&lt;/code&gt;这样的事件总线解决这些场景，我们将很容易地结束&lt;code&gt;event soup&lt;/code&gt;场景，其中事件以意想不到的方式链接自己，并且很难推断应用程序。&lt;/p&gt;
&lt;p&gt;这是因为事件可以很容易地转换为命令，使&lt;code&gt;emitter&lt;/code&gt;知道接收器的内部。 此外，还有可能意外地将事件链接在一起。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Redux&lt;/code&gt;看起来像一个事件总线，但事实并非如此。 实际上，&lt;code&gt;Redux&lt;/code&gt;存储是&lt;code&gt;Command&lt;/code&gt;和&lt;code&gt;Observable&lt;/code&gt;模式的组合。 我们对&lt;code&gt;store&lt;/code&gt;做的是，我们发送一个称为&lt;code&gt;action&lt;/code&gt;的命令对象：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;nx&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dispatch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;REFRESH_MESSAGES&amp;#39;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们将一个&lt;code&gt;action&lt;/code&gt;发送到&lt;code&gt;store&lt;/code&gt;，&lt;code&gt;store&lt;/code&gt;将对&lt;code&gt;store&lt;/code&gt;内的数据进行操作。 但是&lt;code&gt;action&lt;/code&gt;的&lt;code&gt;emitter&lt;/code&gt;不知道&lt;code&gt;store&lt;/code&gt;会用它做什么。&lt;/p&gt;
&lt;p&gt;我们还可以从应用程序的完全不同的部分发送另一个操作：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;nx&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dispatch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;MARK_MESSAGE_AS_READ&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;messageId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;103&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;store&lt;/code&gt;将处理它并更新消息列表。 然后将消息发送到需要它的应用程序的任何部分。 但接收端不知道是什么触发了新数据的生成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从后端到达的新消息&lt;/li&gt;
&lt;li&gt;刷新请求&lt;/li&gt;
&lt;li&gt;消息被标记为已读&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么这与解耦和扩展复杂性有什么关系呢？&lt;/p&gt;
&lt;h2 id=&#34;store如何允许分离的组件交互&#34;&gt;&lt;code&gt;store&lt;/code&gt;如何允许分离的组件交互&lt;/h2&gt;
&lt;p&gt;使用新版本数据的组件（可能是消息列表和计数器）不知道是什么导致数据发生变化，就像我们订阅&lt;code&gt;RxJs Observable&lt;/code&gt;时我们不知道什么触发了值的发出，我们只知道我们有新的值。&lt;/p&gt;
&lt;p&gt;消费组件已经订阅了&lt;code&gt;store&lt;/code&gt;，就像他们订阅了&lt;code&gt;RxJs Observable&lt;/code&gt;一样。 这种模式效果很好，因为我们必须尽力将发出的数据转换为命令，而事件总线则非常容易。&lt;/p&gt;
&lt;h2 id=&#34;服务器推送怎么样&#34;&gt;服务器推送怎么样？&lt;/h2&gt;
&lt;p&gt;我们现在说服务器也在不断地推送新数据，新消息。 数据也通过调度操作推送：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;nx&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dispatch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;DISPLAY_NEW_MESSAGE&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;messageId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;104&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;userId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;3000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;status&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;UNREAD&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;text&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Hello World !&amp;#39;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在所有情况下，都会收到一个新的消息列表并将其呈现为消息列表或未读消息的计数器。 渲染的结果将是一致的：我们将不会有一个全部读取的消息列表和一个表示有3条未读消息的计数器。&lt;/p&gt;
&lt;h2 id=&#34;这种情况是store闪耀的时候&#34;&gt;这种情况是&lt;code&gt;store&lt;/code&gt;闪耀的时候&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;store&lt;/code&gt;是解决可编辑数据和多个参与者问题的理想解决方案，但我们可以想象数据不会从服务器推送出来。 在这种情况下，我们只有组件交互和协调问题，但我们没有竞争条件的可能性。&lt;/p&gt;
&lt;p&gt;在这种情况下，我们试图解决的问题只是在组件树的多个断开连接的位置进行组件交互，对吗？&lt;/p&gt;
&lt;p&gt;我们不再需要由多个并发角色编辑相同数据的解决方案。 这导致了&lt;code&gt;Redux&lt;/code&gt;和&lt;code&gt;store&lt;/code&gt;的一个重要特征。&lt;/p&gt;
&lt;h2 id=&#34;store是解决多个问题的复合解决方案而不仅仅是一个问题&#34;&gt;&lt;code&gt;store&lt;/code&gt;是解决多个问题的复合解决方案，而不仅仅是一个问题&lt;/h2&gt;
&lt;p&gt;我们可以看到这个例子看到&lt;code&gt;store&lt;/code&gt;是一个多责任的解决方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;他们通过&lt;code&gt;Observable&lt;/code&gt;模式解决了组件交互的问题&lt;/li&gt;
&lt;li&gt;如果需要，它们提供客户端缓存，以避免重复的&lt;code&gt;Ajax&lt;/code&gt;请求&lt;/li&gt;
&lt;li&gt;它们提供了放置临时&lt;code&gt;UI&lt;/code&gt;状态的位置，因为我们填写大型表单或者想要在路由器视图之间导航时在搜索表单中存储搜索条件&lt;/li&gt;
&lt;li&gt;它们解决了允许多个参与者修改客户端瞬态数据的问题&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;store&lt;/code&gt;不仅仅解决其中一个问题，而是解决所有这些问题。&lt;/p&gt;
&lt;h2 id=&#34;多责任解决方案有什么问题&#34;&gt;多责任解决方案有什么问题？&lt;/h2&gt;
&lt;p&gt;一个潜在的问题是那些问题并不总是在一起：你可能想要解决一个而不是另一个。 并非每个应用程序都具有与&lt;code&gt;Facebook&lt;/code&gt;相同的限制：它是全球最大的&lt;code&gt;Web&lt;/code&gt;应用程序，拥有18亿用户。&lt;/p&gt;
&lt;p&gt;假设您的应用程序是典型的企业应用程序，用户少于100个：您对客户端缓存的使用有限，并且可能没有服务器推送要求。您可能有服务器推送但数据通常是只读的。&lt;/p&gt;
&lt;p&gt;在这种情况下，您可能无法从&lt;code&gt;store&lt;/code&gt;架构中受益（稍后会详细介绍）。&lt;/p&gt;
&lt;p&gt;此外，您可能需要涵盖复杂的组件交互方案，而无需将数据存储在内存中。 这里的重要部分是这些问题并不总是在一起：它们聚集在一起，用于非常特殊的应用程序而不是其他应用程序。&lt;/p&gt;
&lt;h2 id=&#34;redux是否避免状态相关的问题&#34;&gt;&lt;code&gt;Redux&lt;/code&gt;是否避免状态相关的问题？&lt;/h2&gt;
&lt;p&gt;重要的是要注意它不会，并且一般都没有其他全局存储解决方案，因为使用&lt;code&gt;Redux&lt;/code&gt;我们正在创建一个大的全局应用程序级状态：存储是一个应用程序范围的单例服务。&lt;/p&gt;
&lt;p&gt;全局应用程序状态的问题不是它创建的方式，而是它存在的事实。 由于我们忘记清理它，因此很容易产生细微的错误。 它实际上并没有改变我们仅使用纯&lt;code&gt;reducer&lt;/code&gt;函数创建状态，或者全局状态是不可变的这一事实。&lt;/p&gt;
&lt;p&gt;所有这些都有所帮助，但我们仍然创建了全局应用程序状态，主要问题仍然存在：它存在并且我们需要在所有正确的位置清理它，并且在复杂性方面不能很好地扩展。&lt;/p&gt;
&lt;p&gt;但是如果需要的话，全局状态没有任何问题：到处都需要一些用户数据，为什么不加载一次并将其放入单例服务？&lt;/p&gt;
&lt;h2 id=&#34;处理全局状态的最佳方式是什么&#34;&gt;处理全局状态的最佳方式是什么？&lt;/h2&gt;
&lt;p&gt;避免全局应用程序状态的最佳方法是不创建它，除非它是必要的。 现代应用程序确实需要比以前更多的状态：例如，当我们浏览应用程序时，我们在哪里保留给定搜索表单的最后搜索结果？&lt;/p&gt;
&lt;p&gt;每当我们从详细信息返回到主表时，我们都不想重复搜索，即使我们触发了路由器导航。&lt;/p&gt;
&lt;h2 id=&#34;我们可以使用临时的本地状态吗&#34;&gt;我们可以使用临时的本地状态吗？&lt;/h2&gt;
&lt;p&gt;这些情况的理想情况是能够创建仅与该特定掌握细节设置的交互本地化的状态，并使其在使用后自动清理自身。&lt;/p&gt;
&lt;p&gt;这就是&lt;code&gt;Angular&lt;/code&gt;允许我们做的事情，我们将在稍后看到。&lt;/p&gt;
&lt;h2 id=&#34;除了store之外angular世界还有替代解决方案吗&#34;&gt;除了&lt;code&gt;store&lt;/code&gt;之外，&lt;code&gt;Angular&lt;/code&gt;世界还有替代解决方案吗？&lt;/h2&gt;
&lt;p&gt;在&lt;code&gt;Angular&lt;/code&gt;中，我们有一整套内置的解决方案来处理复杂的组件交互场景。 所有这些解决方案的核心是&lt;code&gt;Angular&lt;/code&gt;依赖注入系统：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果我们愿意，我们可以在组件树中深入注入服务&lt;/li&gt;
&lt;li&gt;如果我们觉得它们本身紧密耦合，我们甚至可以将组件或服务注入彼此&lt;/li&gt;
&lt;li&gt;我们可以创建可能存储或不存储数据的共享数据服务&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但那只是开始。 让我们回到主要细节场景：
我们可以创建一个非全局服务，并使用分层注入器将其关联到页面的一部分。 这意味着服务及其最终状态将透明地清理自己。&lt;/p&gt;
&lt;h2 id=&#34;创建自我清理的本地状态&#34;&gt;创建自我清理的本地状态&lt;/h2&gt;
&lt;p&gt;假设我们已经浏览了包含消息列表的应用程序的一部分，并且我们单击列表并转到消息的详细信息。&lt;/p&gt;
&lt;p&gt;这是该路由的顶级组件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;messages-container&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;providers&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;MessagesService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        Messages Master Detail Container:
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        &amp;lt;router-outlet&amp;gt;&amp;lt;/router-outlet&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;  `&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MessagesContainerComponent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

      &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;请注意&lt;code&gt;providers&lt;/code&gt;属性中的&lt;code&gt;MessagesService&lt;/code&gt;。 这是什么意思？ 这意味着该服务不是应用程序范围的单例。 因此，如果我们想要在打开和关闭多个详细信息时将主服务器的搜索结果保留在内存中，则&lt;code&gt;MessagesService&lt;/code&gt;将是放置它而不是全局存储的理想位置。 为什么？&lt;/p&gt;
&lt;p&gt;因为&lt;code&gt;MessagesService&lt;/code&gt;的这个实例是&lt;code&gt;MessagesContainerComponent&lt;/code&gt;及其兄弟的本地实例。 它只能在那里注入，而不是应用程序中的任何其他地方。&lt;/p&gt;
&lt;p&gt;您还可以创建一个&lt;code&gt;MessagesTableService&lt;/code&gt;并将其注入表的级别，使用它来加载和分页数据并且并排放置多个表，每个表都有自己的&lt;code&gt;MessagesTableService&lt;/code&gt;实例。&lt;/p&gt;
&lt;p&gt;关于这些仅由组件树的子集可见的本地服务的好处在于，当我们离开其路由时，它们与相关组件一起清理自己。&lt;/p&gt;
&lt;p&gt;本地有状态服务可以实现为例如&lt;code&gt;observable&lt;/code&gt;数据服务。&lt;/p&gt;
&lt;h2 id=&#34;angular和store频繁的选择&#34;&gt;&lt;code&gt;Angular&lt;/code&gt;和&lt;code&gt;store&lt;/code&gt;——频繁的选择？&lt;/h2&gt;
&lt;p&gt;我们可以看到，在&lt;code&gt;Angular&lt;/code&gt;中，我们可以使用许多组件间通信机制，而不仅仅是&lt;code&gt;@Input()&lt;/code&gt;，我们还有一种自动创建和处理本地状态的机制。&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;Angular&lt;/code&gt;，我们不一定受益于&lt;code&gt;store&lt;/code&gt;来解决这些问题，还有许多其他内置解决方案。&lt;/p&gt;
&lt;p&gt;很多时候，&lt;code&gt;store&lt;/code&gt;被添加到应用程序中以获得类似于可观察的&lt;code&gt;API&lt;/code&gt;以允许某些组件交互：为什么不简单地使用&lt;code&gt;Observable&lt;/code&gt;？&lt;/p&gt;
&lt;p&gt;添加存储是对应用程序整体架构的重要约束，它意味着创建大量的全局应用程序状态。 如果内置更好的替代方案并不意味着这一点，为什么不考虑它们呢？&lt;/p&gt;
&lt;h2 id=&#34;使用store有成本&#34;&gt;使用&lt;code&gt;store&lt;/code&gt;有成本&lt;/h2&gt;
&lt;p&gt;该&lt;code&gt;store&lt;/code&gt;确实解决了组件交互的问题，但它也创建了在应用程序中管理状态的需求，而在使用其他解决方案时可能不存在。&lt;/p&gt;
&lt;p&gt;这可能意味着在&lt;code&gt;Angular&lt;/code&gt;中，&lt;code&gt;store&lt;/code&gt;解决方案比&lt;code&gt;React&lt;/code&gt;更不常用？ 事实上，在最初的一段时间之后，&lt;code&gt;React&lt;/code&gt;也会寻求其他解决方案，我们将会看到。&lt;/p&gt;
&lt;p&gt;通常还提到了支持&lt;code&gt;store&lt;/code&gt;解决方案选择的其他参数：性能，可测试性，工具以及保持应用程序可预测且易于推理的能力。 让我们逐一介绍这些，从最后一个开始。&lt;/p&gt;
&lt;h2 id=&#34;单向数据流&#34;&gt;单向数据流&lt;/h2&gt;
&lt;p&gt;单向数据流是我们在&lt;code&gt;React&lt;/code&gt;和&lt;code&gt;Angular&lt;/code&gt;中都听到的一个重要属性：它被称为在应用程序中查找的属性，它确保它们是可预测且易于推理的。&lt;/p&gt;
&lt;h2 id=&#34;react中的单向数据流&#34;&gt;&lt;code&gt;React&lt;/code&gt;中的单向数据流&lt;/h2&gt;
&lt;p&gt;在最初的&lt;code&gt;Flux&lt;/code&gt;会话中，单向数据流描述如下：用户触发&lt;code&gt;action&lt;/code&gt;，将其分派到生成新模型的&lt;code&gt;store&lt;/code&gt;并将其发送到视图。&lt;/p&gt;
&lt;p&gt;但是视图在呈现时不能自己发送进一步的&lt;code&gt;action&lt;/code&gt;，如果&lt;code&gt;action&lt;/code&gt;的发送已经在进行，也不能发送另一个&lt;code&gt;action&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;避免这种情况看起来像基于原始演示文稿的&lt;code&gt;Flux&lt;/code&gt;的主要目标之一，请看&lt;a href=&#34;https://www.youtube.com/watch?v=nYkdrAPrdcw&amp;amp;feature=youtu.be&amp;amp;list=PLb0IAmt7-GS188xDYE-u1ShQmFFGbrk0v&amp;amp;t=1075&#34;&gt;这里&lt;/a&gt;。 &lt;a href=&#34;https://www.youtube.com/watch?v=nYkdrAPrdcw&amp;amp;feature=youtu.be&amp;amp;list=PLb0IAmt7-GS188xDYE-u1ShQmFFGbrk0v&amp;amp;t=1157&#34;&gt;这里&lt;/a&gt;是另一个参考。
另外，请查看最初的&lt;code&gt;Flux&lt;/code&gt;调度程序&lt;a href=&#34;https://github.com/facebook/flux/blob/master/src/Dispatcher.js#L183&#34;&gt;代码&lt;/a&gt;，其中在会谈中提到了检查。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;React&lt;/code&gt;和&lt;code&gt;Flux&lt;/code&gt;中的&lt;code&gt;UI&lt;/code&gt;可预测性似乎主要通过在数据层上设置有益约束来实现：防止链式调度。&lt;/p&gt;
&lt;h2 id=&#34;redux和单向数据流&#34;&gt;&lt;code&gt;Redux&lt;/code&gt;和单向数据流&lt;/h2&gt;
&lt;p&gt;重要的是要注意&lt;code&gt;Redux&lt;/code&gt;不能防止最初的&lt;code&gt;Flux&lt;/code&gt;会谈中提到的链式调度场景。 使用&lt;code&gt;Redux&lt;/code&gt;，我们可以在原始&lt;code&gt;Flux&lt;/code&gt;中触发订阅方法的另一个调度，如果已经调度了一个&lt;code&gt;action&lt;/code&gt;，我们就无法触发另一个。&lt;/p&gt;
&lt;p&gt;因此，强制执行单向数据流的需求似乎并不是&lt;code&gt;Redux&lt;/code&gt;如此广泛采用的主要原因之一，因为通过设计并且至少根据原始&lt;code&gt;Flux&lt;/code&gt;会谈中提供的定义，它不会阻止链式调度问题。&lt;/p&gt;
&lt;p&gt;也许是因为它太过限制而且在实践中它并没有发生太多？&lt;/p&gt;
&lt;h2 id=&#34;angular中的单向数据流&#34;&gt;&lt;code&gt;Angular&lt;/code&gt;中的单向数据流&lt;/h2&gt;
&lt;p&gt;在&lt;code&gt;Angular&lt;/code&gt;中，我们还看到单向数据流被提及为允许我们以可预测的方式推断应用程序的属性。&lt;/p&gt;
&lt;p&gt;但是关注点似乎有点不同：它不是关于对数据层施加约束，数据层可以是任何形式。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Angular&lt;/code&gt;中的单向数据流被描述为确保视图无法自行更新。 那是什么意思？&lt;/p&gt;
&lt;h2 id=&#34;开发模式下的单向数据流和渲染&#34;&gt;开发模式下的单向数据流和渲染&lt;/h2&gt;
&lt;p&gt;渲染开始时，我们在一次扫描中浏览组件树，而在渲染期间组件不能在第二次传递时给出不同的结果或修改父组件。&lt;/p&gt;
&lt;p&gt;基本上，评估模板中的表达式或触发某些组件生命周期方法的行为本身不会触发视图中的进一步更改，从而创建类似于&lt;code&gt;AngularJs&lt;/code&gt;的多步藏检测的情况，这有时会导致不可预测的结果。&lt;/p&gt;
&lt;h2 id=&#34;打破angular单向数据流&#34;&gt;打破&lt;code&gt;Angular&lt;/code&gt;单向数据流&lt;/h2&gt;
&lt;p&gt;想象一下，你正在向屏幕上打印一个随机数：如果你试图通过组件&lt;code&gt;getter&lt;/code&gt;方法计算它并将其传递给模板表达式，我们将在开发模式中破坏应用程序，因为你第二次从上到下扫描没有得到相同的结果 ：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;messages-container&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;       {{randomNumber}}
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;  `&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MessagesContainerComponent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

      &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;


     &lt;span class=&#34;nx&#34;&gt;get&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;randomNumber() {&lt;/span&gt;
          &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
     &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;试试吧，你应该得到：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;Expression has changed after it was checked
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;因此，看起来确保&lt;code&gt;UI&lt;/code&gt;中可预测的呈现行为并阻止视图自身更新，我们不一定需要采用类似于&lt;code&gt;store&lt;/code&gt;的体系结构。&lt;/p&gt;
&lt;p&gt;让我们回顾一下使用&lt;code&gt;store&lt;/code&gt;的另一个常见原因：提高性能，接下来让我们回顾可测试性和工具。&lt;/p&gt;
&lt;h2 id=&#34;store和性能&#34;&gt;&lt;code&gt;store&lt;/code&gt;和性能&lt;/h2&gt;
&lt;p&gt;有时候&lt;code&gt;store&lt;/code&gt;被认为是使应用程序更高效的一种方式，因为我们可以使用&lt;code&gt;ImmutableJs&lt;/code&gt;或&lt;code&gt;Deep Freeze&lt;/code&gt;这样的状态使状态不可变，然后我们可以在任何地方使用&lt;code&gt;OnPush&lt;/code&gt;变更检测。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Angular&lt;/code&gt;变化检测机制开箱即用，并且行为非常直观。 默认情况下，我们仅使用模板中的表达式作为表达式来检测更改，其余部分将被忽略（请查看此&lt;a href=&#34;https://blog.angular-university.io/how-does-angular-2-change-detection-really-work/&#34;&gt;文章&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;OnPush&lt;/code&gt;实际上是一种优化，只有少数应用程序可能会从中受益，例如加载大量数据的应用程序（以及我们可以加载多少对用户仍然有用的数据），或者在非常有限的设备中运行的应用程序。&lt;/p&gt;
&lt;p&gt;可以肯定地说，大多数应用程序都不属于这些类别（目前的智能手机）。 但是如果我们仍然需要&lt;code&gt;OnPush&lt;/code&gt;，我们可以在没有&lt;code&gt;store&lt;/code&gt;的情况下使用它，特别是如果我们的数据大部分都是只读的话。&lt;/p&gt;
&lt;p&gt;如果应用程序是某种类型的实时仪表板，如图表仪表板，则可能更好地限制数据或其他解决方案。 我们甚至可以从更改检测中分离&lt;code&gt;UI&lt;/code&gt;的一个分支并限制其渲染。&lt;/p&gt;
&lt;p&gt;这里的要点是添加&lt;code&gt;store&lt;/code&gt;确实意味着我们将使应用程序更高效或更容易优化，因为我们可以以完全独立的方式优化变更检测系统 - 这两个东西可以一起使用但是没有固有的联系。&lt;/p&gt;
&lt;p&gt;采用&lt;code&gt;store&lt;/code&gt;架构的另一个共同点是可测试性，让我们看一下，这是进入工具演示之前的最后一点。&lt;/p&gt;
&lt;h2 id=&#34;store和可测试性&#34;&gt;&lt;code&gt;store&lt;/code&gt;和可测试性&lt;/h2&gt;
&lt;p&gt;引入&lt;code&gt;store&lt;/code&gt;经常提出的主要好处之一是它将提高应用程序的可测试性。&lt;/p&gt;
&lt;p&gt;确实，&lt;code&gt;reducer&lt;/code&gt;函数很容易测试，但是应用程序本身并没有通过引入&lt;code&gt;store&lt;/code&gt;来测试，因为我们通过依赖注入系统注入依赖项而不是直接在组件内部创建它们。&lt;/p&gt;
&lt;p&gt;假设一个应用程序没有很多数据修改或服务器和用户对数据的并发修改：该应用程序可能不需要存储，并且引入它不会使它更易于测试。&lt;/p&gt;
&lt;p&gt;但最后也是最重要的是，我们获得了巨大的利益 - 工具。&lt;/p&gt;
&lt;h2 id=&#34;store和工具&#34;&gt;&lt;code&gt;store&lt;/code&gt;和工具&lt;/h2&gt;
&lt;p&gt;使用&lt;code&gt;store&lt;/code&gt;的最大原因之一是它提供的工具生态系统。 工具是惊人的，&lt;code&gt;time traveling&lt;/code&gt;调试，能够将&lt;code&gt;store&lt;/code&gt;状态附加到错误报告和热重新加载，这些是巨大的功能。&lt;/p&gt;
&lt;p&gt;通过查看这个&lt;a href=&#34;https://www.youtube.com/watch?v=70ojPxMA7Ig&#34;&gt;简短的视频&lt;/a&gt;来演示&lt;code&gt;Ngrx DevTools&lt;/code&gt;。 如果你从未见过它，它真的值得。&lt;/p&gt;
&lt;p&gt;这些工具是惊人的，但看起来现在&lt;code&gt;Redux&lt;/code&gt;不是必须在新的&lt;code&gt;React&lt;/code&gt;应用程序中使用，那么它在工具方面如何工作？&lt;/p&gt;
&lt;h3 id=&#34;redux的常用替代方案&#34;&gt;&lt;code&gt;Redux&lt;/code&gt;的常用替代方案&lt;/h3&gt;
&lt;p&gt;在最初采用&lt;code&gt;Redux&lt;/code&gt;的一段时间之后，许多&lt;code&gt;React&lt;/code&gt;应用程序正在使用&lt;code&gt;MobX&lt;/code&gt;构建，&lt;code&gt;MobX&lt;/code&gt;是&lt;code&gt;Observable&lt;/code&gt;模式的变体。 我们可以在文档中看到这个描述：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;MobX&lt;/code&gt;为现有的数据结构（如对象，数组和类实例）添加了可观察的功能。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这可以通过使用&lt;code&gt;@observable&lt;/code&gt;装饰器（&lt;code&gt;ES.Next&lt;/code&gt;）注释您的类属性来完成。 这是一个小代码示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Todo&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Math&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
    &lt;span class=&#34;kd&#34;&gt;@observable&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;title&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;kd&#34;&gt;@observable&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;finished&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;如果您在&lt;code&gt;NgRx Dev Tools&lt;/code&gt;上看到上面的视频，这看起来很熟悉吗？ 看一下，还有一些&lt;a href=&#34;https://github.com/zalmoxisus/mobx-remotedev&#34;&gt;开发者工具&lt;/a&gt;就像&lt;code&gt;Mobx&lt;/code&gt;的&lt;code&gt;redux dev&lt;/code&gt;工具。&lt;/p&gt;
&lt;p&gt;实际上，&lt;code&gt;Mobx&lt;/code&gt;开发工具也使用相同的浏览器插件。 基于这个例子，似乎拥有这种高级类型的工具我们不一定需要采用&lt;code&gt;store&lt;/code&gt;架构。 我们需要做的就是使用具有良好或不断发展的工具生态系统的&lt;code&gt;Observable&lt;/code&gt;库编写我们的应用程序。&lt;/p&gt;
&lt;h4 id=&#34;其他相关生态系统中的工具&#34;&gt;其他相关生态系统中的工具&lt;/h4&gt;
&lt;p&gt;另一个与流的概念和可观察模式及其变化相关的生态系统是&lt;code&gt;CycleJs&lt;/code&gt;生态系统。 以下是&lt;code&gt;Flux&lt;/code&gt;，&lt;code&gt;Redux&lt;/code&gt;和&lt;code&gt;CycleJs&lt;/code&gt;生态系统中一些开发人员工具演示的视图。 就在此之前，我们讨论了添加工具而不是使用预先定义的架构（通过&lt;a href=&#34;https://twitter.com/andrestaltz&#34;&gt;@andaltaltz&lt;/a&gt;）。
整个&lt;a href=&#34;https://youtu.be/R-GzJgEccEQ&#34;&gt;演讲&lt;/a&gt;很棒，但如果你选择一个适合你的应用程序的架构，那么在13:03的5分钟讨论会产生一些非常有趣的观点。&lt;/p&gt;
&lt;p&gt;请注意，许多这种高级工具通常是跨生态系统进行的工作。&lt;/p&gt;
&lt;p&gt;这里要记住的最重要的事情是，看起来还有其他方法可以在不采用&lt;code&gt;store&lt;/code&gt;架构的情况下获得出色的工具。&lt;/p&gt;
&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;
&lt;p&gt;很可能&lt;code&gt;Store&lt;/code&gt;架构最初在&lt;code&gt;React&lt;/code&gt;世界中变得流行，因为它们解决了一些基本问题，&lt;code&gt;React&lt;/code&gt;只是&lt;code&gt;View&lt;/code&gt;没有（按设计）提供开箱即用的解决方案：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;为解耦组件交互提供类似&lt;code&gt;Observable&lt;/code&gt;的模式&lt;/li&gt;
&lt;li&gt;为临时&lt;code&gt;UI&lt;/code&gt;状态提供客户端容器&lt;/li&gt;
&lt;li&gt;提供缓存以避免过多的&lt;code&gt;HTTP&lt;/code&gt;请求&lt;/li&gt;
&lt;li&gt;为多个参与者提供并发数据修改的解决方案&lt;/li&gt;
&lt;li&gt;为工具提供一个钩子&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后半年到一年后，生态系统演变为仅在某些应用类型中采用&lt;code&gt;store&lt;/code&gt;而不是其他应用类型。 现在&lt;code&gt;Angular&lt;/code&gt;世界可能会发生同样的事情，结果可能是一样的。 让我们关注工具，&lt;code&gt;RxJs 5&lt;/code&gt;的主要特性之一就是提高可调试性。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>(译)Angular架构03-如何使用`Observable`数据服务构建`Angular`应用程序及常见的设计缺陷</title>
      <link>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angular%E6%9E%B6%E6%9E%8403-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8observable%E6%95%B0%E6%8D%AE%E6%9C%8D%E5%8A%A1%E6%9E%84%E5%BB%BAangular%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E5%8F%8A%E5%B8%B8%E8%A7%81%E7%9A%84%E8%AE%BE%E8%AE%A1%E7%BC%BA%E9%99%B7/</link>
      <pubDate>Wed, 17 Oct 2018 08:25:57 +0000</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angular%E6%9E%B6%E6%9E%8403-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8observable%E6%95%B0%E6%8D%AE%E6%9C%8D%E5%8A%A1%E6%9E%84%E5%BB%BAangular%E5%BA%94%E7%94%A8%E7%A8%8B%E5%BA%8F%E5%8F%8A%E5%B8%B8%E8%A7%81%E7%9A%84%E8%AE%BE%E8%AE%A1%E7%BC%BA%E9%99%B7/</guid>
      
        <description>&lt;p&gt;这篇文章是正在进行的&lt;code&gt;Angular&lt;/code&gt;架构系列的一部分，我们将在视图层和服务层一级介绍常见的设计问题和解决方案。 这是完整系列：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;视图层架构-智能组件与展示组件&lt;/li&gt;
&lt;li&gt;视图层架构-容器与展示组件常见的设计缺陷&lt;/li&gt;
&lt;li&gt;服务层架构-如何使用&lt;code&gt;Observable&lt;/code&gt;数据服务构建&lt;code&gt;Angular&lt;/code&gt;应用程序&lt;/li&gt;
&lt;li&gt;服务层架构-&lt;code&gt;Redux&lt;/code&gt;和&lt;code&gt;Ngrx Store&lt;/code&gt;-何时使用&lt;code&gt;Store&lt;/code&gt;？为什么？&lt;/li&gt;
&lt;li&gt;服务层架构-&lt;code&gt;Ngrx Store&lt;/code&gt;-架构指南&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在这篇文章中，我们将看到如何围绕&lt;code&gt;Observable&lt;/code&gt;数据服务的概念构建&lt;code&gt;Angular&lt;/code&gt;应用程序。 这是构建应用程序服务层的几种策略之一。 我们来看看以下主题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用于构建&lt;code&gt;Angular&lt;/code&gt;应用程序的替代架构&lt;/li&gt;
&lt;li&gt;什么是&lt;code&gt;Observable&lt;/code&gt;数据服务&lt;/li&gt;
&lt;li&gt;如何使用它&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RxJs&lt;/code&gt;的&lt;code&gt;subject&lt;/code&gt;以及如何使用它&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BehaviourSubject&lt;/code&gt;以及如何使用它&lt;/li&gt;
&lt;li&gt;如何构建&lt;code&gt;Observable&lt;/code&gt;数据服务&lt;/li&gt;
&lt;li&gt;要避免的陷阱&lt;/li&gt;
&lt;li&gt;结论&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果您想看一个非常简单的方法来调试&lt;code&gt;RxJs Observables&lt;/code&gt;，请看一下这篇文章 - &lt;a href=&#34;https://blog.angular-university.io/debug-rxjs/&#34;&gt;如何调试&lt;code&gt;RxJs&lt;/code&gt;——一种调试&lt;code&gt;Rxjs Observables&lt;/code&gt;的简单方法&lt;/a&gt;。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;用于构建angular应用程序的替代架构&#34;&gt;用于构建&lt;code&gt;Angular&lt;/code&gt;应用程序的替代架构&lt;/h2&gt;
&lt;p&gt;有几种可能性可用于构建&lt;code&gt;Angular&lt;/code&gt;应用程序。 最近有一种趋势，用类似于&lt;code&gt;Redux&lt;/code&gt;的风格构建具有单个原子状态的类似&lt;code&gt;Flux&lt;/code&gt;的应用程序。 以下是以这种方式构建应用程序的几种替代方法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果使用集中存储模式构建应用程序的应用程序，建议使用&lt;a href=&#34;https://blog.angular-university.io/angular-ngrx-store-and-effects-crash-course/&#34;&gt;&lt;code&gt;@ngrx/store&lt;/code&gt;指南&lt;/a&gt;。&lt;/li&gt;
&lt;li&gt;如果使用&lt;code&gt;Redux&lt;/code&gt;本身构建应用程序，请参阅此&lt;a href=&#34;https://angular-university.io/angular-2-application-architecture-building-flux-like-apps-using-redux-and-immutable-js-js/&#34;&gt;文章&lt;/a&gt;以获取更多详细信息和示例应用程序&lt;/li&gt;
&lt;li&gt;如果使用&lt;code&gt;Redux&lt;/code&gt;和单个状态原子的概念构建应用程序，但在&lt;code&gt;Rxjs&lt;/code&gt;中实现它。 请参阅此其他&lt;a href=&#34;https://angular-university.io/angular-2-application-architecture-building-applications-using-rxjs-and-functional-reactive-programming-vs-redux/&#34;&gt;文章&lt;/a&gt;，了解其执行方式和示例应用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇文章将提供一个不使用单个状态原子的替代方案，并且包含使用&lt;code&gt;Observable&lt;/code&gt;数据服务。 如果你开始使用&lt;code&gt;Observables&lt;/code&gt;和&lt;code&gt;Angular&lt;/code&gt;，你可能想看看这篇文章，我们将讨论一些常见的故障情况。&lt;/p&gt;
&lt;h2 id=&#34;什么是observable数据服务&#34;&gt;什么是&lt;code&gt;Observable&lt;/code&gt;数据服务&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Observable&lt;/code&gt;数据服务是&lt;code&gt;Angular&lt;/code&gt;可注入服务，可用于向应用程序的多个部分提供数据。 可以将名为&lt;code&gt;store&lt;/code&gt;的服务注入到需要数据的任何位置：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;App&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;todoStore&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;TodoStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
                &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;uiStateStore&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;UiStateStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在这种情况下，我们注入两个服务，一个包含应用程序数据，这是一个待办事项列表，另一个服务包含&lt;code&gt;UI&lt;/code&gt;的当前状态：例如，当前显示给用户的错误消息。&lt;/p&gt;
&lt;h2 id=&#34;如何使用observable数据服务&#34;&gt;如何使用&lt;code&gt;Observable&lt;/code&gt;数据服务&lt;/h2&gt;
&lt;p&gt;数据服务公开了一个&lt;code&gt;observable&lt;/code&gt;，例如&lt;code&gt;TodoStore&lt;/code&gt;公开了&lt;code&gt;todos observable&lt;/code&gt;。 这个&lt;code&gt;observable&lt;/code&gt;的每个值都是一个新的待办事项列表。&lt;/p&gt;
&lt;p&gt;然后可以使用数据服务直接在模板中使用&lt;code&gt;async&lt;/code&gt;管道：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ul&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;todo-list&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;li&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngFor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;let todo of todoStore.todos | async&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
        ...
    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;li&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ul&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;如何修改服务的数据&#34;&gt;如何修改服务的数据&lt;/h2&gt;
&lt;p&gt;服务中的数据通过调用它们的动作方法来修改，例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;nx&#34;&gt;onAddTodo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;todoStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addTodo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newTodo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{},&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;uiStateStore&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;endBackendAction&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;然后，数据存储将根据操作方法调用为其数据发出新值，并且所有订户将接收新值并相应地更新。&lt;/p&gt;
&lt;h3 id=&#34;关于observable数据服务的一些有趣的事情&#34;&gt;关于&lt;code&gt;Observable&lt;/code&gt;数据服务的一些有趣的事情&lt;/h3&gt;
&lt;p&gt;请注意，&lt;code&gt;TodoStore&lt;/code&gt;的用户不知道是什么触发了新的&lt;code&gt;todos&lt;/code&gt;列表：添加&lt;code&gt;todo&lt;/code&gt;，删除&lt;code&gt;todo&lt;/code&gt;或切换&lt;code&gt;todo&lt;/code&gt;状态。 &lt;code&gt;store&lt;/code&gt;的消费者只知道可以获得新值，并且视图将相应地进行调整。 这有效地解耦了应用程序的多个部分，因为数据的使用者不知道何时发生了修改。&lt;/p&gt;
&lt;p&gt;另请注意，注入存储的应用程序的智能组件没有任何状态变量，这是一件好事，因为这些是编程错误的常见来源。&lt;/p&gt;
&lt;p&gt;另外值得注意的是，智能组件中没有一个是直接使用&lt;code&gt;Http&lt;/code&gt;后端服务的，只有对存储的调用才能触发数据修改。&lt;/p&gt;
&lt;p&gt;现在我们已经了解了如何使用&lt;code&gt;Observable&lt;/code&gt;数据服务，让我们看看如何使用&lt;code&gt;RxJ&lt;/code&gt;构建。&lt;/p&gt;
&lt;h2 id=&#34;rxjs的subject以及如何使用它&#34;&gt;&lt;code&gt;RxJs&lt;/code&gt;的&lt;code&gt;subject&lt;/code&gt;以及如何使用它&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Observable&lt;/code&gt;数据服务的核心是&lt;code&gt;RxJs&lt;/code&gt;的&lt;code&gt;subject&lt;/code&gt;。 &lt;code&gt;subject&lt;/code&gt;实现&lt;code&gt;Observer&lt;/code&gt;和&lt;code&gt;Observable&lt;/code&gt;接口，这意味着我们可以使用它们来发出值以及注册订阅者。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;subject&lt;/code&gt;不仅是传统的事件总线，而且更强大，因为它为所有&lt;code&gt;RxJs&lt;/code&gt;功能操作提供了它。 但从本质上讲，我们只是使用它来订阅，就像一个常规的&lt;code&gt;observable&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;subject&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Subject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;subject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Received new subject value: &amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;但与常规&lt;code&gt;observable&lt;/code&gt;不同，&lt;code&gt;Subject&lt;/code&gt;也可用于向其订阅者发送值：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;nx&#34;&gt;subject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;Subject&lt;/code&gt;有一个特殊性，妨碍我们使用它来构建&lt;code&gt;Observable&lt;/code&gt;数据服务：如果我们订阅它，我们将得不到最后一个值，我们不得不等到应用程序的某个部分调用next()。&lt;/p&gt;
&lt;p&gt;这引起了一个问题，特别是在程序引导情况下，应用程序仍在初始化并且并非所有订户都已注册，例如并非所有&lt;code&gt;async&lt;/code&gt;管道都有机会自行注册，因为并非所有模板都已初始化。&lt;/p&gt;
&lt;h2 id=&#34;behaviorsubject以及如何使用它&#34;&gt;&lt;code&gt;BehaviorSubject&lt;/code&gt;以及如何使用它&lt;/h2&gt;
&lt;p&gt;解决方案是使用&lt;code&gt;BehaviorSubject&lt;/code&gt;。 这种类型的主题将在订阅时返回流的最后一个值，或者如果尚未发出值则返回初始状态：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;BehaviorSubject&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;BehaviorSubject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;initialState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;BehaviorSubject&lt;/code&gt;还有另一个有趣的属性：我们可以随时检索流的当前值：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;currentValue&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;behaviorSubject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这使得&lt;code&gt;BehaviorSubject&lt;/code&gt;成为&lt;code&gt;Observable&lt;/code&gt;数据服务的核心。 我们来看一个具体的例子。&lt;/p&gt;
&lt;h2 id=&#34;如何构建observable数据服务&#34;&gt;如何构建&lt;code&gt;Observable&lt;/code&gt;数据服务&lt;/h2&gt;
&lt;p&gt;您可以在&lt;a href=&#34;https://github.com/jhades/angular2-rxjs-observable-data-services/blob/master/src/state/TodoStore.ts&#34;&gt;此处&lt;/a&gt;找到&lt;code&gt;store&lt;/code&gt;的完整示例，但这是该服务中最重要的部分：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Injectable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;TodoStore&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_todos&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;BehaviorSubject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Todo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;BehaviorSubject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;([]));&lt;/span&gt;

    &lt;span class=&#34;kr&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;todos&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;List&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;Todo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_todos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;asObservable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;

    &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;todoBackendService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;TodoBackendService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;loadInitialData&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们可以看到&lt;code&gt;store&lt;/code&gt;包含一个私有成员变量&lt;code&gt;_todos&lt;/code&gt;，它是一个&lt;code&gt;BehaviorSubject&lt;/code&gt;，其初始状态为&lt;code&gt;Todos&lt;/code&gt;的空列表。&lt;/p&gt;
&lt;p&gt;构造函数注入了&lt;code&gt;Http&lt;/code&gt;后端服务，这是应用程序中使用此服务的唯一位置，应用程序的其余部分将注入&lt;code&gt;TodoStore&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;store&lt;/code&gt;在构建时被初始化，所以我们再次使用&lt;code&gt;BehaviorSubject&lt;/code&gt;非常重要，否则这将无效。&lt;/p&gt;
&lt;p&gt;但是使用额外的公共成员变量&lt;code&gt;todos&lt;/code&gt;背后的原因是什么？&lt;/p&gt;
&lt;h2 id=&#34;陷阱1---不要直接暴露subject&#34;&gt;陷阱＃1 - 不要直接暴露&lt;code&gt;subject&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;在此示例中，我们不直接将&lt;code&gt;subject&lt;/code&gt;公开给&lt;code&gt;store&lt;/code&gt;客户端，而是公开一个&lt;code&gt;observable&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这是为了防止服务客户端自己直接发出存储值而不是调用操作方法，从而绕过存储。&lt;/p&gt;
&lt;h3 id=&#34;避免event-soup&#34;&gt;避免&lt;code&gt;event soup&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;直接暴露&lt;code&gt;subject&lt;/code&gt;可能导致&lt;code&gt;event soup&lt;/code&gt;应用，其中事件以难以理解的方式链接在一起。&lt;/p&gt;
&lt;p&gt;直接访问&lt;code&gt;subject&lt;/code&gt;的内部实现细节就像返回对对象的内部数据结构的内部引用：暴露内部意味着产生对象的控制并允许第三方发出值。&lt;/p&gt;
&lt;p&gt;可能有一些有效的用例，但这很可能几乎不是预期的。&lt;/p&gt;
&lt;h2 id=&#34;写一个action方法&#34;&gt;写一个&lt;code&gt;action&lt;/code&gt;方法&lt;/h2&gt;
&lt;p&gt;在这种类型的应用程序中，操作只是&lt;code&gt;store&lt;/code&gt;提供的方法。 让我们看看如何构建&lt;code&gt;addTodo&lt;/code&gt;操作：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;nx&#34;&gt;addTodo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newTodo&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;Todo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Observable&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;obs&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;todoBackendService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;saveTodo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newTodo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;obs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_todos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_todos&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newTodo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
            &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;obs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这只是一种方法。 我们称后端服务本身返回一个可观察的成功或错误。&lt;/p&gt;
&lt;p&gt;我们订阅了相同的&lt;code&gt;observable&lt;/code&gt;，并且在成功时我们通过将新&lt;code&gt;todo&lt;/code&gt;添加到当前列表来计算新的待办事项列表。&lt;/p&gt;
&lt;h2 id=&#34;陷阱2---避免重复的http调用&#34;&gt;陷阱＃2 - 避免重复的&lt;code&gt;HTTP&lt;/code&gt;调用&lt;/h2&gt;
&lt;p&gt;在这个例子中要记住的一件事是，&lt;code&gt;Http&lt;/code&gt;的可观察返回将有两个订阅者：一个在&lt;code&gt;addTodo&lt;/code&gt;方法中，一个用户调用&lt;code&gt;addTodo&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这将导致（由于&lt;code&gt;observable&lt;/code&gt;默认工作的方式）重复的&lt;code&gt;HTTP&lt;/code&gt;调用，因为设置了两个单独的处理链。 有关此观点以及观察者可能会让我们感到惊讶的其他方式的详细信息，请参阅此&lt;a href=&#34;&#34;&gt;文章&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;要解决此问题，我们可以执行以下操作，以确保不会发生重复的&lt;code&gt;http&lt;/code&gt;调用：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;nx&#34;&gt;saveTodo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newTodo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Todo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;kd&#34;&gt;var&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Headers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;append&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;application/json; charset=utf-8&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;post&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;/todo&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;JSON&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stringify&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newTodo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;toJS&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()),{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;headers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;publishLast&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;refCount&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;要注意直接返回热可观察而不是&lt;code&gt;HTTP&lt;/code&gt;冷可观察的权衡：没有重复的网络调用，但&lt;code&gt;saveTodo&lt;/code&gt;的调用者可能无法自己执行某些操作（如重试）。&lt;/p&gt;
&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Observable&lt;/code&gt;数据服务或存储是一种简单直观的模式，可以在不引入太多新概念的情况下利用&lt;code&gt;Angular&lt;/code&gt;中功能响应式编程的强大功能。&lt;/p&gt;
&lt;p&gt;熟悉的概念，如&lt;code&gt;subject&lt;/code&gt;，基本上是一个事件总线，是这种模式的基础，这使得它比其他需要其他几个&lt;code&gt;RxJs&lt;/code&gt;构造的模式更容易学习。&lt;/p&gt;
&lt;p&gt;不直接暴露&lt;code&gt;subject&lt;/code&gt;的一些预防措施可能足以使应用程序易于理解，但这取决于用例。&lt;/p&gt;
&lt;p&gt;正如我们在陷阱部分中看到的那样，需要熟悉&lt;code&gt;RxJs&lt;/code&gt;以及可观察的工作方式。 查看以前的&lt;a href=&#34;https://angular-university.io/functional-reactive-programming-for-angular-2-developers-rxjs-and-observables/&#34;&gt;文章&lt;/a&gt;了解更多详情。&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>(译)Angular架构02-容器与展示组件常见的设计缺陷</title>
      <link>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angular%E6%9E%B6%E6%9E%8402-%E5%AE%B9%E5%99%A8%E4%B8%8E%E5%B1%95%E7%A4%BA%E7%BB%84%E4%BB%B6%E5%B8%B8%E8%A7%81%E7%9A%84%E8%AE%BE%E8%AE%A1%E7%BC%BA%E9%99%B7/</link>
      <pubDate>Mon, 15 Oct 2018 20:36:31 +0000</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angular%E6%9E%B6%E6%9E%8402-%E5%AE%B9%E5%99%A8%E4%B8%8E%E5%B1%95%E7%A4%BA%E7%BB%84%E4%BB%B6%E5%B8%B8%E8%A7%81%E7%9A%84%E8%AE%BE%E8%AE%A1%E7%BC%BA%E9%99%B7/</guid>
      
        <description>&lt;p&gt;这篇文章是正在进行的&lt;code&gt;Angular&lt;/code&gt;架构系列的一部分，我们将在视图层和服务层一级介绍常见的设计问题和解决方案。 这是完整系列：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;视图层架构-智能组件与展示组件&lt;/li&gt;
&lt;li&gt;视图层架构-容器与展示组件常见的设计缺陷&lt;/li&gt;
&lt;li&gt;服务层架构-如何使用&lt;code&gt;Observable&lt;/code&gt;数据服务构建&lt;code&gt;Angular&lt;/code&gt;应用程序&lt;/li&gt;
&lt;li&gt;服务层架构-&lt;code&gt;Redux&lt;/code&gt;和&lt;code&gt;Ngrx Store&lt;/code&gt;-何时使用&lt;code&gt;Store&lt;/code&gt;？为什么？&lt;/li&gt;
&lt;li&gt;服务层架构-&lt;code&gt;Ngrx Store&lt;/code&gt;-架构指南&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接下来我们来谈谈&lt;code&gt;Angular&lt;/code&gt;组件架构：我们将介绍非常常见的组件设计和潜在的设计问题，您可能会在应用它时遇到这些问题。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;通用组件设计以及它的潜在问题&#34;&gt;通用组件设计（以及它的潜在问题）&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Angular&lt;/code&gt;应用程序开发的一个非常重要的方面是应用程序组件设计：如何将不同类型的组件组合在一起，何时使用组件与指令，如何以及何时将组件中的功能提取到指令等。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Angular&lt;/code&gt;组件提供了许多可以以多种不同方式使用和组合的功能。 这使我们可以根据情况采用各种各样的应用程序设计。&lt;/p&gt;
&lt;p&gt;在这篇文章中，我们将讨论我们经常听到的一种特殊类型的设计。&lt;/p&gt;
&lt;h3 id=&#34;容器组件与展示组件&#34;&gt;容器组件与展示组件&lt;/h3&gt;
&lt;p&gt;我们将要讨论的设计方案是容器组件与展示组件之间的组件分离。&lt;/p&gt;
&lt;p&gt;这是一种流行的设计，现在&lt;code&gt;Angular&lt;/code&gt;生态系统中越来越多地使用它，因为现在&lt;code&gt;Angular&lt;/code&gt;支持&lt;code&gt;Component&lt;/code&gt;模型。 该设计在&lt;a href=&#34;https://twitter.com/dan_abramov&#34;&gt;Dan Abramov(@dan_abramov)&lt;/a&gt;的博客文章中介绍：&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0&#34;&gt;展示和容器组件&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;该文章适用于&lt;code&gt;React&lt;/code&gt;，但这些概念也适用于任何允许基于组件的设计模型的生态系统，如&lt;code&gt;Angular&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;容器与展示设计的一个例子&#34;&gt;容器与展示设计的一个例子&lt;/h2&gt;
&lt;p&gt;因此，让我们举一个这个设计的快速示例，请记住，不同的术语用于命名不同类型的组件。&lt;/p&gt;
&lt;p&gt;设计的核心思想是有不同类型的组件。 使用与上述博客文章相同的术语，我们有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;容器组件：这些组件知道如何从服务层检索数据。 请注意，路由的顶级组件通常是容器组件，这就是为什么这种类型的组件最初这样命名的原因&lt;/li&gt;
&lt;li&gt;展示组件 - 这些组件只是将数据作为输入，并知道如何在屏幕上显示它。 他们还可以发出自定义事件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;让我们举一个这个设计的简单例子，它实际上已经包含了潜在的设计问题。 为了让它更有趣，我建议如下：尝试在我提供示例时发现设计问题，我们将在本文后面讨论该问题。&lt;/p&gt;
&lt;p&gt;如果您已经尝试过使用此设计，很可能您遇到了这个问题。&lt;/p&gt;
&lt;h3 id=&#34;以响应式编写的顶级组件&#34;&gt;以响应式编写的顶级组件&lt;/h3&gt;
&lt;p&gt;那么让我们开始使用我们路由的顶级组件。 让我们看一下用响应式编写的简单路由顶级组件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;course-detail&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;templateUrl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;./course-detail.component.html&amp;#39;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseDetailComponent&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OnInit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;user$&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;User&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;course$&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;lessons$&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;ActivatedRoute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coursesService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;CoursesService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
     &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newsletterService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;NewsletterService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;userService&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;

  &lt;span class=&#34;nx&#34;&gt;ngOnInit() {&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user$&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;userService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

      &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course$&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;paramMap&lt;/span&gt;
          &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;params&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
          &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;switchMap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;courseUrl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coursesService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;findCourseByUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;courseUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;

      &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lessons$&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course$&lt;/span&gt;
         &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;switchMap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coursesService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;findLessonsForCourse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;email&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newsletterService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribeToNewsletter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
            &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
                &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;alert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Subscription successful ...&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
                &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这是一个简单的组件，它显示了课程的详细信息：包含一个标题，其中包含课程摘要（以及新闻通讯框）和&lt;code&gt;lesson&lt;/code&gt;列表。&lt;/p&gt;
&lt;p&gt;因此，让我们分解一下我们在这个顶级组件中所拥有的内容以及它当前的设计方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;该组件注入了路由器依赖项，但也有一些特定于应用程序的服务&lt;/li&gt;
&lt;li&gt;该组件没有任何可以直接引用数据的变量，如&lt;code&gt;lessons&lt;/code&gt;或&lt;code&gt;courses&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;相反，组件在&lt;code&gt;ngOnInit&lt;/code&gt;上声明了一些&lt;code&gt;observable&lt;/code&gt;，它们是从服务层获得的其他&lt;code&gt;observable&lt;/code&gt;派生的&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;顶级组件设计概述&#34;&gt;顶级组件设计概述&lt;/h3&gt;
&lt;p&gt;此顶级组件将根据路由标识符参数定义如何从服务层获取数据。&lt;/p&gt;
&lt;p&gt;这是响应式样式应用程序中的典型顶级组件，它不使用路由器数据预取（稍后将详细介绍）。 此组件最初将不显示任何数据，它将对服务层执行一次或多次调用以获取数据。&lt;/p&gt;
&lt;p&gt;请注意，组件只是定义了一组&lt;code&gt;Observable&lt;/code&gt;，但是在组件类中没有进行任何订阅：那么数据如何显示呢？&lt;/p&gt;
&lt;h3 id=&#34;顶级组件的模板&#34;&gt;顶级组件的模板&lt;/h3&gt;
&lt;p&gt;现在让我们看一下这个组件的模板，看看这些&lt;code&gt;observable&lt;/code&gt;是如何被使用的：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;screen-container&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;course-detail-header&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;]=&amp;#34;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;]=&amp;#34;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;#34;&lt;/span&gt;
        &lt;span class=&#34;err&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;firstName&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;]=&amp;#34;(&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt;  &lt;span class=&#34;err&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;firstName&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;)=&amp;#34;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;($&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;event&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;course-detail-header&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;

    &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;table&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;class&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;table lessons-list card card-strong&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tbody&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tr&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;ngFor&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;let lesson of (lessons$ | async)&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;....&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tbody&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;table&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
  
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们可以看到，我们正在使用&lt;code&gt;observable&lt;/code&gt;，我们通过&lt;code&gt;async&lt;/code&gt;管道订阅它们。 然后，数据将应用于存在于此路由的顶级组件下的本地组件树：&lt;/p&gt;
&lt;p&gt;多种类型的数据，包括用户，&lt;code&gt;lessons&lt;/code&gt;和&lt;code&gt;courses&lt;/code&gt;，将被传递到&lt;code&gt;course-detail-header&lt;/code&gt;组件，以及&lt;code&gt;lesson&lt;/code&gt;列表。
这些组件负责呈现顶级组件检索的数据&lt;/p&gt;
&lt;h4 id=&#34;关于多个订阅的说明&#34;&gt;关于多个订阅的说明&lt;/h4&gt;
&lt;p&gt;一件重要的事情：&lt;code&gt;lessons$&lt;/code&gt;这个&lt;code&gt;observable&lt;/code&gt;订阅了两次。 在这种情况下，它不会造成问题，因为来自服务层的&lt;code&gt;observable&lt;/code&gt;旨在防止对后端的多个请求，例如使用&lt;code&gt;publishLast().refCount()&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;请注意，这只是确保多个订阅不成问题的一种可能解决方案。 现在让我们来看看顶层组件模板中使用的其中一个本地组件。 我们将看到他们的设计非常不同。&lt;/p&gt;
&lt;h2 id=&#34;研究展示组件的设计&#34;&gt;研究展示组件的设计&lt;/h2&gt;
&lt;p&gt;顶级组件是一个容器组件，但是模板中使用的其他组件呢？&lt;/p&gt;
&lt;p&gt;展示组件将负责获取输入数据并将其呈现给用户。 例如，&lt;code&gt;course-detail-header&lt;/code&gt;是一个展示组件。 我们来看看这个组件的样子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;course-detail-header&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;h2&amp;gt;{{course?.description}}&amp;lt;/h2&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;h5&amp;gt;Total lessons: {{lessons?.length}}&amp;lt;/h5&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;newsletter [firstName]=&amp;#34;firstName&amp;#34; (subscribe)=&amp;#34;onSubscribe($event)&amp;#34;&amp;gt;&amp;lt;/newsletter&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
  &lt;span class=&#34;nx&#34;&gt;changeDetection&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;ChangeDetectionStrategy.OnPush&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseDetailHeaderComponent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;kd&#34;&gt;@Input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;kd&#34;&gt;@Input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
    &lt;span class=&#34;kd&#34;&gt;@Input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;firstName&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;kd&#34;&gt;@Output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;EventEmitter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;onSubscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;email&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;emit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;提醒：尝试通过此设计发现问题&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;正如我们所看到的，组件将一些数据作为&lt;code&gt;input&lt;/code&gt;，然后呈现给屏幕。 此组件与应用程序的服务层没有依赖关系，而是通过&lt;code&gt;input&lt;/code&gt;接收其数据。&lt;/p&gt;
&lt;p&gt;它还发出&lt;code&gt;output&lt;/code&gt;，例如订阅&lt;code&gt;output&lt;/code&gt;事件。 但是这个事件来自哪里？ 我们可以看到，这是为了响应来自简报组件的具有相同名称的事件而被触发。&lt;/p&gt;
&lt;p&gt;新闻通讯组件的外观如何，它是如何设计的？ 我们来看一下。&lt;/p&gt;
&lt;h2 id=&#34;展示组件更深一层组件树&#34;&gt;展示组件更深一层组件树&lt;/h2&gt;
&lt;p&gt;新闻通讯组件也是一个展示组件，因为它需要输入，显示订阅表单并在订阅时发出事件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;newsletter&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;fieldset class=&amp;#34;newsletter&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;legend&amp;gt;Newsletter&amp;lt;/legend&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;h5&amp;gt;Hello {{firstName}}, enter your email below to subscribe:&amp;lt;/h5&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;form&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        &amp;lt;input #email type=&amp;#34;email&amp;#34; name=&amp;#34;email&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        &amp;lt;input  type=&amp;#34;button&amp;#34; class=&amp;#34;button button-primary&amp;#34; value=&amp;#34;Subscribe&amp;#34;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;               (click)=&amp;#34;subscribeToNewsletter(email)&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;/form&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;/fieldset&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;styleUrls&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;./newsletter.component.css&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;NewsletterComponent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

    &lt;span class=&#34;kd&#34;&gt;@Input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;firstName&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;kd&#34;&gt;@Output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;EventEmitter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;subscribeToNewsletter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;emailField&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;emit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;emailField&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;emailField&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;因此，这是我们目前为新闻通讯组件设计的设计，所以让我们更详细地查看它，看看问题可能是什么。&lt;/p&gt;
&lt;h2 id=&#34;这个设计的潜在问题&#34;&gt;这个设计的潜在问题&lt;/h2&gt;
&lt;p&gt;您可能已经注意到新闻稿组件中的一些与&lt;code&gt;course-detail-header&lt;/code&gt;组件类似的内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;input&lt;/code&gt;属性&lt;code&gt;firstName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;output&lt;/code&gt;事件&lt;code&gt;subscribe&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在这两个组件中重复这两个元素。 所以看起来我们在这个设计中做了一些事情，这些事情可能无法在更大的组件树中很好地扩展，因为涉及很多重复。&lt;/p&gt;
&lt;p&gt;让我们回顾一下这两个问题，看看我们怎么可能设计这个问题。&lt;/p&gt;
&lt;h2 id=&#34;设计问题1---中间组件中的外来属性&#34;&gt;设计问题1 - 中间组件中的外来属性&lt;/h2&gt;
&lt;p&gt;看起来我们正在传递像&lt;code&gt;firstName&lt;/code&gt;这样的&lt;code&gt;input&lt;/code&gt;而不是本地组件树，以便像新闻稿组件这样的叶子组件使用它们。 但是中间组件本身并没有使用&lt;code&gt;input&lt;/code&gt;，它们只是将它们传递给子组件。&lt;/p&gt;
&lt;p&gt;通常，本地组件树比此示例大得多，因此该问题可能会导致大量重复输入。&lt;/p&gt;
&lt;p&gt;此外，更重要的是：如果我们使用第三方窗口小部件库并且我们使用其中一些组件作为中间组件，我们可能无法通过组件树传递所有必要的数据，具体取决于库的设计方式。&lt;/p&gt;
&lt;p&gt;还有另一个与产出有关的类似问题。&lt;/p&gt;
&lt;h2 id=&#34;设计问题2---在本地组件树上冒泡的自定义事件&#34;&gt;设计问题2 - 在本地组件树上冒泡的自定义事件&lt;/h2&gt;
&lt;p&gt;正如我们所看到的，订阅事件也在组件树的多个层次上重复，这是因为设计自定义事件不会冒泡。&lt;/p&gt;
&lt;p&gt;所以在这里我们还有一个代码重复问题，对于一个更大的例子，它不能很好地扩展，并且不能与第三方库一起工作 - 在这种情况下我们无法应用这种技术。&lt;/p&gt;
&lt;p&gt;此外，订阅新闻通讯（对&lt;code&gt;newsletterService&lt;/code&gt;的调用）的逻辑是在顶级路由组件上，而不是在新闻通讯组件上。&lt;/p&gt;
&lt;p&gt;这是因为只有顶级组件才能访问服务层，但最终导致该组件可能会保留很多逻辑。&lt;/p&gt;
&lt;p&gt;那么我们如何在&lt;code&gt;Angular&lt;/code&gt;中解决这些问题呢？ 我们来看看可能的解决方案。&lt;/p&gt;
&lt;h2 id=&#34;防止自定义事件冒泡&#34;&gt;防止自定义事件冒泡&lt;/h2&gt;
&lt;p&gt;如果我们发现自己处于组件树中手动冒泡事件的情况，那么对于某些更简单的情况可能会很好。 但是，如果事件冒泡/外来属性开始变得难以维护的话，这里有一个替代方案。&lt;/p&gt;
&lt;p&gt;我们将通过一步一步的重构来呈现替代方案。 让我们再次从顶级组件开始重构，看看新解决方案如何避免我们发现的问题。&lt;/p&gt;
&lt;p&gt;如果您想查看与此类似的重构版本的视频版本，请查看此&lt;a href=&#34;https://youtu.be/9m3_HHeP9Ko&#34;&gt;视频(Youtube)&lt;/a&gt;。&lt;/p&gt;
&lt;h3 id=&#34;重构的顶级组件&#34;&gt;重构的顶级组件&lt;/h3&gt;
&lt;p&gt;让我们更改顶级组件，使其不再传递尽可能多的数据或从本地组件树接收尽可能多的事件。 我们还删除新闻订阅逻辑。&lt;/p&gt;
&lt;p&gt;新版本的顶级组件现在拥有的代码比以前少得多：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;course-detail&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;templateUrl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;./course-detail.component.html&amp;#39;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseDetailComponent&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OnInit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;course$&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;lessons$&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;ActivatedRoute&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
              &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coursesService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;CoursesService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;

  &lt;span class=&#34;nx&#34;&gt;ngOnInit() {&lt;/span&gt;

      &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course$&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;route&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;paramMap&lt;/span&gt;
         &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;params&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;params&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;id&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;
         &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;switchMap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;courseUrl&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coursesService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;findCourseByUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;courseUrl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;

      &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lessons$&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course$&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;switchMap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coursesService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;findLessonsForCourse&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;所以这看起来是一个好的开始。 那么顶级组件模板呢？ 重构后新模板大部分相同，但&lt;code&gt;course-detail-header&lt;/code&gt;组件除外：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;course-detail-header&lt;/span&gt;  &lt;span class=&#34;err&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;]=&amp;#34;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;#34;&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;]=&amp;#34;&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;$&lt;/span&gt; &lt;span class=&#34;err&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;async&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;course-detail-header&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这看起来比我们以前的版本好：我们看不到&lt;code&gt;firstName&lt;/code&gt;的传递或者&lt;code&gt;subscribe&lt;/code&gt;事件的冒泡。&lt;/p&gt;
&lt;p&gt;那么在重构之后，&lt;code&gt;course-detail-header&lt;/code&gt;中间组件现在是什么样子？&lt;/p&gt;
&lt;h3 id=&#34;重构的中间组件&#34;&gt;重构的中间组件&lt;/h3&gt;
&lt;p&gt;我们在这里可以看到，在重构之后，新版本的&lt;code&gt;course-detail-header&lt;/code&gt;组件现在变得更加简单：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;course-detail-header&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;h2&amp;gt;{{course?.description}}&amp;lt;/h2&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;h5&amp;gt;Total lessons: {{lessons?.length}}&amp;lt;/h5&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;newsletter&amp;gt;&amp;lt;/newsletter&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;changeDetection&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;ChangeDetectionStrategy.OnPush&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseDetailHeaderComponent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;kd&#34;&gt;@Input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;kd&#34;&gt;@Input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;该组件的新版本仍包含新闻通讯，但它不再是冒泡事件并传递组件本身不需要的数据。&lt;/p&gt;
&lt;p&gt;所以这看起来比我们最初提出的版本要好很多。 但现在订阅新闻通讯的功能在哪里？&lt;/p&gt;
&lt;p&gt;现在让我们看看这个重构中的最后一个组件：叶子组件。&lt;/p&gt;
&lt;h3 id=&#34;重构的叶子组件&#34;&gt;重构的叶子组件&lt;/h3&gt;
&lt;p&gt;正如我们所看到的，新闻通讯组件现在以完全不同的方式设计：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;newsletter&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;fieldset class=&amp;#34;newsletter&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;legend&amp;gt;Newsletter&amp;lt;/legend&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;h5&amp;gt;Hello {{firstName}}, enter your email below to subscribe:&amp;lt;/h5&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;form&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        &amp;lt;input #email type=&amp;#34;email&amp;#34; name=&amp;#34;email&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        &amp;lt;input  type=&amp;#34;button&amp;#34; class=&amp;#34;button button-primary&amp;#34; value=&amp;#34;Subscribe&amp;#34;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;               (click)=&amp;#34;subscribeToNewsletter(email)&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;/form&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;/fieldset&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;NewsletterComponent&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OnInit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;firstName&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;userService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newsletterService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;NewsletterService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;ngOnInit() {&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;userService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;firstName&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;firstName&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;subscribeToNewsletter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;email&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newsletterService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribeToNewsletter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
            &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
                &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;alert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Subscription successful ...&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
                &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;那么现在这个新版新闻通讯组件的最大设计差异是什么呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;最大的区别实际上是这个新版本看起来很像容器组件！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;正如我们所看到的，有时最好的解决方案是将服务深入到组件树中。 这真的简化了本例中涉及的所有多个组件。&lt;/p&gt;
&lt;p&gt;但是叶子组件的这种实现可以进一步改进，所以让我们进一步详细介绍这个设计，看看如何。&lt;/p&gt;
&lt;h3 id=&#34;查看新的组件设计解决方案&#34;&gt;查看新的组件设计解决方案&lt;/h3&gt;
&lt;p&gt;这个特定组件树的新设计似乎更易于维护。 不再冒泡自定义事件或通过组件树传递无关的&lt;code&gt;input&lt;/code&gt;属性。&lt;/p&gt;
&lt;p&gt;新闻通讯组件现在知道服务层，正在从中获取所有数据。 它引用了新闻通讯服务，因此可以直接调用它。 请注意，如果需要，此组件仍可以接收输入，稍后将详细说明。&lt;/p&gt;
&lt;h3 id=&#34;利用angular功能来获得更简单的设计&#34;&gt;利用&lt;code&gt;Angular&lt;/code&gt;功能来获得更简单的设计&lt;/h3&gt;
&lt;p&gt;我们可以看到，在组件树的这个新版本中，我们现在正在利用&lt;code&gt;Angular&lt;/code&gt;依赖注入系统在本地组件树中深入注入服务。&lt;/p&gt;
&lt;p&gt;这允许深度嵌套的组件（如新闻通讯组件）直接从服务层接收数据，而不必通过输入接收数据。&lt;/p&gt;
&lt;p&gt;这使得顶级组件和中间组件都更简单，并避免代码重复。 它还允许将与服务层交互的逻辑深深地放入组件树中，如果这是最有意义的话。&lt;/p&gt;
&lt;h4 id=&#34;当前新闻通讯组件实现的一个问题&#34;&gt;当前新闻通讯组件实现的一个问题&lt;/h4&gt;
&lt;p&gt;这个新版本的新闻通讯组件只有一个问题：与以前版本的表现组件不同：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这个新版本不适用于OnPush变化检测！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;使新闻通讯组件与onpush兼容&#34;&gt;使新闻通讯组件与&lt;code&gt;OnPush&lt;/code&gt;兼容&lt;/h2&gt;
&lt;p&gt;在此之前您可能已经注意到，当我们切换组件以使用&lt;code&gt;OnPush&lt;/code&gt;更改检测时，事情就会停止工作 - 即使我们没有在组件级别进行本地数据变动。&lt;/p&gt;
&lt;p&gt;其中一个例子是当前版本的新闻通讯组件，它确实是不会反映模板上名字的新版本。&lt;/p&gt;
&lt;p&gt;但是这里有一个与&lt;code&gt;OnPush&lt;/code&gt;兼容的组件版本：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;newsletter&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;changeDetection&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;ChangeDetectionStrategy.OnPush&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;fieldset class=&amp;#34;newsletter&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;legend&amp;gt;Newsletter&amp;lt;/legend&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;h5&amp;gt;Hello {{firstName$ | async }}, enter your email below to subscribe:&amp;lt;/h5&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    ...
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;&amp;lt;/fieldset&amp;gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;NewsletterComponent&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OnInit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;firstName$&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Observable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;;&lt;/span&gt;

    &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;userService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;UserService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newsletterService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;NewsletterService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;ngOnInit() {&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;firstName$&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;userService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;firstName&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;subscribeToNewsletter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;email&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newsletterService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribeToNewsletter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;email&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
            &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
                &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;alert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;Subscription successful ...&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
                &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;那么这个新实现有什么不同呢？ 在这个版本的组件中，我们定义了一个名为&lt;code&gt;firstName$&lt;/code&gt;的&lt;code&gt;observable&lt;/code&gt;，我们已经使用&lt;code&gt;async&lt;/code&gt;管道在模板中使用了它。&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;async&lt;/code&gt;管道将确保在发出新版本的&lt;code&gt;firstName&lt;/code&gt;时（例如，当用户登录时）重新呈现组件，即使组件没有&lt;code&gt;input&lt;/code&gt; - 因为&lt;code&gt;sync&lt;/code&gt;管道将检测到&lt;code&gt;observable&lt;/code&gt;发出了一个新值，因此它将标记该组件以进行重新渲染。&lt;/p&gt;
&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;
&lt;p&gt;因此，我们可以看到有许多可能的组件设计，具体取决于具体情况。 如果需要，使用&lt;code&gt;Angular&lt;/code&gt;依赖注入系统确实可以很容易地在组件树中深入注入服务。&lt;/p&gt;
&lt;p&gt;因此，我们不一定必须通过组件树的多个级别传递数据和事件，因为这可能会因代码重复（以及其他问题）而导致可维护性问题。&lt;/p&gt;
&lt;p&gt;但是，为什么在尝试应用容器+展示设计时，这种情况最终会如此发生？&lt;/p&gt;
&lt;h3 id=&#34;因为自定义事件冒泡问题&#34;&gt;因为自定义事件冒泡问题&lt;/h3&gt;
&lt;p&gt;这个设计可能最终被使用的一个主要原因可能是：在软件设计中，我们提供的名称可能会产生很大的影响。&lt;/p&gt;
&lt;p&gt;而容器组件这个名称让我们认为只有路由的顶层组件应该具有这种类型的设计，并且它所使用的所有其他组件应该是展示性的，而事实并非如此。&lt;/p&gt;
&lt;p&gt;容器这个名称并没有让我们想到像新闻通讯组件那样的叶子组件。&lt;/p&gt;
&lt;p&gt;所以为了避免这个问题，这里有一个建议：如果我们需要为知道服务层的组件命名，并且有一个名称有助于应用程序设计讨论，我们可以调用它们，例如智能组件， 并将术语容器保留为路由的顶级组件。&lt;/p&gt;
&lt;p&gt;在实践中，根据需要混合和匹配多种类型的组件设计实际上更加实用，并且在必要时在树的不同级别使用不同类型的组件 - 根据需要混合不同的功能。&lt;/p&gt;
&lt;p&gt;我希望你喜欢这篇文章，并且它有助于开始使用视图层设计。 请务必查看上面架构系列上的其他文章！&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>(译)Angular架构01-智能组件与展示组件</title>
      <link>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angular%E6%9E%B6%E6%9E%8401-%E6%99%BA%E8%83%BD%E7%BB%84%E4%BB%B6%E4%B8%8E%E5%B1%95%E7%A4%BA%E7%BB%84%E4%BB%B6/</link>
      <pubDate>Mon, 15 Oct 2018 14:04:46 +0000</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angular%E6%9E%B6%E6%9E%8401-%E6%99%BA%E8%83%BD%E7%BB%84%E4%BB%B6%E4%B8%8E%E5%B1%95%E7%A4%BA%E7%BB%84%E4%BB%B6/</guid>
      
        <description>&lt;p&gt;这篇文章是正在进行的&lt;code&gt;Angular&lt;/code&gt;架构系列的一部分，我们将在视图层和服务层一级介绍常见的设计问题和解决方案。 这是完整系列：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;视图层架构-智能组件与展示组件&lt;/li&gt;
&lt;li&gt;视图层架构-容器与展示组件常见的设计缺陷&lt;/li&gt;
&lt;li&gt;服务层架构-如何使用&lt;code&gt;Observable&lt;/code&gt;数据服务构建&lt;code&gt;Angular&lt;/code&gt;应用程序&lt;/li&gt;
&lt;li&gt;服务层架构-&lt;code&gt;Redux&lt;/code&gt;和&lt;code&gt;Ngrx Store&lt;/code&gt;-何时使用&lt;code&gt;Store&lt;/code&gt;？为什么？&lt;/li&gt;
&lt;li&gt;服务层架构-&lt;code&gt;Ngrx Store&lt;/code&gt;-架构指南&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;视图层架构简介&#34;&gt;视图层架构简介&lt;/h2&gt;
&lt;p&gt;在构建&lt;code&gt;Angular&lt;/code&gt;应用程序时，我们在开始时遇到的最常见问题是：我们如何构建应用程序？&lt;/p&gt;
&lt;p&gt;我们可能会找到的直接答案是：我们将所有内容拆分为组件！ 但我们很快发现还有更多问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有哪些类型的组件？&lt;/li&gt;
&lt;li&gt;组件应如何互动？&lt;/li&gt;
&lt;li&gt;我应该将服务注入任何组件吗？&lt;/li&gt;
&lt;li&gt;如何使我的组件可以跨视图重用？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们将尝试通过将组件基本上分成两种类型来解答这个问题和其他问题（但还有更多内容）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;智能组件：有时也称为应用程序级组件，容器组件或控制器组件&lt;/li&gt;
&lt;li&gt;展示组件：有时也称为纯组件或哑组件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;让我们找出这两种组件之间的差异，我们何时应该使用它们以及为什么！&lt;/p&gt;
&lt;p&gt;你也可以通过&lt;a href=&#34;https://youtu.be/TzrZNpSfswA&#34;&gt;视频&lt;/a&gt;学习，它将一个组件重构为两个组件，视频中的示例我们将在下面描述。&lt;/p&gt;
&lt;h2 id=&#34;将应用程序拆分为不同类型的组件&#34;&gt;将应用程序拆分为不同类型的组件&lt;/h2&gt;
&lt;p&gt;为了理解两种类型组件之间的区别，让我们从一个简单的应用程序开始，其中尚不存在组件分离。&lt;/p&gt;
&lt;p&gt;我们开始构建应用程序的主屏幕，并为单个模板添加了多个功能：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;app-home&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;h2&amp;gt;All Lessons&amp;lt;/h2&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;h4&amp;gt;Total Lessons: {{lessons?.length}}&amp;lt;/h4&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;div class=&amp;#34;lessons-list-container v-h-center-block-parent&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        &amp;lt;table class=&amp;#34;table lessons-list card card-strong&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;            &amp;lt;tbody&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;            &amp;lt;tr *ngFor=&amp;#34;let lesson of lessons&amp;#34; (click)=&amp;#34;selectLesson(lesson)&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;                &amp;lt;td class=&amp;#34;lesson-title&amp;#34;&amp;gt; {{lesson.description}} &amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;                &amp;lt;td class=&amp;#34;duration&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;                    &amp;lt;i class=&amp;#34;md-icon duration-icon&amp;#34;&amp;gt;access_time&amp;lt;/i&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;                    &amp;lt;span&amp;gt;{{lesson.duration}}&amp;lt;/span&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;                &amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;            &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;            &amp;lt;/tbody&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;        &amp;lt;/table&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;    &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;styleUrls&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;./home.component.css&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;HomeComponent&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OnInit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessonsService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;LessonsService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

  &lt;span class=&#34;nx&#34;&gt;ngOnInit() {&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lessonsService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;findAllLessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
          &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
          &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allLessons&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;
          &lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

  &lt;span class=&#34;nx&#34;&gt;selectLesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;理解问题&#34;&gt;理解问题&lt;/h3&gt;
&lt;p&gt;虽然这个主页组件仍然非常简单，但它已经开始具有相当大的尺寸。 例如，我们已经实现了一个包含&lt;code&gt;lesson&lt;/code&gt;列表的表。&lt;/p&gt;
&lt;p&gt;但是应用程序的其他部分可能还需要此功能，例如，假设我们有另一个屏幕显示给定&lt;code&gt;course&lt;/code&gt;的目录。&lt;/p&gt;
&lt;p&gt;在该屏幕中，我们还希望显示&lt;code&gt;lesson&lt;/code&gt;列表，但仅显示属于该&lt;code&gt;course&lt;/code&gt;的&lt;code&gt;lesson&lt;/code&gt;。 在这种情况下，我们需要的东西与我们在主屏幕中实现的内容非常相似。&lt;/p&gt;
&lt;p&gt;我们不应该只是跨组件复制粘贴，我们应该创建一个可重用的组件。&lt;/p&gt;
&lt;h2 id=&#34;让我们创建一个展示组件&#34;&gt;让我们创建一个展示组件&lt;/h2&gt;
&lt;p&gt;在这种情况下我们想要做的是将屏幕的表部分提取到一个单独的组件中，让我们称之为&lt;code&gt;LessonsListComponent&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OnInit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;EventEmitter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;@angular/core&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;../shared/model/lesson&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;lessons-list&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;      &amp;lt;table class=&amp;#34;table lessons-list card card-strong&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          &amp;lt;tbody&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          &amp;lt;tr *ngFor=&amp;#34;let lesson of lessons&amp;#34; (click)=&amp;#34;selectLesson(lesson)&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;              &amp;lt;td class=&amp;#34;lesson-title&amp;#34;&amp;gt; {{lesson.description}} &amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;              &amp;lt;td class=&amp;#34;duration&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;                  &amp;lt;i class=&amp;#34;md-icon duration-icon&amp;#34;&amp;gt;access_time&amp;lt;/i&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;                  &amp;lt;span&amp;gt;{{lesson.duration}}&amp;lt;/span&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;              &amp;lt;/td&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          &amp;lt;/tr&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          &amp;lt;/tbody&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;      &amp;lt;/table&amp;gt;  
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;  `&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;styleUrls&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;./lessons-list.component.css&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonsListComponent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

  &lt;span class=&#34;kd&#34;&gt;@Input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;

  &lt;span class=&#34;kd&#34;&gt;@Output&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;lesson&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;lessonEmitter&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;EventEmitter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;();&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;selectLesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lessonEmitter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;emit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;现在让我们仔细看看这个组件：它没有通过构造函数注入&lt;code&gt;lesson&lt;/code&gt;服务。 相反，它通过&lt;code&gt;@Input&lt;/code&gt;接收输入属性中的&lt;code&gt;lessons&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这意味着组件本身不知道&lt;code&gt;lessons&lt;/code&gt;的来源：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lessons&lt;/code&gt;可能是所有&lt;code&gt;lessons&lt;/code&gt;的清单&lt;/li&gt;
&lt;li&gt;或者&lt;code&gt;lessons&lt;/code&gt;可能是特定&lt;code&gt;course&lt;/code&gt;所有&lt;code&gt;lessons&lt;/code&gt;的清单&lt;/li&gt;
&lt;li&gt;甚至&lt;code&gt;lessons&lt;/code&gt;可能是任何给定搜索列表中的页面&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们可以在所有这些场景中重用此组件，因为&lt;code&gt;lesson-list&lt;/code&gt;组件不知道数据来自何处。 组件的责任纯粹是向用户呈现数据而不是从特定位置获取数据。&lt;/p&gt;
&lt;p&gt;这就是我们通常将这种类型的组件称为展示组件的原因。 但是&lt;code&gt;Home&lt;/code&gt;组件发生了什么？&lt;/p&gt;
&lt;h2 id=&#34;让我们创建一个智能组件&#34;&gt;让我们创建一个智能组件&lt;/h2&gt;
&lt;p&gt;如果我们回到&lt;code&gt;Home&lt;/code&gt;组件，这就是重构后的样子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OnInit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;@angular/core&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;LessonsService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;../shared/model/lessons.service&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;../shared/model/lesson&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;app-home&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;      &amp;lt;h2&amp;gt;All Lessons&amp;lt;/h2&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;      &amp;lt;h4&amp;gt;Total Lessons: {{lessons?.length}}&amp;lt;/h4&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;      &amp;lt;div class=&amp;#34;lessons-list-container v-h-center-block-parent&amp;#34;&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;          &amp;lt;lessons-list [lessons]=&amp;#34;lessons&amp;#34; (lesson)=&amp;#34;selectLesson($event)&amp;#34;&amp;gt;&amp;lt;/lessons-list&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;      &amp;lt;/div&amp;gt;
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;styleUrls&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;./home.component.css&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;HomeComponent&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OnInit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessonsService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;LessonsService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

  &lt;span class=&#34;nx&#34;&gt;ngOnInit() {&lt;/span&gt;
     &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

  &lt;span class=&#34;nx&#34;&gt;selectLesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们可以看到，我们已经用新的可重用&lt;code&gt;lessons-list&lt;/code&gt;组件替换了主屏幕的列表部分。 主组件仍然知道如何从服务中检索&lt;code&gt;lesson&lt;/code&gt;列表，以及这是什么类型的列表（如果这些&lt;code&gt;lesson&lt;/code&gt;是某个&lt;code&gt;course&lt;/code&gt;的&lt;code&gt;lesson&lt;/code&gt;等）。&lt;/p&gt;
&lt;p&gt;但Home组件不知道如何向用户提供&lt;code&gt;lesson&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;home组件是什么类型的组件&#34;&gt;Home组件是什么类型的组件？&lt;/h2&gt;
&lt;p&gt;让我们给这种类型的组件命名，类似于home组件，它是一个特定于应用程序的组件：让我们称之为智能组件。&lt;/p&gt;
&lt;p&gt;这种类型的组件固有地绑定到应用程序本身，因此我们可以看到它在构造函数中接收一些特定于应用程序的依赖项，如&lt;code&gt;LessonsService&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在另一个应用程序中使用此组件将非常困难。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们视图的顶级组件可能永远是智能组件。 即使我们将数据加载转移到路由器数据解析器，该组件仍然至少必须将&lt;code&gt;ActivatedRoute&lt;/code&gt;服务注入其中。&lt;/p&gt;
&lt;p&gt;因此，我们希望通过使用一组展示组件在内部编写来实现顶级智能组件。 这就是那么简单。&lt;/p&gt;
&lt;h2 id=&#34;智能组件和展示组件之间的典型交互&#34;&gt;智能组件和展示组件之间的典型交互&lt;/h2&gt;
&lt;p&gt;我们在这里看到的示例非常频繁，我们让智能组件通过&lt;code&gt;@Input&lt;/code&gt;将数据注入展示组件，并接收展示组件可能通过&lt;code&gt;@Output&lt;/code&gt;触发的任何操作。&lt;/p&gt;
&lt;p&gt;在这种情况下，我们使用自定义&lt;code&gt;lesson&lt;/code&gt;事件来指示我们已在列表中选择了给定&lt;code&gt;lesson&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;@Output&lt;/code&gt;，展示组件通过明确定义的界面与智能组件保持隔离：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;lesson-list&lt;/code&gt;展示组件只知道它发出了一个事件，但不知道事件的接收者是什么，或接收者为响应事件做了什么&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Home&lt;/code&gt;智能组件订阅&lt;code&gt;lesson&lt;/code&gt;自定义事件并对事件做出反应，但它不知道是什么触发了事件。 用户是否双击&lt;code&gt;lesson&lt;/code&gt;列表或用户是否单击了视图按钮？ 这对智能组件是透明的。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以这一切都清楚简单，这里可能出现什么问题？&lt;/p&gt;
&lt;h2 id=&#34;拆分智能与展示组件的明确方法&#34;&gt;拆分智能与展示组件的明确方法？&lt;/h2&gt;
&lt;p&gt;有了这个，我们可能会在这一点上得出结论，构建我们的应用程序就像使所有顶级组件成为智能组件一样简单，并使用本地展示组件树构建它们。&lt;/p&gt;
&lt;p&gt;但问题是，它有时并不那么简单，因为像&lt;code&gt;lesson&lt;/code&gt;这样的自定义事件不会冒泡。 因此，如果您有一个深层组件，并且您希望上面有多个级别的组件来了解该事件，则该事件不会冒泡。&lt;/p&gt;
&lt;h2 id=&#34;导致自定义事件不冒泡的问题是什么&#34;&gt;导致自定义事件不冒泡的问题是什么？&lt;/h2&gt;
&lt;p&gt;假设&lt;code&gt;lesson&lt;/code&gt;列表和&lt;code&gt;Home&lt;/code&gt;组件之间只有一层嵌套，我们有几个级别：&lt;code&gt;lesson&lt;/code&gt;列表位于选项卡面板内的可折叠面板内。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;lesson&lt;/code&gt;列表仍然希望通过&lt;code&gt;lesson&lt;/code&gt;事件通知&lt;code&gt;Home&lt;/code&gt;组件已选择&lt;code&gt;lesson&lt;/code&gt;。 但是中间&lt;code&gt;TabPanel&lt;/code&gt;和&lt;code&gt;CollapsiblePanel&lt;/code&gt;中的两个组件是非特定于应用程序的展示组件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;想象它们是Angular Material库的组成部分！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这些展示组件不了解&lt;code&gt;lesson&lt;/code&gt;事件，因此无法将其冒泡。 那么我们如何实现这一点，以及为什么自定义事件不能简单地冒泡？&lt;/p&gt;
&lt;h2 id=&#34;为什么自定义事件不会冒泡就像点击dom事件一样&#34;&gt;为什么自定义事件不会冒泡，就像点击DOM事件一样？&lt;/h2&gt;
&lt;p&gt;这不是偶然的，它是设计的，可能是为了避免&lt;code&gt;event soup&lt;/code&gt;场景，使用类似于&lt;code&gt;AngularJs&lt;/code&gt;的&lt;code&gt;$scope.$emit()&lt;/code&gt;和&lt;code&gt;$scope.$broadcast()&lt;/code&gt;的服务总线的解决方案往往会意外创建。&lt;/p&gt;
&lt;p&gt;这些类型的机制往往最终会在应用程序的不同位置之间创建紧密的依赖关系，这些依赖关系不应该彼此意识到，事件最终会被触发多次或依次触发，只看一个文件时就不明显了。&lt;/p&gt;
&lt;p&gt;因此，展示组件的自定义事件只能由其父组件可见，而不能在更深的组件树可见。&lt;/p&gt;
&lt;p&gt;如果由于某种原因我们确实需要冒泡行为，我们仍然可以使用&lt;code&gt;Javascript&lt;/code&gt;原生的&lt;code&gt;element.dispatchEvent()&lt;/code&gt;实现它。 但大多数情况下，这不是我们想要实施的。&lt;/p&gt;
&lt;h2 id=&#34;那么我们如何解决选项卡面板场景中可折叠面板内lesson列表的情况呢&#34;&gt;那么我们如何解决选项卡面板场景中可折叠面板内&lt;code&gt;lesson&lt;/code&gt;列表的情况呢？&lt;/h2&gt;
&lt;p&gt;我们仍然应该为&lt;code&gt;lesson&lt;/code&gt;列表创建一个展示组件。 提供&lt;code&gt;lesson&lt;/code&gt;的功能可以被隔离，因此&lt;code&gt;LessonsListComponent&lt;/code&gt;的版本仍然适用，它只是在应用程序的各个地方使用的东西。 但是这个列表如何通知&lt;code&gt;Home&lt;/code&gt;组件？&lt;/p&gt;
&lt;p&gt;为此，有几种解决方案。 我们应该研究的一个解决方案，特别是在构建大规模应用程序时，应该研究像&lt;code&gt;ngrx/store&lt;/code&gt;这样的解决方案。&lt;/p&gt;
&lt;p&gt;但即使使用&lt;code&gt;store&lt;/code&gt;解决方案，我们也可能不希望在展示组件中注入&lt;code&gt;store&lt;/code&gt;。 因为选择&lt;code&gt;lesson&lt;/code&gt;的结果可能并不总是将事件分派给&lt;code&gt;store&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;为了简化示例，让我们首先创建一个专门的类似于&lt;code&gt;store&lt;/code&gt;的服务来解决这个&lt;code&gt;lesson&lt;/code&gt;选择问题：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Injectable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonSelectedService&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

    &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;_selected&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;BehaviorSubject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;BehaviorSubject&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

    &lt;span class=&#34;kr&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;selected$&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_selected&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;asObservable&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    
    
    &lt;span class=&#34;nx&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
         &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;_selected&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;next&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;正如我们所看到的，&lt;code&gt;LessonSelectedService&lt;/code&gt;公开了一个可观察的&lt;code&gt;selected$&lt;/code&gt;，它将在每次选择新&lt;code&gt;lesson&lt;/code&gt;时发出一个值。&lt;/p&gt;
&lt;p&gt;请注意，我们在服务内部创建了一个&lt;code&gt;subject&lt;/code&gt;，但我们没有将它暴露给外部。 这是因为&lt;code&gt;subject&lt;/code&gt;本质上是一个事件总线，因此我们希望控制谁可以在服务中发出事件。&lt;/p&gt;
&lt;p&gt;如果我们公开&lt;code&gt;subject&lt;/code&gt;，我们会给应用程序的任何其他部分代表服务发出事件的能力，这是应该避免的。&lt;/p&gt;
&lt;p&gt;那么如何使用这个服务，因为我们不能将它注入&lt;code&gt;LessonsListComponent&lt;/code&gt;，对吧？ 我们将讨论该部分，现在让我们首先看看如何在&lt;code&gt;Home&lt;/code&gt;组件中使用这个新服务。&lt;/p&gt;
&lt;h2 id=&#34;在home组件中使用新服务&#34;&gt;在&lt;code&gt;Home&lt;/code&gt;组件中使用新服务&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Home&lt;/code&gt;组件将做的是，它将在其构造函数中注入新组件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({...})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;HomeComponent&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OnInit&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
        &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessonsService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;LessonsService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
        &lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessonSelectedService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;LessonSelectedService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

  &lt;span class=&#34;nx&#34;&gt;ngOnInit() {&lt;/span&gt;
      &lt;span class=&#34;p&#34;&gt;....&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lessonSelectedService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;selected$&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;subscribe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;selectLesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

  &lt;span class=&#34;nx&#34;&gt;selectLesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;正如我们所看到的，我们订阅了&lt;code&gt;selected$&lt;/code&gt;这个&lt;code&gt;Observable&lt;/code&gt;，它发出了新的&lt;code&gt;lesson&lt;/code&gt;，我们触发了组件的特定逻辑来处理选择。&lt;/p&gt;
&lt;p&gt;但请注意，&lt;code&gt;Home&lt;/code&gt;组件不知道&lt;code&gt;lesson&lt;/code&gt;列表，它只知道应用程序的其他部分触发了&lt;code&gt;lesson&lt;/code&gt;选择。 应用程序的两个部分仍然是隔离的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;选择的&lt;code&gt;Emitter&lt;/code&gt;不知道&lt;code&gt;Home&lt;/code&gt;组件&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Home&lt;/code&gt;组件不知道该&lt;code&gt;lesson&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;两个角色只知道&lt;code&gt;LessonSelectedService&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以我们已经解决了这个问题吗？ 还没有，因为我们仍然不想在&lt;code&gt;LessonListComponent&lt;/code&gt;中注入新服务，这将使它成为一个智能组件，我们希望将它保持为展示组件。 那么如何解决这个问题呢？&lt;/p&gt;
&lt;h2 id=&#34;如何将lessonslistcomponent保持为展示组件&#34;&gt;如何将&lt;code&gt;LessonsListComponent&lt;/code&gt;保持为展示组件？&lt;/h2&gt;
&lt;p&gt;实际上，解决问题的一种方法是使其成为智能组件;-)我们可以得出结论，在该表存在的应用程序的任何地方，我们总是想要触发对&lt;code&gt;LessonSelectedService&lt;/code&gt;的调用。&lt;/p&gt;
&lt;p&gt;这将使&lt;code&gt;lesson-list&lt;/code&gt;组件成为应用程序特定组件，无论如何它可能已经存在。 例如，我们可能不会发布此组件并在多个应用程序中使用它。&lt;/p&gt;
&lt;p&gt;因此，这将解决问题，这意味着像&lt;code&gt;Home&lt;/code&gt;组件这样的顶级应用程序组件可能由不仅仅是展示组件的组件树组成。&lt;/p&gt;
&lt;h2 id=&#34;智能组件不仅仅是顶级组件&#34;&gt;智能组件不仅仅是顶级组件&lt;/h2&gt;
&lt;p&gt;智能组件不必仅是顶级路由器组件。 我们可以看到树中可能还有其他组件也会注入像&lt;code&gt;LessonSelectedService&lt;/code&gt;这样的服务，并且不一定只从&lt;code&gt;@Input()&lt;/code&gt;获取它们的数据。&lt;/p&gt;
&lt;h2 id=&#34;将lessonslistcomponent保持为展示组件的另一种解决方案&#34;&gt;将&lt;code&gt;LessonsListComponent&lt;/code&gt;保持为展示组件的另一种解决方案&lt;/h2&gt;
&lt;p&gt;解决问题的另一种方法是保持&lt;code&gt;lesson-list&lt;/code&gt;组件不变，并在需要的地方使用它。 但在这种情况下，我们可以将它包装在一个智能组件中，该组件将注入&lt;code&gt;LessonSelectedService&lt;/code&gt;：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kd&#34;&gt;@Component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;custom-lessons-list&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;template&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;       &amp;lt;lessons-list [lessons]=&amp;#34;lessons&amp;#34; (lesson)=&amp;#34;selectLesson($event)&amp;#34;&amp;gt;&amp;lt;/lessons-list&amp;gt;  
&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;  `&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CustomLessonsListComponent&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

   &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;private&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessonSelectedService&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;LessonSelectedService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

   &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

   &lt;span class=&#34;nx&#34;&gt;selectLesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
       &lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lessonSelectedService&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;select&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; 
   &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在此我们创建了一个包装器智能组件，并将其称为&lt;code&gt;CustomLessonsListComponent&lt;/code&gt;。 在这种情况下，我们包装了我们自己的展示组件，但我们也可以包装来自第三方库的组件。&lt;/p&gt;
&lt;p&gt;想象一下&lt;code&gt;MyCustomCountrySelectDropdown&lt;/code&gt;，它包含一个通用下拉列表并使用来自具体服务的数据注入它。&lt;/p&gt;
&lt;h2 id=&#34;如何确定要构建的组件&#34;&gt;如何确定要构建的组件？&lt;/h2&gt;
&lt;p&gt;在开始构建我们的应用程序时，并不总是很明显区分什么是智能组件，什么是展示组件。&lt;/p&gt;
&lt;p&gt;那么如何在许多组件中拆分应用程序？ 即使页面的标题只使用一次，它应该是一个组件吗？&lt;/p&gt;
&lt;p&gt;组织和可读性是创建组件的唯一原因，即使它仅在一个地方使用。 在较小的文件中分隔内容有助于保持代码库的可维护性，并且使用&lt;code&gt;Angular CLI&lt;/code&gt;在创建新组件时没有任何开销：使用单个命令，我们有一个工作组件，可以在几秒钟内粘贴标头。&lt;/p&gt;
&lt;h2 id=&#34;如何进行组件设计&#34;&gt;如何进行组件设计&lt;/h2&gt;
&lt;p&gt;解决这个问题的一种方法是避免从一开始就定义什么是组件以及什么类型：我们可以从仅使用纯HTML和第三方组件构建顶级组件开始。&lt;/p&gt;
&lt;p&gt;只有当模板开始变大时，我们才开始将其分解为组件。 如果在屏幕的多个部分中使用了某些内容并且总是触发给定的操作（如调用&lt;code&gt;store&lt;/code&gt;调度），我们可能会考虑重构为较小的智能组件。&lt;/p&gt;
&lt;p&gt;如果稍后我们意识到我们需要呈现与我们刚刚创建的智能组件相同的数据，我们可以将展示部分从它中提取到展示组件中。&lt;/p&gt;
&lt;p&gt;获得一组精心设计的组件的最佳方法是通过连续重构，这可以通过使用&lt;code&gt;Angular CLI&lt;/code&gt;无负担地完成。&lt;/p&gt;
&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;
&lt;p&gt;在构建应用程序时，我们可以寻找机会将纯展示逻辑提取到展示组件中：这些只使用&lt;code&gt;@Input&lt;/code&gt;和&lt;code&gt;Output&lt;/code&gt;，并且在我们需要隔离展示逻辑并重用它时非常有用。&lt;/p&gt;
&lt;p&gt;如果我们想要保持两个组件分离并且彼此不知道，则可以使用共享服务或&lt;code&gt;store&lt;/code&gt;来完成组件树中的不同级别的智能组件之间或甚至兄弟之间的通信。&lt;/p&gt;
&lt;p&gt;但我们可能还希望将组件完全注入到彼此中并创建紧密耦合，有时这是最佳解决方案。 在这种情况下，通过例如&lt;code&gt;@ViewChild&lt;/code&gt;将组件相互注入可能是最好的方法。&lt;/p&gt;
&lt;h3 id=&#34;智能与展示组件是有用的区别&#34;&gt;智能与展示组件是有用的区别&lt;/h3&gt;
&lt;p&gt;一般而言，智能与展示组件之间的区别是非常好记的，但它可能不适用于应用程序的所有组件。&lt;/p&gt;
&lt;p&gt;我们可以拥有一个既知道服务又可以在树中更深层次地呈现一些数据的小组件，就像在&lt;code&gt;lesson&lt;/code&gt;选择时调用&lt;code&gt;store&lt;/code&gt;的&lt;code&gt;lesson&lt;/code&gt;列表。&lt;/p&gt;
&lt;p&gt;将此组件进一步拆分为智能和展示组件可能并非总是必要的。&lt;/p&gt;
&lt;p&gt;智能与展示组件在自我省问时促进我们的思维方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这种展示逻辑在应用程序的其他地方是否有用？&lt;/li&gt;
&lt;li&gt;将事情进一步分解会有用吗？&lt;/li&gt;
&lt;li&gt;我们是否在应用中创建了意外的紧耦合？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们不一定需要将构建的每个组件的所有渲染逻辑提取到单独的展示组件中。 它更多的是在任何给定时间构建对我们的应用程序最有意义的组件，并且如果需要在&lt;code&gt;CLI&lt;/code&gt;的简单连续迭代过程中进行重构。&lt;/p&gt;
&lt;p&gt;我希望你喜欢这篇文章，并且它有助于开始使用视图层设计。 请务必查看上面链接的架构系列上的其他文章！&lt;/p&gt;
</description>
      
    </item>
    
    <item>
      <title>(译)Angular NgRx Entity完全实用指南</title>
      <link>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angular-ngrx-entity%E5%AE%8C%E5%85%A8%E5%AE%9E%E7%94%A8%E6%8C%87%E5%8D%97/</link>
      <pubDate>Fri, 12 Oct 2018 14:48:58 +0000</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/web/angular/%E8%AF%91angular-ngrx-entity%E5%AE%8C%E5%85%A8%E5%AE%9E%E7%94%A8%E6%8C%87%E5%8D%97/</guid>
      
        <description>&lt;p&gt;使用 NgRx 构建我们的应用程序时，我们要做的第一件事就是&lt;strong&gt;确定在&lt;code&gt;store&lt;/code&gt;中存储数据的最佳格式&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我们需要在任何 NgRx 应用程序中处理集中存储中的业务数据，但如果我们必须提出自己的临时解决方案，则该过程&lt;em&gt;可能是重复且耗时&lt;/em&gt;的。&lt;/p&gt;
&lt;p&gt;我们经常发现自己为不同类型的数据手写完全相同的 reducer 逻辑和 selector，这很容易出错并且会减慢开发过程。&lt;/p&gt;
&lt;p&gt;在这篇文章中，我们将了解 NgRx 实体如何真正帮助我们处理&lt;code&gt;store&lt;/code&gt;中的业务数据。&lt;/p&gt;
&lt;p&gt;我们将&lt;strong&gt;详细了解 NgRx 实体及其使用的实体状态格式的价值主张是什么&lt;/strong&gt;，我们将准确了解 NgRx 实体解决的问题，并知道何时使用它以及为什么使用它。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;前言&#34;&gt;前言&lt;/h2&gt;
&lt;p&gt;请注意，这篇文章建立在其他&lt;code&gt;store&lt;/code&gt;概念的基础上，例如action，reducers 和 selectors。 如果您正在寻找 NgRx &lt;code&gt;store&lt;/code&gt;架构的介绍，请看看这篇文章：&lt;a href=&#34;https://blog.angular-university.io/angular-2-redux-ngrx-rxjs/&#34;&gt;Angular Service Layers: Redux, RxJs and Ngrx Store - When to Use a Store And Why?&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;如果您正在寻找帮助设置 NgRx 开发环境的指南，包括 DevTools，带集成 router 的 time travelling 调试器和 NgRx Store Freeze，请查看：&lt;a href=&#34;https://blog.angular-university.io/angular-ngrx-devtools/&#34;&gt;Angular Ngrx DevTools: Important Practical Tips&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;所以，不用多说了，让我们开始深入了解我们的 NgRx 实体！ 让我们从头开始，首先了解什么是实体。&lt;/p&gt;
&lt;h2 id=&#34;什么是实体entity&#34;&gt;什么是实体(Entity)？&lt;/h2&gt;
&lt;p&gt;在 NgRx 中，我们在&lt;code&gt;store&lt;/code&gt;中存储不同类型的状态，这通常包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;业务数据，例如在线课程平台的 Course 或 Lesson&lt;/li&gt;
&lt;li&gt;一些 UI 状态，例如用户设置 UI&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;实体代表某种业务数据，因此 Course 和 Lesson 就是实体类型的示例。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在我们的代码中，实体被定义为Typescript类型。 例如，在在线课程系统中，最重要的实体是 Course 和 Lesson，使用这两种自定义对象类型定义：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Course&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;iconUrl?&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;courseListIcon?&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;longDescription?&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;categories&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;lessonsCount?&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;promo?&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Lesson&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;duration&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;courseId?&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;videoId?&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;实体唯一标识符&#34;&gt;实体唯一标识符&lt;/h3&gt;
&lt;p&gt;我们可以看到，两个实体都有一个名为id的唯一标识符字段，可以是字符串或数字。 这是一个技术标识符，对于给定的实体实例是唯一的：例如，没有两个课程具有相同的ID。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我们存储在&lt;code&gt;store&lt;/code&gt;中的大多数数据都是实体！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;如何在store中存储实体集合&#34;&gt;如何在&lt;code&gt;store&lt;/code&gt;中存储实体集合？&lt;/h2&gt;
&lt;p&gt;让我们假设我们想在内存&lt;code&gt;store&lt;/code&gt;中存储一系列课程：我们将如何做到这一点？一种方法是在 &lt;code&gt;courses&lt;/code&gt; 属性下将 courses 存储在一个数组中。&lt;/p&gt;
&lt;p&gt;完整的&lt;code&gt;store&lt;/code&gt;状态看起来像这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;courses&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
      &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Angular Ngrx Course&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;BEGINNER&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Angular for Beginners&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;BEGINNER&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;2&lt;/span&gt;      
    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;Angular Security Course - Web Security Fundamentals&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;ADVANCED&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;3&lt;/span&gt;      
    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Angular Tutorial For Beginners - Build Your First App - Hello World Step By Step&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;4:17&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;seqNo&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;courseId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Building Your First  Component - Component Composition&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2:07&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;seqNo&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;courseId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;        
     &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;   
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;为什么不在数组中存储相关实体&#34;&gt;为什么不在数组中存储相关实体？&lt;/h3&gt;
&lt;p&gt;我们首先想到的是以数组的形式在&lt;code&gt;store&lt;/code&gt;中存储实体，但这种方法可能会导致几个潜在的问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果我们想根据它的已知id查找课程，我们将不得不遍历整个集合，这对于非常大的集合来说可能是低效的&lt;/li&gt;
&lt;li&gt;更重要的是，通过使用数组，我们可能会意外地在数组中存储相同课程的不同版本（具有相同的id）&lt;/li&gt;
&lt;li&gt;如果我们将所有实体存储为数组，我们的 reducers 对于每个实体看起来几乎相同&lt;/li&gt;
&lt;li&gt;例如，假设将新实体添加到集合中的简单情况。 我们将重新实现几次完全相同的逻辑，用于向集合中添加新实体并重新排序数组以获取特定的自定义排序顺序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们可以看到，我们将实体存储在&lt;code&gt;store&lt;/code&gt;中的格式对我们的程序有很大影响。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;然后让我们试着找出在&lt;code&gt;store&lt;/code&gt;中存储实体的理想格式。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;设计实体store状态数组或映射&#34;&gt;设计实体&lt;code&gt;store&lt;/code&gt;状态：数组或映射？&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;store&lt;/code&gt;的一个角色是充当内存客户端数据库，该数据库包含整个数据库的一部分，我们从客户端通过 selector 派生我们的视图模型。&lt;/p&gt;
&lt;p&gt;这与传统的设计相反，后者包括通过 API 调用从服务器引入视图模型。 因为存储是内存数据库，所以将业务实体存储在它们自己的内存数据库“表”中是有意义的，并为它们提供类似于主键的唯一标识符。&lt;/p&gt;
&lt;p&gt;然后可以将数据扁平化，并使用实体唯一标识符链接在一起，就像在数据库中一样。&lt;/p&gt;
&lt;p&gt;一种很好的建模方法是将实体集合存储在 Javascript 对象的形式下，就像映射一样。 在此设置中，实体的键将是唯一ID，值将是整个对象。&lt;/p&gt;
&lt;p&gt;在这种新格式中，整个&lt;code&gt;store&lt;/code&gt;状态的是这样的：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;courses&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Angular Ngrx Course&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;BEGINNER&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;
           &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
        &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Angular for Beginners&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;BEGINNER&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;2&lt;/span&gt;                  
        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
        &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Angular Security Course - Web Security Fundamentals&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;BEGINNER&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
              &lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;3&lt;/span&gt;                  
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Angular Tutorial For Beginners - Build Your First App - Hello World Step By Step&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;s2&#34;&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;4:17&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;s2&#34;&gt;&amp;#34;seqNo&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;courseId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
        &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Building Your First  Component - Component Composition&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;s2&#34;&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2:07&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;s2&#34;&gt;&amp;#34;seqNo&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;courseId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;....&lt;/span&gt;
        &lt;span class=&#34;mi&#34;&gt;35&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;35&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Unidirectional Data Flow And The Angular Development Mode&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;s2&#34;&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;7:07&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;s2&#34;&gt;&amp;#34;seqNo&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
            &lt;span class=&#34;nx&#34;&gt;courseId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;0&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;设计id查找的状态&#34;&gt;设计id查找的状态&lt;/h3&gt;
&lt;p&gt;我们可以看到，这种格式使得通过id查找实体非常简单，这是一种非常常见的操作。 例如，为了查找id为1的 course，我们只需编写：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;courses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;它还使状态变得扁平化，使得组合多个实体并通过 selector 查询“连接”它们变得更加简单。 但是只有一个问题：我们丢失了关于集合&lt;em&gt;顺序&lt;/em&gt;的信息！&lt;/p&gt;
&lt;p&gt;这是因为与数组不同，Javascript 对象的属性没有关联它们的顺序。 是否有任何仍然通过id在映射中存储我们的数据，并仍保留有关顺序信息的方法？&lt;/p&gt;
&lt;h3 id=&#34;设计保存实体顺序的状态&#34;&gt;设计保存实体顺序的状态&lt;/h3&gt;
&lt;p&gt;是的，我们只需要同时使用映射和数组！ 我们将对象存储在一个映射（称为 entities）中，然后将顺序信息存储在一个数组中（称为 ids）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;courses&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
            &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Angular Ngrx Course&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;BEGINNER&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;                      
               &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
            &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
            &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Angular for Beginners&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;BEGINNER&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;2&lt;/span&gt;                                            
            &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
            &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Angular Security Course - Web Security Fundamentals&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;BEGINNER&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                  &lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;3&lt;/span&gt;                                            
            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;35&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
            &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Angular Tutorial For Beginners - Build Your First App - Hello World Step By Step&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;s2&#34;&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;4:17&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;s2&#34;&gt;&amp;#34;seqNo&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;nx&#34;&gt;courseId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;
            &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
            &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Building Your First  Component - Component Composition&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;s2&#34;&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2:07&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;s2&#34;&gt;&amp;#34;seqNo&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;nx&#34;&gt;courseId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;
            &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
            &lt;span class=&#34;p&#34;&gt;....&lt;/span&gt;
            &lt;span class=&#34;mi&#34;&gt;35&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;35&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;Unidirectional Data Flow And The Angular Development Mode&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;s2&#34;&gt;&amp;#34;duration&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;7:07&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;s2&#34;&gt;&amp;#34;seqNo&amp;#34;&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;6&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                &lt;span class=&#34;nx&#34;&gt;courseId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;0&lt;/span&gt;
            &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;实体状态格式&#34;&gt;实体状态格式&lt;/h3&gt;
&lt;p&gt;这种状态格式将实体映射与id数组合在一起称为实体状态格式(Entity State format)。&lt;/p&gt;
&lt;p&gt;这是将业务实体存储在集中式存储中的理想格式，但是如果我们必须从头开始手动编写它们，则在编写 reducer 和 selector 时保持此状态会带来额外的负担。&lt;/p&gt;
&lt;p&gt;例如，如果我们必须编写一些类型定义来表示完整的存储状态，它们看起来像这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;StoreState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;courses&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;CourseState&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;LessonsState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;


&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CoursesState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
     &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
     &lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; 

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonsState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
     &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[];&lt;/span&gt;
     &lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;key&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;正如我们所看到的，我们已经在这里做了一些重复工作，因为类型 CoursesState 和 LessonsState 几乎相同。 更重要的是，这两个实体的所有 reducer 和 selector 代码也非常相似。&lt;/p&gt;
&lt;h3 id=&#34;编写支持实体状态格式的reducer&#34;&gt;编写支持实体状态格式的reducer&lt;/h3&gt;
&lt;p&gt;例如，一个用于 LoadCourse action 的 reducer，它接受当前的 CoursesState，并为其添加一个新的 course，并根据 seqNo 字段重新排序该集合。&lt;/p&gt;
&lt;p&gt;以下就是 LoadCourse action 的 reducer 逻辑：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialCoursesState&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;CoursesState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[],&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sortBySeqNo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;e1&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;e2&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;e1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;e2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coursesReducer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
   &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialCoursesState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
    &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CoursesState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;COURSE_LOADED&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
             &lt;span class=&#34;c1&#34;&gt;// push new id to array and re-order
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;             &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;slice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
             &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
             &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sortBySeqNo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
             &lt;span class=&#34;c1&#34;&gt;// build a new courses state
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;             &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                 &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                 &lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                     &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
                     &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt;
                 &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
             &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; 
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;正如我们所看到的，只需在&lt;code&gt;store&lt;/code&gt;中添加 course 即可。 问题是我们必须为其他常见操作编写类似的代码，例如更新&lt;code&gt;store&lt;/code&gt;中的 course 或删除它。&lt;/p&gt;
&lt;h3 id=&#34;避免重复的-reducer-逻辑&#34;&gt;避免重复的 reducer 逻辑&lt;/h3&gt;
&lt;p&gt;但是比这更大的问题是，将一个 Lesson 加载到 LessonsState 中的等效 LoadLesson 操作的代码几乎相同：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialLessonsState&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;LessonsState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[],&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sortBySeqNo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;e1&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;e2&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;e1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;e2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessonsReducer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialLessonsState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
    &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;LessonActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonsState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;LESSON_LOADED&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
             &lt;span class=&#34;c1&#34;&gt;// push new id to array and re-order
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;             &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;slice&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
             &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
             &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sortBySeqNo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
             &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                 &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
                 &lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                     &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
                     &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;
                 &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
             &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; 
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;除了使用 Lesson 类型而不是 Course 之外，此代码几乎与我们之前编写的 reducer 逻辑完全一样！&lt;/p&gt;
&lt;p&gt;我们可以看到，将我们的实体保留在这个双数组和映射场景中会产生大量重复代码。&lt;/p&gt;
&lt;h3 id=&#34;避免重复的selector逻辑&#34;&gt;避免重复的selector逻辑&lt;/h3&gt;
&lt;p&gt;除了重复的类型定义，重复的初始状态和几乎相同的 reducer 逻辑之外，我们还会有很多几乎相同的 selector 逻辑。&lt;/p&gt;
&lt;p&gt;例如，以下是 Course 实体的一些常用selector，它选择&lt;code&gt;store&lt;/code&gt;中可用的所有 course：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;selectCoursesState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 
      &lt;span class=&#34;nx&#34;&gt;createFeatureSelector&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;CoursesState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;courses&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;selectAllCourses&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;createSelector&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;selectCoursesState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;coursesState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allCourses&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coursesState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;allCourses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sortBySeqNo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allCourses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;功能selector的快速说明&#34;&gt;功能selector的快速说明&lt;/h3&gt;
&lt;p&gt;注意 selectCoursesState 功能 selector，这是一个辅助 selector，它只接受整个&lt;code&gt;store&lt;/code&gt;状态的 &lt;code&gt;courses&lt;/code&gt; 属性，如下所示：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;nx&#34;&gt;storeState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;courses&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;使用此实用程序的优点是类型安全，并且可以很容易地定义延迟加载的 selector，这些 selector 无法访问根 &lt;code&gt;store&lt;/code&gt; 状态的类型定义。&lt;/p&gt;
&lt;p&gt;selector selectAllCourses 获取 &lt;code&gt;store&lt;/code&gt; 中的所有 courses 并将它们放入数组中，并根据 seqNo 字段对数组进行排序。&lt;/p&gt;
&lt;p&gt;问题是我们需要一些几乎相同的逻辑用于 Lesson 实体：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;selectLessonsState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 
      &lt;span class=&#34;nx&#34;&gt;createFeatureSelector&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;LessonsState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;lessons&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;selectAllLessons&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;createSelector&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;selectLessonsState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;lessonsState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allLessons&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;Object&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;values&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lessonsState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;entities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;allLessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sort&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sortBySeqNo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allLessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们可以看到，这段代码几乎与我们之前为 Course 实体编写的 selector 完全相同。&lt;/p&gt;
&lt;h3 id=&#34;大量重复代码&#34;&gt;大量重复代码&lt;/h3&gt;
&lt;p&gt;让我们总结一下到目前为止我们看到的几乎相同的代码类型：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实体状态定义（如 CoursesState 和 LessonsState）&lt;/li&gt;
&lt;li&gt;初始reducer状态（如 initialCoursesState 和 initialLessonsState）&lt;/li&gt;
&lt;li&gt;reducer 逻辑&lt;/li&gt;
&lt;li&gt;selector 逻辑&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大量的重复代码，只是为了将这种采用优化的实体状态格式的数据保存在我们的数据库中。 问题是，这是在&lt;code&gt;store&lt;/code&gt;中存储相关实体的理想格式，如果我们不使用它，我们可能最终会遇到其他问题。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;好消息是我们可以通过利用NgRx实体来避免几乎所有这些重复的代码！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;什么是-ngrx-实体何时使用它&#34;&gt;什么是 NgRx 实体，何时使用它？&lt;/h2&gt;
&lt;p&gt;NgRx 实体是一个小型库，可帮助我们将实体保持在这种理想的实体状态格式（ID数组加上实体映射）。&lt;/p&gt;
&lt;p&gt;该库旨在与NgRx Store结合使用，实际上是 NgRx 生态系统的关键部分。 从我们的项目开始使用 NgRx 实体，而不是尝试使用我们自己的特殊内存数据库格式，这样做要好得多。&lt;/p&gt;
&lt;p&gt;现在让我们学习 NgRx 实体提供给我们编写 NgRx 应用程序的许多方法。&lt;/p&gt;
&lt;h3 id=&#34;定义实体状态&#34;&gt;定义实体状态&lt;/h3&gt;
&lt;p&gt;回到我们的 Course 实体，让我们现在使用 NgRx 实体重新定义实体状态：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CoursesState&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;EntityState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这与我们之前编写的类型定义相同，但我们现在不必为每个单独的实体定义 id 和 entities 属性。 相反，我们可以简单地从 EntityState 继承，同时具有相同的类型安全性，以及更少的代码。&lt;/p&gt;
&lt;h2 id=&#34;ngrx实体适配器entity-adapter&#34;&gt;NgRx实体适配器(Entity Adapter)&lt;/h2&gt;
&lt;p&gt;为了能够使用NgRx Entity的其他功能，我们需要首先创建一个实体适配器。 适配器是一个实用程序类，它提供了一系列实用程序函数，旨在更简单地操作实体状态。&lt;/p&gt;
&lt;p&gt;适配器允许我们以更简单的方式编写所有初始实体状态，reducers 和 selector，同时仍然将我们的实体保持为标准的 EntityState 格式。&lt;/p&gt;
&lt;p&gt;以下是 Course 实体的适配器，配置为使用 seqNo 字段对实体进行排序：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt; : &lt;span class=&#34;kt&#34;&gt;EntityAdapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 
   &lt;span class=&#34;nx&#34;&gt;createEntityAdapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;({&lt;/span&gt;
       &lt;span class=&#34;nx&#34;&gt;sortComparer&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;sortBySeqNo&lt;/span&gt;
   &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;定义默认实体排序顺序&#34;&gt;定义默认实体排序顺序&lt;/h2&gt;
&lt;p&gt;请注意，我们使用了可选的 sortComparer 属性，该属性用于设置 Course 实体的排序顺序，这将决定该实体的id数组的顺序。&lt;/p&gt;
&lt;p&gt;如果我们不使用此可选属性，则将使用id字段对 Course 进行排序。&lt;/p&gt;
&lt;h2 id=&#34;使用-ngrx-实体编写更简单的-reducer&#34;&gt;使用 NgRx 实体编写更简单的 reducer&lt;/h2&gt;
&lt;p&gt;现在让我们使用适配器并使用它来定义我们的 Reducer 所需的初始状态。&lt;/p&gt;
&lt;p&gt;然后我们将实现与以前相同的 reducer 逻辑：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialCoursesState&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;CoursesState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 
      &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getInitialState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessonsReducer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialLessonsState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
    &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;LessonActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonsState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes.LESSON_LOADED&lt;/span&gt;:
             &lt;span class=&#34;kt&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; 
            &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;注意现在使用适配器编写 reducer 逻辑会更容易。 适配器将帮助我们操作现有的 CourseState，方法是在 addOne 中调用我们之前手动执行的所有操作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;addOne 将创建现有状态对象的副本，而不是改变现有状态&lt;/li&gt;
&lt;li&gt;然后 addOne 将创建一个 ids 数组的副本，它将在正确的排序位置添加新的 Course&lt;/li&gt;
&lt;li&gt;将创建实体对象的副本，该副本指向所有先前的 Course 对象，而无需通过深层副本重新创建这些对象&lt;/li&gt;
&lt;li&gt;新实体对象将添加新 Course&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;使用实体适配器的好处&#34;&gt;使用实体适配器的好处&lt;/h3&gt;
&lt;p&gt;正如我们所看到的，通过使用适配器编写我们的 reducer，我们可以节省大量的工作并避免常见的 reducer 逻辑错误，因为这种类型的逻辑很容易出错。&lt;/p&gt;
&lt;p&gt;偶然地改变存储状态并不常见，这可能会导致问题，特别是如果我们在我们的应用程序中使用&lt;code&gt;OnPush&lt;/code&gt;更改检测。&lt;/p&gt;
&lt;p&gt;使用适配器可以防止所有这些问题，同时减少编写 reducer 所需的大量代码。&lt;/p&gt;
&lt;h3 id=&#34;ngrx-entity适配器支持的操作&#34;&gt;NgRx Entity适配器支持的操作&lt;/h3&gt;
&lt;p&gt;除了 addOne 之外，NgRx 实体适配器还支持一系列常见的集合修改操作，否则我们必须亲自实现。&lt;/p&gt;
&lt;p&gt;以下是所有受支持操作的完整示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coursesReducer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialCoursesState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
    &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CoursesState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ADD_COURSE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UPSERT_COURSE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;upsertOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ADD_COURSES&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;courses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UPSERT_COURSES&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;upsertMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;courses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UPDATE_COURSE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;updateOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UPDATE_COURSES&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;updateMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;courses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;DELETE_COURSE&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;removeOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;DELETE_COURSES&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;removeMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;LOAD_COURSES&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;courses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;CLEAR_COURSES&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;removeAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;适配器方法的行为方式如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;addOne：向集合中添加一个实体&lt;/li&gt;
&lt;li&gt;addMany：添加多个实体&lt;/li&gt;
&lt;li&gt;addAll：用新的集合替换整个集合&lt;/li&gt;
&lt;li&gt;removeOne：删除一个实体&lt;/li&gt;
&lt;li&gt;removeMany：删除多个实体&lt;/li&gt;
&lt;li&gt;removeAll：清除整个集合&lt;/li&gt;
&lt;li&gt;updateOne：更新一个现有实体&lt;/li&gt;
&lt;li&gt;updateMany：更新多个现有实体&lt;/li&gt;
&lt;li&gt;upsertOne：更新或插入一个实体&lt;/li&gt;
&lt;li&gt;upsertMany：更新或插入多个实体&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;现在想象一下，如果我们必须自己实现所有这些 reducer 逻辑，那会是什么样的！&lt;/p&gt;
&lt;h2 id=&#34;使用ngrx-entity-selectors&#34;&gt;使用NgRx Entity Selectors&lt;/h2&gt;
&lt;p&gt;NgRx 实体帮助我们的另一件事是使用常用的 selector，例如 selectAllCourses 和 selectAllLessons。&lt;/p&gt;
&lt;p&gt;通过运行以下命令，我们可以随时生成一系列常用的 selector：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectEntities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectIds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectTotal&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getSelectors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这些 selector 都可以直接在我们的组件中使用，也可以作为构建其他 selector 的起点。&lt;/p&gt;
&lt;p&gt;请注意，这些 selector 都以与实体无关的方式命名，因此如果在同一文件中需要多个 selector，建议按以下方式导入它们：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fromCourses&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;./courses.reducers&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;// 这相当于我们之前手动编写的 selectAllCourses
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;selectAllCourses&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fromCourses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;selectAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这些 selector 随时可以使用，并且与我们自己手动编写的 selector 一样安全。&lt;/p&gt;
&lt;h2 id=&#34;ngrx实体的目的不是为了做什么&#34;&gt;NgRx实体的目的不是为了做什么&lt;/h2&gt;
&lt;p&gt;请注意，尽管 NgRx Entity 使得编写 Course 实体的状态，reducer 和 selector 逻辑变得更加容易，但我们仍然必须编写 reducer 函数本身，尽管这样使用适配器的方式更简单。&lt;/p&gt;
&lt;p&gt;使用 NgRx 实体不会避免必须为每个实体编写 reducer 逻辑，尽管这使它更简单。&lt;/p&gt;
&lt;p&gt;这意味着对于 Lesson 实体，我们必须做一些非常相似的事情。 惯例是将所有这些密切相关的代码直接放在我们定义了实体 reducer 函数的同一文件中。&lt;/p&gt;
&lt;p&gt;对于 Lesson 实体，这就是完整的 lesson.reducers.ts 文件的样子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonsState&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;EntityState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt; : &lt;span class=&#34;kt&#34;&gt;EntityAdapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;createEntityAdapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;({&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;sortComparer&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;sortBySeqNo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;


&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialLessonsState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getInitialState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessonsReducer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialLessonsState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;LessonActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonsState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

  &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActionTypes.LESSON_LOADED&lt;/span&gt;:
             &lt;span class=&#34;kt&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectEntities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectIds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectTotal&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getSelectors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;实际上，每个实体的 reducer 逻辑略有不同，因此 reducer 功能之间不会重复代码。&lt;/p&gt;
&lt;p&gt;如果您正在寻找更进一步的解决方案，并且无需编写实体特定的 reducer 逻辑，请查看&lt;a href=&#34;https://github.com/johnpapa/angular-ngrx-data&#34;&gt;ngrx-data&lt;/a&gt;。&lt;/p&gt;
&lt;h2 id=&#34;配置自定义唯一id字段&#34;&gt;配置自定义唯一ID字段&lt;/h2&gt;
&lt;p&gt;正如我们所提到的，我们程序中的实体都应该有一个名为id的技术标识符字段。 但如果由于某种原因，这个领域：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在给定实体中不可用&lt;/li&gt;
&lt;li&gt;或者它有不同的名称&lt;/li&gt;
&lt;li&gt;或者我们只是想使用恰好是 natural key 的另一个属性&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们仍然可以通过向适配器提供自定义 idselector 功能来实现。 这是一个例子：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt; : &lt;span class=&#34;kt&#34;&gt;EntityAdapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 
      &lt;span class=&#34;nx&#34;&gt;createEntityAdapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;({&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;sortComparer&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;sortBySeqNo&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;selectId&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;lesson&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;courseId&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;-&amp;#39;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;seqNo&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;适配器将调用此函数以从给定实体中提取唯一键。&lt;/p&gt;
&lt;p&gt;在此示例中，我们通过将 courseId 属性与 Lesson 序号连接来为 Lesson 实体创建唯一标识符，该序号对于给定的 Lesson 是唯一的。&lt;/p&gt;
&lt;h3 id=&#34;处理自定义状态属性&#34;&gt;处理自定义状态属性&lt;/h3&gt;
&lt;p&gt;到目前为止，我们只是通过扩展 EntityState 类型来定义我们的实体状态。 但可能我们的实体状态还具有除标准ID和实体之外的其他自定义属性。&lt;/p&gt;
&lt;p&gt;假设对于 Course 实体，我们还需要一个额外的标志来指示 courses 是否已经加载。 我们可以在 CoursesState 中定义额外的状态属性，然后使用适配器在 reducer 逻辑中更新该属性。&lt;/p&gt;
&lt;p&gt;以下是 CoursesState reducer 文件 courses.reducers.ts 的完整示例，现在包括额外的 state 属性：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CoursesState&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;EntityState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;allCoursesLoaded&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;boolean&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt; : &lt;span class=&#34;kt&#34;&gt;EntityAdapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;createEntityAdapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;();&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialCoursesState&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;CoursesState&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 
      &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getInitialState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;allCoursesLoaded&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;false&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coursesReducer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialCoursesState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
  &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;CourseActions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CoursesState&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActionTypes.COURSE_LOADED&lt;/span&gt;:
      &lt;span class=&#34;kt&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;CourseActionTypes.ALL_COURSES_LOADED&lt;/span&gt;:
      &lt;span class=&#34;kt&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
                &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;courses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
                    &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; 
                  &lt;span class=&#34;nx&#34;&gt;allCoursesLoaded&lt;/span&gt;:&lt;span class=&#34;kt&#34;&gt;true&lt;/span&gt;
                &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectEntities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectIds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectTotal&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getSelectors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;以下是我们必须要做的事情，包括这个额外的属性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首先，我们将 allCoursesLoaded 属性添加到 CoursesState 的类型定义中&lt;/li&gt;
&lt;li&gt;接下来，我们需要通过将一个可选对象传递给对 getInitialState() 的调用来在 initialCoursesState 中定义此属性的初始值&lt;/li&gt;
&lt;li&gt;我们现在可以在我们的 reducer 逻辑中设置这个属性，就像我们在ALL_COURSES_LOADED reducer中一样。&lt;/li&gt;
&lt;li&gt;为此，我们简单地需要使用spread（&amp;hellip;运算符）制作 CourseState 的副本，然后我们修改属性并将这个新状态对象传递给适配器调用&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;使用ngrx-schematics脚手架生成实体&#34;&gt;使用NgRx Schematics脚手架生成实体&lt;/h2&gt;
&lt;p&gt;如果您想快速生成我们在本文中展示的 reducer 文件，您可以通过使用NgRx Schematics获得一个非常好的开始。&lt;/p&gt;
&lt;p&gt;要使用实体 Schematics，我们需要做的第一件事是设置此CLI属性：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;ng config cli.defaultCollection @ngrx/schematics
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;在此之后，我们可以通过运行以下命令生成一个全新的Lesson reducer文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;ng generate entity --name Lesson --module courses/courses.module.ts
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;ngrx实体-schematics-生成什么&#34;&gt;NgRx实体 Schematics 生成什么？&lt;/h3&gt;
&lt;p&gt;现在让我们查看上面命令生成的输出。 首先，我们有一个空的Entity模型文件：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Lesson&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Schematics 命令还将生成一个完整的 Action 文件，每个 Action 对应于实体适配器中的一个状态修改方法：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;54
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;55
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;56
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;57
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;58
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;59
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;60
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;61
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;62
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;63
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;64
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;65
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;66
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;67
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;68
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;69
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;70
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;71
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;72
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;73
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;74
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;75
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;76
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;77
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;78
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;79
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;80
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;81
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;82
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;enum&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;LoadLessons&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[Lesson] Load Lessons&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;AddLesson&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[Lesson] Add Lesson&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;UpsertLesson&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[Lesson] Upsert Lesson&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;AddLessons&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[Lesson] Add Lessons&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;UpsertLessons&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[Lesson] Upsert Lessons&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;UpdateLesson&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[Lesson] Update Lesson&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;UpdateLessons&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[Lesson] Update Lessons&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;DeleteLesson&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[Lesson] Delete Lesson&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;DeleteLessons&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[Lesson] Delete Lessons&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;ClearLessons&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;[Lesson] Clear Lessons&amp;#39;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LoadLessons&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Action&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;LoadLessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AddLesson&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Action&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;AddLesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UpsertLesson&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Action&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UpsertLesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AddLessons&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Action&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;AddLessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UpsertLessons&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Action&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UpsertLessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UpdateLesson&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Action&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UpdateLesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Update&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UpdateLessons&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Action&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UpdateLessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Update&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;[]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DeleteLesson&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Action&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;DeleteLesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DeleteLessons&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Action&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;DeleteLessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

  &lt;span class=&#34;kr&#34;&gt;constructor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;public&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ClearLessons&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;implements&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Action&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;readonly&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ClearLessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActions&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
 &lt;span class=&#34;nx&#34;&gt;LoadLessons&lt;/span&gt;
 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AddLesson&lt;/span&gt;
 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UpsertLesson&lt;/span&gt;
 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;AddLessons&lt;/span&gt;
 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UpsertLessons&lt;/span&gt;
 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UpdateLesson&lt;/span&gt;
 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;UpdateLessons&lt;/span&gt;
 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DeleteLesson&lt;/span&gt;
 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;DeleteLessons&lt;/span&gt;
 &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ClearLessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;查看actions文件的内容&#34;&gt;查看Actions文件的内容&lt;/h3&gt;
&lt;p&gt;此文件遵循 Actions 文件的正常建议结构：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个枚举 LessonActionTypes，每个 Lesson action 一个条目&lt;/li&gt;
&lt;li&gt;每个 action 一个类，数据通过 payload 属性传递给 action&lt;/li&gt;
&lt;li&gt;底部的一个联合类型 LessonActions，包含此文件的所有 action 类&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后一个联合类型对于编写 reducer 逻辑特别有用。 多亏了它，我们可以在 Reducer 的 case 块中进行完整的类型推断和 IDE auto-completion。&lt;/p&gt;
&lt;h2 id=&#34;ngrx实体-updatet-类型&#34;&gt;NgRx实体 &lt;code&gt;Update&amp;lt;T&amp;gt;&lt;/code&gt; 类型&lt;/h2&gt;
&lt;p&gt;另请注意，在某些操作的定义中，我们使用的是&lt;code&gt;Update &amp;lt;Lesson&amp;gt;&lt;/code&gt;类型。 这是NgRx实体提供的辅助类型，用于帮助模型部分实体更新。&lt;/p&gt;
&lt;p&gt;此类型具有标识更新实体的属性标识，以及另一个名为 changes 的属性，该属性指定对实体进行的修改。&lt;/p&gt;
&lt;p&gt;以下是 Course 类型的有效更新对象示例：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;update&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Update&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Course&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;changes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;NgRx In Depth&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;categories&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;INTERMEDIATE&amp;#39;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;查看-reducer-文件的内容&#34;&gt;查看 reducer 文件的内容&lt;/h3&gt;
&lt;p&gt;NgRx Entity Schematics 命令还将如期生成Entity reducer文件和 test 文件。 以下是reducer文件的内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;45
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;46
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;47
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;48
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;49
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;50
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;51
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;52
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;53
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;54
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;55
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;56
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;57
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;58
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;59
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;60
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;61
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;62
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;63
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;64
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;65
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;66
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;67
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;State&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;extends&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;EntityState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;EntityAdapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 
      &lt;span class=&#34;nx&#34;&gt;createEntityAdapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;Lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;();&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialState&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;State&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 
      &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getInitialState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({});&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;reducer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;initialState&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;LessonActions&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;State&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;switch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;AddLesson&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UpsertLesson&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;upsertOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;AddLessons&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UpsertLessons&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;upsertMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UpdateLesson&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;updateOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lesson&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;UpdateLessons&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;updateMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;DeleteLesson&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;removeOne&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;DeleteLessons&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;removeMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ids&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;LoadLessons&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;action&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;payload&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;lessons&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;case&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;LessonActionTypes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ClearLessons&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;removeAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;default&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;state&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectIds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectEntities&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectAll&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;selectTotal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;adapter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getSelectors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;如何最好地使用-schematics-输出&#34;&gt;如何最好地使用 Schematics 输出？&lt;/h3&gt;
&lt;p&gt;请注意生成的 Schematics 文件（与生成CLI的任何其他文件一样）并不意味着保持不变。&lt;/p&gt;
&lt;p&gt;实际上，您可能甚至不想使用 actions 文件，而是使用给定的一组约定编写自己的操作，例如&lt;a href=&#34;https://www.youtube.com/watch?v=JmnsEvoy-gY&amp;amp;feature=youtu.be&#34;&gt;本演讲&lt;/a&gt;中推荐的约定。&lt;/p&gt;
&lt;p&gt;此外，可能并非所有操作都需要在应用程序中进行，因此仅保留我们需要的操作并对其进行调整非常重要。 像往常一样，Schematics 生成的文件只是一个有用的开端，需要根据具体情况进行调整。&lt;/p&gt;
&lt;h2 id=&#34;github-仓库中可运行的例子&#34;&gt;Github 仓库中可运行的例子&lt;/h2&gt;
&lt;p&gt;有关如何将 NgRx 实体与我们在示例中使用的两个实体（Course 和 Lesson）一起使用的小型应用程序的完整运行示例，请查看此&lt;a href=&#34;https://github.com/angular-university/angular-ngrx-course&#34;&gt;仓库&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;以下是NgRx DevTools，显示了两个实体的&lt;code&gt;store&lt;/code&gt;内容：
&lt;img src=&#34;https://kiyonlin.github.io/angular-university/ngrx-entity-2.png&#34; alt=&#34;NgRx DevTools&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;结论&#34;&gt;结论&lt;/h2&gt;
&lt;p&gt;NgRx 实体是一个非常有用的包，但为了理解它首先要熟悉基本存储概念（如Actions，Reducers 和 Selectors）以及一般的&lt;code&gt;store&lt;/code&gt;体系结构。&lt;/p&gt;
&lt;p&gt;如果我们已经熟悉这些概念，我们可能已经尝试找到构建&lt;code&gt;store&lt;/code&gt;内数据的最佳方法。&lt;/p&gt;
&lt;p&gt;NgRx 实体通过为我们的业务实体提供实体状态格式来为此提供答案，该格式针对id进行查找而优化，同时仍保留实体订单信息。&lt;/p&gt;
&lt;p&gt;NgRx 实体适配器与 NgRx Schematics 一起使得使用 NgRx 实体来存储我们的数据变得非常简单。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;但请注意，并非所有&lt;code&gt;store&lt;/code&gt;都需要使用NgRx实体！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;NgRx Entity 专门用于处理我们&lt;code&gt;store&lt;/code&gt;中的业务实体，使得以方便的方式将它们存储在内存中。&lt;/p&gt;
&lt;h3 id=&#34;了解有关-ngrx-生态系统的更多信息&#34;&gt;了解有关 NgRx 生态系统的更多信息&lt;/h3&gt;
&lt;p&gt;我希望这篇文章能帮助您开始使用Ngrx Entity，并且希望您喜欢它！&lt;/p&gt;
&lt;p&gt;如果您希望了解如何开始使用NgRx生态系统，您可能需要查看本系列之前的博文：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.angular-university.io/angular-2-redux-ngrx-rxjs/&#34;&gt;Angular Service Layers: Redux, RxJs and Ngrx Store - When to Use a Store And Why?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.angular-university.io/angular-ngrx-devtools/&#34;&gt;Angular Ngrx DevTools: Important Practical Tips&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
      
    </item>
    
    <item>
      <title>iptables详解-5-实用匹配扩展iprange,string,time</title>
      <link>https://kiyonlin.github.io/post/work/iptables/iptables-5-%E5%AE%9E%E7%94%A8%E5%8C%B9%E9%85%8D%E6%89%A9%E5%B1%95-iprange-string-time/</link>
      <pubDate>Wed, 22 Aug 2018 08:59:15 +0000</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/iptables/iptables-5-%E5%AE%9E%E7%94%A8%E5%8C%B9%E9%85%8D%E6%89%A9%E5%B1%95-iprange-string-time/</guid>
      
        <description>&lt;p&gt;本章介绍三个实用的规则匹配扩展模块&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;：&lt;code&gt;iprange&lt;/code&gt; 、&lt;code&gt;string&lt;/code&gt; 和 &lt;code&gt;time&lt;/code&gt;。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;pre&gt;&lt;code&gt;  注意
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;p&gt;本系列文章测试环境为 &lt;code&gt;centos 7&lt;/code&gt;，&lt;code&gt;iptables&lt;/code&gt; 版本 &lt;code&gt;1.4.21&lt;/code&gt;。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;iprange-扩展&#34;&gt;iprange 扩展&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; 匹配IP地址时，&lt;code&gt;-s&lt;/code&gt; 和 &lt;code&gt;-d&lt;/code&gt; 选项只能指定离散的IP地址，不能指定连续的IP地址，这时候就需要 &lt;code&gt;iprange&lt;/code&gt; 扩展模块。该模块包含了两个选项：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[!] &amp;ndash;src-range from[-to]&lt;/li&gt;
&lt;li&gt;[!] &amp;ndash;dst-range from[-to]&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;拒绝来自 &lt;code&gt;10.211.54.111&lt;/code&gt; 到 &lt;code&gt;10.211.55.11&lt;/code&gt; 范围的IP地址报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -I INPUT -m iprange --src-range 10.211.55.3-10.211.55.11 -j DROP
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;34&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;2308&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0            &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt; IP range 10.211.55.3-10.211.55.11
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;string-扩展&#34;&gt;string 扩展&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;string&lt;/code&gt; 扩展可以匹配报文中的字符串或者字节流，是一个比较实用的扩展。该扩展的选项如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--algo {bm|kmp}&lt;/code&gt; 指定字符串匹配算法，必填选项(算法介绍可以参考这两篇文章&lt;a href=&#34;http://www.ruanyifeng.com/blog/2013/05/boyer-moore_string_search_algorithm.html&#34;&gt;bm&lt;/a&gt;，&lt;a href=&#34;http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html&#34;&gt;kmp&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--from offset&lt;/code&gt; 指定在报文中的起始偏移量，默认为0，可选项&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--to offset&lt;/code&gt; 指定在报文中的结束偏移量，默认是报文大小，可选项&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[!] --string pattern&lt;/code&gt; 匹配字符串&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[!] --hex-string pattern&lt;/code&gt; 匹配十六进制字节流&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;下面让我们实际应用一下该扩展。这里我们在 &lt;code&gt;10.211.55.19&lt;/code&gt; 搭建了一个 &lt;code&gt;http&lt;/code&gt; 服务，包含两个页面：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;22
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;23
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;24
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;25
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;26
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;27
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;28
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;29
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;30
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;31
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;32
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;33
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;34
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;35
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;36
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;37
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;38
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;39
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;40
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;41
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;42
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;43
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;44
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;curl 10.211.55.19/drop.html -v                      
* About to connect&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; to 10.211.55.19 port &lt;span class=&#34;m&#34;&gt;80&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#0)&lt;/span&gt;
*   Trying 10.211.55.19...
* Connected to 10.211.55.19 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.211.55.19&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; port &lt;span class=&#34;m&#34;&gt;80&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#0)&lt;/span&gt;
&amp;gt; GET /drop.html HTTP/1.1
&amp;gt; User-Agent: curl/7.29.0
&amp;gt; Host: 10.211.55.19
&amp;gt; Accept: */*
&amp;gt;
&amp;lt; HTTP/1.1 &lt;span class=&#34;m&#34;&gt;200&lt;/span&gt; OK
&amp;lt; Server: openresty/1.13.6.1
&amp;lt; Date: Thu, &lt;span class=&#34;m&#34;&gt;23&lt;/span&gt; Aug &lt;span class=&#34;m&#34;&gt;2018&lt;/span&gt; 00:59:18 GMT
&amp;lt; Content-Type: text/html
&amp;lt; Content-Length: &lt;span class=&#34;m&#34;&gt;8&lt;/span&gt;
&amp;lt; Last-Modified: Wed, &lt;span class=&#34;m&#34;&gt;22&lt;/span&gt; Aug &lt;span class=&#34;m&#34;&gt;2018&lt;/span&gt; 09:11:45 GMT
&amp;lt; Connection: keep-alive
&amp;lt; ETag: &lt;span class=&#34;s2&#34;&gt;&amp;#34;5b7d28d1-8&amp;#34;&lt;/span&gt;
&amp;lt; Accept-Ranges: bytes
&amp;lt;
drop
me
* Connection &lt;span class=&#34;c1&#34;&gt;#0 to host 10.211.55.19 left intact&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;#####################################################&lt;/span&gt;
curl 10.211.55.19/hello.html -v                     
* About to connect&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; to 10.211.55.19 port &lt;span class=&#34;m&#34;&gt;80&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#0)&lt;/span&gt;
*   Trying 10.211.55.19...
* Connected to 10.211.55.19 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.211.55.19&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; port &lt;span class=&#34;m&#34;&gt;80&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#0)&lt;/span&gt;
&amp;gt; GET /hello.html HTTP/1.1
&amp;gt; User-Agent: curl/7.29.0
&amp;gt; Host: 10.211.55.19
&amp;gt; Accept: */*
&amp;gt;
&amp;lt; HTTP/1.1 &lt;span class=&#34;m&#34;&gt;200&lt;/span&gt; OK
&amp;lt; Server: openresty/1.13.6.1
&amp;lt; Date: Thu, &lt;span class=&#34;m&#34;&gt;23&lt;/span&gt; Aug &lt;span class=&#34;m&#34;&gt;2018&lt;/span&gt; 00:59:52 GMT
&amp;lt; Content-Type: text/html
&amp;lt; Content-Length: &lt;span class=&#34;m&#34;&gt;12&lt;/span&gt;
&amp;lt; Last-Modified: Wed, &lt;span class=&#34;m&#34;&gt;22&lt;/span&gt; Aug &lt;span class=&#34;m&#34;&gt;2018&lt;/span&gt; 09:11:59 GMT
&amp;lt; Connection: keep-alive
&amp;lt; ETag: &lt;span class=&#34;s2&#34;&gt;&amp;#34;5b7d28df-c&amp;#34;&lt;/span&gt;
&amp;lt; Accept-Ranges: bytes
&amp;lt;
hello world
* Connection &lt;span class=&#34;c1&#34;&gt;#0 to host 10.211.55.19 left intact&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;拒绝包含 &lt;code&gt;world&lt;/code&gt; 字符串的报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -F OUTPUT
iptables -I OUTPUT -m string --algo kmp --string &lt;span class=&#34;s2&#34;&gt;&amp;#34;world&amp;#34;&lt;/span&gt; -j REJECT
iptables -nvL OUTPUT
Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;15&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;2432&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            STRING match  &lt;span class=&#34;s2&#34;&gt;&amp;#34;world&amp;#34;&lt;/span&gt; ALGO name kmp TO &lt;span class=&#34;m&#34;&gt;65535&lt;/span&gt; reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;因为我们要用测试机获取 &lt;code&gt;10.211.55.19&lt;/code&gt; 上的页面，所以 &lt;code&gt;iptables&lt;/code&gt; 规则要写在 &lt;code&gt;OUTPUT&lt;/code&gt; 链上。
再使用测试机 &lt;code&gt;curl&lt;/code&gt; 一下 &lt;code&gt;hello.html&lt;/code&gt; 页面，发现无法获取页面内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;curl 10.211.55.19/hello.html -v                     
* About to connect&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; to 10.211.55.19 port &lt;span class=&#34;m&#34;&gt;80&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#0)&lt;/span&gt;
*   Trying 10.211.55.19...
* Connected to 10.211.55.19 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.211.55.19&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; port &lt;span class=&#34;m&#34;&gt;80&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#0)&lt;/span&gt;
&amp;gt; GET /hello.html HTTP/1.1
&amp;gt; User-Agent: curl/7.29.0
&amp;gt; Host: 10.211.55.19
&amp;gt; Accept: */*
&amp;gt;
&amp;lt; HTTP/1.1 &lt;span class=&#34;m&#34;&gt;200&lt;/span&gt; OK
&amp;lt; Server: openresty/1.13.6.1
&amp;lt; Date: Thu, &lt;span class=&#34;m&#34;&gt;23&lt;/span&gt; Aug &lt;span class=&#34;m&#34;&gt;2018&lt;/span&gt; 01:00:03 GMT
&amp;lt; Content-Type: text/html
&amp;lt; Content-Length: &lt;span class=&#34;m&#34;&gt;12&lt;/span&gt;
&amp;lt; Last-Modified: Wed, &lt;span class=&#34;m&#34;&gt;22&lt;/span&gt; Aug &lt;span class=&#34;m&#34;&gt;2018&lt;/span&gt; 09:11:59 GMT
&amp;lt; Connection: keep-alive
&amp;lt; ETag: &lt;span class=&#34;s2&#34;&gt;&amp;#34;5b7d28df-c&amp;#34;&lt;/span&gt;
&amp;lt; Accept-Ranges: bytes
&amp;lt;                       
^C
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;查看 &lt;code&gt;OUTPUT&lt;/code&gt; 链，拦截了96个报文：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL OUTPUT
Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;220&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;15689&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
   &lt;span class=&#34;m&#34;&gt;96&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;7704&lt;/span&gt; REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            STRING match  &lt;span class=&#34;s2&#34;&gt;&amp;#34;world&amp;#34;&lt;/span&gt; ALGO name kmp TO &lt;span class=&#34;m&#34;&gt;65535&lt;/span&gt; reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;拒绝包含 &lt;code&gt;drop\nme&lt;/code&gt; 字节流的报文
查看 &lt;code&gt;ascii&lt;/code&gt; 表，可以知道 &lt;code&gt;\n&lt;/code&gt; 的十六进制值为 &lt;code&gt;0A&lt;/code&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -I OUTPUT -m string --algo kmp --hex-string &lt;span class=&#34;s2&#34;&gt;&amp;#34;drop|0A|me&amp;#34;&lt;/span&gt; -j REJECT
iptables -nvL OUTPUT
Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;31&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;4008&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            STRING match  &lt;span class=&#34;s2&#34;&gt;&amp;#34;|64726f700a6d65|&amp;#34;&lt;/span&gt; ALGO name kmp TO &lt;span class=&#34;m&#34;&gt;65535&lt;/span&gt; reject-with icmp-port-unreachable
  &lt;span class=&#34;m&#34;&gt;108&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;8640&lt;/span&gt; REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            STRING match  &lt;span class=&#34;s2&#34;&gt;&amp;#34;world&amp;#34;&lt;/span&gt; ALGO name kmp TO &lt;span class=&#34;m&#34;&gt;65535&lt;/span&gt; reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;&amp;quot;drop|0A|me&amp;quot;&lt;/code&gt; 被转化成了纯字节流字符串 &lt;code&gt;&amp;quot;|64726f700a6d65|&amp;quot;&lt;/code&gt;。
再使用测试机 &lt;code&gt;curl&lt;/code&gt; 一下 &lt;code&gt;drop.html&lt;/code&gt; 页面，发现无法获取页面内容：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;curl 10.211.55.19/drop.html -v                      
* About to connect&lt;span class=&#34;o&#34;&gt;()&lt;/span&gt; to 10.211.55.19 port &lt;span class=&#34;m&#34;&gt;80&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#0)&lt;/span&gt;
*   Trying 10.211.55.19...
* Connected to 10.211.55.19 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.211.55.19&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; port &lt;span class=&#34;m&#34;&gt;80&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#0)&lt;/span&gt;
&amp;gt; GET /drop.html HTTP/1.1
&amp;gt; User-Agent: curl/7.29.0
&amp;gt; Host: 10.211.55.19
&amp;gt; Accept: */*
&amp;gt;
&amp;lt; HTTP/1.1 &lt;span class=&#34;m&#34;&gt;200&lt;/span&gt; OK
&amp;lt; Server: openresty/1.13.6.1
&amp;lt; Date: Thu, &lt;span class=&#34;m&#34;&gt;23&lt;/span&gt; Aug &lt;span class=&#34;m&#34;&gt;2018&lt;/span&gt; 01:01:43 GMT
&amp;lt; Content-Type: text/html
&amp;lt; Content-Length: &lt;span class=&#34;m&#34;&gt;8&lt;/span&gt;
&amp;lt; Last-Modified: Wed, &lt;span class=&#34;m&#34;&gt;22&lt;/span&gt; Aug &lt;span class=&#34;m&#34;&gt;2018&lt;/span&gt; 09:11:45 GMT
&amp;lt; Connection: keep-alive
&amp;lt; ETag: &lt;span class=&#34;s2&#34;&gt;&amp;#34;5b7d28d1-8&amp;#34;&lt;/span&gt;
&amp;lt; Accept-Ranges: bytes
&amp;lt;                      
^C
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;查看 &lt;code&gt;OUTPUT&lt;/code&gt; 链，拦截了54个报文：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL OUTPUT
Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;490&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;34180&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
   &lt;span class=&#34;m&#34;&gt;54&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;4140&lt;/span&gt; REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            STRING match  &lt;span class=&#34;s2&#34;&gt;&amp;#34;|64726f700a6d65|&amp;#34;&lt;/span&gt; ALGO name kmp TO &lt;span class=&#34;m&#34;&gt;65535&lt;/span&gt; reject-with icmp-port-unreachable
  &lt;span class=&#34;m&#34;&gt;108&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;8640&lt;/span&gt; REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            STRING match  &lt;span class=&#34;s2&#34;&gt;&amp;#34;world&amp;#34;&lt;/span&gt; ALGO name kmp TO &lt;span class=&#34;m&#34;&gt;65535&lt;/span&gt; reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;!-- raw HTML omitted --&gt;
&lt;pre&gt;&lt;code&gt;  注意
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;p&gt;&lt;code&gt;http&lt;/code&gt; 协议是基于 &lt;code&gt;tcp&lt;/code&gt; 协议的可靠连接，当客户端未得到响应时，会尝试重传，尝试一定次数后才确认请求失败。所以上述两条规则匹配到的报文数最后都会达到108。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL OUTPUT
Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;2202&lt;/span&gt; packets, 279K bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
  &lt;span class=&#34;m&#34;&gt;108&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;8208&lt;/span&gt; REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            STRING match  &lt;span class=&#34;s2&#34;&gt;&amp;#34;|64726f700a6d65|&amp;#34;&lt;/span&gt; ALGO name kmp TO &lt;span class=&#34;m&#34;&gt;65535&lt;/span&gt; reject-with icmp-port-unreachable
  &lt;span class=&#34;m&#34;&gt;108&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;8640&lt;/span&gt; REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            STRING match  &lt;span class=&#34;s2&#34;&gt;&amp;#34;world&amp;#34;&lt;/span&gt; ALGO name kmp TO &lt;span class=&#34;m&#34;&gt;65535&lt;/span&gt; reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;!-- raw HTML omitted --&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;pre&gt;&lt;code&gt;  注意
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;p&gt;实验过程中，发现使用 &lt;code&gt;bm&lt;/code&gt; 算法时，无法匹配目标字符串。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;time扩展&#34;&gt;time扩展&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;time&lt;/code&gt; 扩展可以匹配报文到达时间。该扩展的常用选项有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;--datestart YYYY[-MM[-DD[Thh[:mm[:ss]]]]]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--datestop YYYY[-MM[-DD[Thh[:mm[:ss]]]]]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--timestart hh:mm[:ss]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--timestop hh:mm[:ss]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[!] --monthdays day[,day...]&lt;/code&gt; 没有31的月份会忽略31，二月份还会忽略29，30&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[!] --weekdays day[,day...]&lt;/code&gt; 星期选项可以是 &lt;code&gt;1-7&lt;/code&gt; 的数字，或者英文缩写 &lt;code&gt;Mon&lt;/code&gt;, &lt;code&gt;Tue&lt;/code&gt;, &lt;code&gt;Wed&lt;/code&gt;, &lt;code&gt;Thu&lt;/code&gt;, &lt;code&gt;Fri&lt;/code&gt;, &lt;code&gt;Sat&lt;/code&gt;, &lt;code&gt;Sun&lt;/code&gt;。只使用两个字母也是允许的：&lt;code&gt;Mo&lt;/code&gt;, &lt;code&gt;Tu&lt;/code&gt;, &lt;code&gt;We&lt;/code&gt;, &lt;code&gt;Th&lt;/code&gt;, &lt;code&gt;Fr&lt;/code&gt;, &lt;code&gt;Sa&lt;/code&gt;, &lt;code&gt;Su&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;周六周日 00:00-00:06, 21:00-23:59 不提供网页服务&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -F OUTPUT
iptables -I OUTPUT -p tcp -m multiport --dports 80,443 -m &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; --weekdays Sa,Su --timestart 21:00  --timestop 06:00 -j REJECT
iptables -nvL OUTPUT
Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;7&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;1272&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            multiport dports 80,443 TIME from 21:00:00 to 06:00:00 on Sat,Sun UTC reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;section class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;man iptables-extensions 8 &lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      
    </item>
    
    <item>
      <title>iptables详解-4-规则匹配</title>
      <link>https://kiyonlin.github.io/post/work/iptables/iptables-4-%E8%A7%84%E5%88%99%E5%8C%B9%E9%85%8D/</link>
      <pubDate>Tue, 21 Aug 2018 14:35:08 +0000</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/iptables/iptables-4-%E8%A7%84%E5%88%99%E5%8C%B9%E9%85%8D/</guid>
      
        <description>&lt;p&gt;本章介绍更多的规则匹配条件。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;pre&gt;&lt;code&gt;  注意
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;p&gt;本系列文章测试环境为 &lt;code&gt;centos 7&lt;/code&gt;，&lt;code&gt;iptables&lt;/code&gt; 版本 &lt;code&gt;1.4.21&lt;/code&gt;。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;源ip地址&#34;&gt;源IP地址&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;指定多个IP地址&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -I INPUT -s 10.211.55.9,10.211.55.10 -j DROP
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;40&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;2652&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       all  --  *      *       10.211.55.10         0.0.0.0/0
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;指定某个网段&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -I INPUT -s 10.211.0.0/16 -j ACCEPT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;27&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;2293&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
   &lt;span class=&#34;m&#34;&gt;40&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;2772&lt;/span&gt; ACCEPT     all  --  *      *       10.211.0.0/16        0.0.0.0/0
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       all  --  *      *       10.211.55.10         0.0.0.0/0
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;对源IP地址取反&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -F INPUT
iptables -A INPUT ! -s 10.211.55.9 -j ACCEPT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
   &lt;span class=&#34;m&#34;&gt;53&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;4145&lt;/span&gt; ACCEPT     all  --  *      *      !10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;上述规则的意思为报文的源IP地址不是 &lt;code&gt;10.211.55.9&lt;/code&gt; 的，都执行 &lt;code&gt;ACCEPT&lt;/code&gt; 操作。
让我们使用测试机 &lt;code&gt;ping&lt;/code&gt; 一下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ping 10.211.55.19                                  
PING 10.211.55.19 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.211.55.19&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.211.55.19: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.401 ms
&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.211.55.19: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.261 ms
&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.211.55.19: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.258 ms
^C
--- 10.211.55.19 ping statistics ---
&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2001ms
rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0.258/0.306/0.401/0.069 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们可以看到，测试机仍然可以 &lt;code&gt;ping&lt;/code&gt; 通目标机器。这是因为，当所有规则都未匹配时，会使用链的默认策略执行操作，而现在 &lt;code&gt;INPUT&lt;/code&gt; 链的默认规则为 &lt;code&gt;ACCEPT&lt;/code&gt;。查看 &lt;code&gt;INPUT&lt;/code&gt; 链情况：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;252&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
  &lt;span class=&#34;m&#34;&gt;505&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;41315&lt;/span&gt; ACCEPT     all  --  *      *      !10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;从输出结果可以看到，默认策略接受了3个报文，总大小为252b。&lt;/p&gt;
&lt;h2 id=&#34;目标ip地址&#34;&gt;目标IP地址&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; 使用 &lt;code&gt;-d, --destination&lt;/code&gt; 选项指定目标IP地址，表示报文要发到哪里去。
查看当前机器的IP地址：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ifconfig &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; awk &lt;span class=&#34;s1&#34;&gt;&amp;#39;/inet\&amp;gt;/ {print $2}&amp;#39;&lt;/span&gt;
10.211.55.19
127.0.0.1
192.168.122.1
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;拒绝向 &lt;code&gt;192.168.122.1&lt;/code&gt; 发送报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -I INPUT -d 192.168.122.1 -j DROP
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       all  --  *      *       0.0.0.0/0            192.168.122.1
 &lt;span class=&#34;m&#34;&gt;5192&lt;/span&gt;  416K ACCEPT     all  --  *      *      !10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;-d&lt;/code&gt; 和 &lt;code&gt;-s&lt;/code&gt; 选项一样，支持多地址以及网段，也可以取反。&lt;/p&gt;
&lt;h2 id=&#34;协议&#34;&gt;协议&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;-p, --protocol&lt;/code&gt; 选项指定匹配报文的协议。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;拒绝来自 &lt;code&gt;10.211.55.9&lt;/code&gt; 的 &lt;code&gt;tcp&lt;/code&gt; 报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -F INPUT
iptables -I INPUT -s 10.211.55.9 -d 10.211.55.19 -p tcp -j REJECT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;32&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;2092&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     tcp  --  *      *       10.211.55.9          10.211.55.19         reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;使用测试机 &lt;code&gt;telnet&lt;/code&gt; 一下:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;telnet 10.211.55.19 &lt;span class=&#34;m&#34;&gt;22&lt;/span&gt;                              
Trying 10.211.55.19...
telnet: connect to address 10.211.55.19: Connection refused
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;再用测试机 &lt;code&gt;ping&lt;/code&gt; 一下:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;ping 10.211.55.19                                   
PING 10.211.55.19 (10.211.55.19) 56(84) bytes of data.
64 bytes from 10.211.55.19: icmp_seq=1 ttl=64 time=0.240 ms
64 bytes from 10.211.55.19: icmp_seq=2 ttl=64 time=0.237 ms
64 bytes from 10.211.55.19: icmp_seq=3 ttl=64 time=0.284 ms
^C
--- 10.211.55.19 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.237/0.253/0.284/0.028 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这时候查看 &lt;code&gt;INPUT&lt;/code&gt; 链:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;428&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;34304&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;    &lt;span class=&#34;m&#34;&gt;60&lt;/span&gt; REJECT     tcp  --  *      *       10.211.55.9          10.211.55.19         reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;拒绝了一个报文。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-p&lt;/code&gt; 选项包含的协议如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;tcp&lt;/li&gt;
&lt;li&gt;udp&lt;/li&gt;
&lt;li&gt;icmp&lt;/li&gt;
&lt;li&gt;udplite&lt;/li&gt;
&lt;li&gt;icmpv6&lt;/li&gt;
&lt;li&gt;esp&lt;/li&gt;
&lt;li&gt;ah&lt;/li&gt;
&lt;li&gt;sctp&lt;/li&gt;
&lt;li&gt;mh&lt;/li&gt;
&lt;li&gt;all(所有协议)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不指定 &lt;code&gt;-p&lt;/code&gt; 选项时，相当于 &lt;code&gt;-p all&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;网卡接口&#34;&gt;网卡接口&lt;/h2&gt;
&lt;p&gt;当机器中有多个网卡时，可以使用 &lt;code&gt;-i, --in-interface&lt;/code&gt; 选项指定具体的网卡接口。
下面是 10.211.55.19 所在网卡的信息:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ifconfig
br0: &lt;span class=&#34;nv&#34;&gt;flags&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;4163&amp;lt;UP,BROADCAST,RUNNING,MULTICAST&amp;gt;  mtu &lt;span class=&#34;m&#34;&gt;1500&lt;/span&gt;
        inet 10.211.55.19  netmask 255.255.255.0  broadcast 10.211.55.255
        inet6 fe80::21c:42ff:fe14:8ef3  prefixlen &lt;span class=&#34;m&#34;&gt;64&lt;/span&gt;  scopeid 0x20&amp;lt;link&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;拒绝流入 &lt;code&gt;br0&lt;/code&gt; 网卡的 &lt;code&gt;ping&lt;/code&gt; 报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -F INPUT
iptables -I INPUT -i br0 -p icmp -j DROP
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;54&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;4185&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       icmp --  br0    *       0.0.0.0/0            0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;使用测试机 ping 一下:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ping 10.211.55.19                                   
PING 10.211.55.19 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.211.55.19&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
^C
--- 10.211.55.19 ping statistics ---
&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; received, 100% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 3000ms
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;查看 &lt;code&gt;INPUT&lt;/code&gt; 链:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;409&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;33105&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;336&lt;/span&gt; DROP       icmp --  br0    *       0.0.0.0/0            0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;-i&lt;/code&gt; 选项只适用于 &lt;code&gt;PREROUTING&lt;/code&gt;、&lt;code&gt;INPUT&lt;/code&gt;、&lt;code&gt;FORWARD&lt;/code&gt; 这三个链，因为只有它们涉及流入网卡。
同理，&lt;code&gt;-o, --out-interface&lt;/code&gt; 选项适用 &lt;code&gt;OUTPUT&lt;/code&gt;、&lt;code&gt;FORWARD&lt;/code&gt;、&lt;code&gt;POSTROUTING&lt;/code&gt; 这三个链。&lt;/p&gt;
&lt;p&gt;另外，&lt;code&gt;-i&lt;/code&gt; 和 &lt;code&gt;-o&lt;/code&gt; 都支持取反 &lt;code&gt;!&lt;/code&gt; 操作，而且参数以 &lt;code&gt;+&lt;/code&gt; 结尾时，表示以参数开头的网卡都能匹配到。&lt;/p&gt;
&lt;h2 id=&#34;扩展匹配&#34;&gt;扩展匹配&lt;/h2&gt;
&lt;p&gt;除了基本匹配条件以为，还有很多扩展匹配条件。这些匹配条件基于各个匹配模块，使用 &lt;code&gt;-m, --match&lt;/code&gt; 选项可以指定模块，然后就可以使用模块中的各种选项。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tcp&lt;/code&gt; 模块中包含了端口匹配条件：&lt;code&gt;[!] --source-port,--sport port[:port]&lt;/code&gt;, &lt;code&gt;[!] --destination-port,--dport port[:port]&lt;/code&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;拒绝 来自 &lt;code&gt;10.211.55.9&lt;/code&gt; 且目标端口为 22 的报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -F INPUT
iptables -I INPUT -p tcp -m tcp -s 10.211.55.9 --dport &lt;span class=&#34;m&#34;&gt;22&lt;/span&gt; -j REJECT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;28&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;1932&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     tcp  --  *      *       10.211.55.9          0.0.0.0/0            tcp dpt:22 reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;使用测试机 &lt;code&gt;ssh&lt;/code&gt; 连接:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ssh root@10.211.55.19                              
ssh: connect to host 10.211.55.19 port 22: Connection refused
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;查看 &lt;code&gt;INPUT&lt;/code&gt; 链:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;496&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;40260&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;    &lt;span class=&#34;m&#34;&gt;60&lt;/span&gt; REJECT     tcp  --  *      *       10.211.55.9          0.0.0.0/0            tcp dpt:22 reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;当使用 &lt;code&gt;-p&lt;/code&gt; 选项时，会默认添加 &lt;code&gt;-m 协议名&lt;/code&gt;。比如 &lt;code&gt;-p udp&lt;/code&gt; 相当于 &lt;code&gt;-p udp -m udp&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--dport&lt;/code&gt; 和 &lt;code&gt;--sport&lt;/code&gt; 都支持端口范围。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;拒绝 来自 &lt;code&gt;10.211.55.9&lt;/code&gt; 且源端口范围 &lt;code&gt;30-40&lt;/code&gt; 的报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -I INPUT -p tcp -s 10.211.55.9 --sport 30:40 -j REJECT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;8&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;540&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     tcp  --  *      *       10.211.55.9          0.0.0.0/0            tcp spts:30:40 reject-with icmp-port-unreachable
    &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;    &lt;span class=&#34;m&#34;&gt;60&lt;/span&gt; REJECT     tcp  --  *      *       10.211.55.9          0.0.0.0/0            tcp dpt:22 reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;端口范围可以省略数字，比如 &lt;code&gt;8080:&lt;/code&gt; 表示 &lt;code&gt;8080:65535&lt;/code&gt;， &lt;code&gt;:3306&lt;/code&gt; 表示 &lt;code&gt;0:3306&lt;/code&gt;。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -I INPUT -p tcp -s 10.211.55.9 --sport 8080: -j REJECT
iptables -I INPUT -p tcp -s 10.211.55.9 --sport :3306 -j REJECT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;10&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;688&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     tcp  --  *      *       10.211.55.9          0.0.0.0/0            tcp spts:0:3306 reject-with icmp-port-unreachable
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     tcp  --  *      *       10.211.55.9          0.0.0.0/0            tcp spts:8080:65535 reject-with icmp-port-unreachable
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     tcp  --  *      *       10.211.55.9          0.0.0.0/0            tcp spts:30:40 reject-with icmp-port-unreachable
    &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;    &lt;span class=&#34;m&#34;&gt;60&lt;/span&gt; REJECT     tcp  --  *      *       10.211.55.9          0.0.0.0/0            tcp dpt:22 reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;multiport模块&#34;&gt;multiport模块&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;--dport&lt;/code&gt; 和 &lt;code&gt;--sport&lt;/code&gt; 只支持连续的端口范围，但不支持离散的端口范围。
这时候需要使用 &lt;code&gt;multiport&lt;/code&gt; 模块的&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[!] --source-ports,--sports port[,port|,port:port]...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[!] --destination-ports,--dports port[,port|,port:port]...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[!] --ports port[,port|,port:port]...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;选项来指定离散端口。其中 &lt;code&gt;--ports&lt;/code&gt; 会匹配源端口和目标端口。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -F INPUT
iptables -I INPUT -p tcp -s 10.211.55.9 -m multiport --dports 22,23,80:88,9000 -j REJECT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;34&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;2248&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     tcp  --  *      *       10.211.55.9          0.0.0.0/0            multiport dports 22,23,80:88,9000 reject-with icmp-port-unreachable
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;!-- raw HTML omitted --&gt;
&lt;pre&gt;&lt;code&gt;  注意
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;p&gt;&lt;code&gt;multiport&lt;/code&gt; 模块只能结合 &lt;code&gt;tcp&lt;/code&gt; 、 &lt;code&gt;udp&lt;/code&gt; 、 &lt;code&gt;udplite&lt;/code&gt; 、 &lt;code&gt;dccp&lt;/code&gt; 和 &lt;code&gt;sctp&lt;/code&gt; 协议使用。
且最多支持15个离散端口，其中范围端口算作两个端口。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
</description>
      
    </item>
    
    <item>
      <title>iptables详解-3-规则管理</title>
      <link>https://kiyonlin.github.io/post/work/iptables/iptables-3-%E8%A7%84%E5%88%99%E7%AE%A1%E7%90%86/</link>
      <pubDate>Wed, 15 Aug 2018 14:19:31 +0000</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/iptables/iptables-3-%E8%A7%84%E5%88%99%E7%AE%A1%E7%90%86/</guid>
      
        <description>&lt;p&gt;本章学习如何对 &lt;code&gt;iptables&lt;/code&gt; 规则进行管理。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;pre&gt;&lt;code&gt;  注意
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;p&gt;本系列文章测试环境为 &lt;code&gt;centos 7&lt;/code&gt;，&lt;code&gt;iptables&lt;/code&gt; 版本 &lt;code&gt;1.4.21&lt;/code&gt;。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;准备工作&#34;&gt;准备工作&lt;/h2&gt;
&lt;p&gt;iptables 中最常用的匹配条件就是源ip，源端口，目标ip，目标端口等。最常用的动作有 &lt;code&gt;ACCEPT&lt;/code&gt;、&lt;code&gt;DROP&lt;/code&gt;、&lt;code&gt;REJECT&lt;/code&gt;等。&lt;/p&gt;
&lt;h3 id=&#34;初始化&#34;&gt;初始化&lt;/h3&gt;
&lt;p&gt;让我们先清空系统提供的默认规则，准备一个初始化环境：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -F INPUT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;298&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;30644&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;因为 &lt;code&gt;filter&lt;/code&gt; 表的 &lt;code&gt;INPUT&lt;/code&gt; 链默认策略为 &lt;code&gt;ACCEPT&lt;/code&gt; ，所以所有到达本机的报文都会通过。&lt;/p&gt;
&lt;h3 id=&#34;测试机&#34;&gt;测试机&lt;/h3&gt;
&lt;p&gt;准备另一台测试机，接下来的规则都会针对这台测试机而做：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ping 10.211.55.19
PING 10.211.55.19 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.211.55.19&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.211.55.19: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.458 ms
&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.211.55.19: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.268 ms
&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.211.55.19: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.266 ms
^C
--- 10.211.55.19 ping statistics ---
&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2002ms
rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0.266/0.330/0.458/0.092 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;增加规则&#34;&gt;增加规则&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;-I, --insert&lt;/code&gt; 选项可以增加规则。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-j, --jump&lt;/code&gt; 接收 &lt;code&gt;target&lt;/code&gt; 作为参数。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;添加一条规则，拒绝所有来自 &lt;code&gt;10.211.55.9&lt;/code&gt; 的报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -t filter -I INPUT -s 10.211.55.9 -j DROP
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;93&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;8970&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;再使用测试机 &lt;code&gt;ping&lt;/code&gt; 一下，会发现不通：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ping 10.211.55.19                                      
PING 10.211.55.19 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.211.55.19&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
^C
--- 10.211.55.19 ping statistics ---
&lt;span class=&#34;m&#34;&gt;11&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; received, 100% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 10003ms
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;但是目标机器的报文量会增加(11个报文，总大小924b)：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;445&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;48402&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
   &lt;span class=&#34;m&#34;&gt;11&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;924&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;code&gt;-A, --append&lt;/code&gt; 选项可以追加规则。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;追加一条规则，接受所有来自 &lt;code&gt;10.211.55.9&lt;/code&gt; 的报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -A INPUT -s 10.211.55.9 -j ACCEPT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;30&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;2072&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
   &lt;span class=&#34;m&#34;&gt;11&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;924&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;再次用测试机 &lt;code&gt;ping&lt;/code&gt;，还是不通:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ping 10.211.55.19                                      
PING 10.211.55.19 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.211.55.19&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
^C
--- 10.211.55.19 ping statistics ---
&lt;span class=&#34;m&#34;&gt;5&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; received, 100% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 4001ms
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;查看现在规则情况:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;904&lt;/span&gt; packets, 100K bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
   &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;1344&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们可以看到第一条规则报文量更新了，但是第二条规则并未收到任何数据。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;-s, --source&lt;/code&gt; 选项指定源IP地址。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;添加一条规则，接受所有来自 &lt;code&gt;10.211.55.9&lt;/code&gt; 的报文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -I INPUT -s 10.211.55.9 -j ACCEPT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;92&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;8870&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  *      *       10.211.55.9          0.0.0.0/0
   &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;1344&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;现在第一条规则接受所有来自 &lt;code&gt;10.211.55.9&lt;/code&gt; 的报文，再使用测试机 &lt;code&gt;ping&lt;/code&gt; 一下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ping 10.211.55.19                                      
PING 10.211.55.19 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.211.55.19&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.211.55.19: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.367 ms
&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.211.55.19: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.296 ms
&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; bytes from 10.211.55.19: &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;ttl&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;64&lt;/span&gt; &lt;span class=&#34;nv&#34;&gt;time&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;0.278 ms
^C
--- 10.211.55.19 ping statistics ---
&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; received, 0% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2001ms
rtt min/avg/max/mdev &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 0.278/0.313/0.367/0.043 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;又可以 ping 通了，而且第一条规则的报文数据也发生了改变：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;15096&lt;/span&gt; packets, 1693K bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;252&lt;/span&gt; ACCEPT     all  --  *      *       10.211.55.9          0.0.0.0/0
   &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;1344&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;从这里，我们可以看出 &lt;code&gt;iptables&lt;/code&gt; 的工作机制：只要匹配了一条规则，就不会再去匹配接下来的规则。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在指定顺序规则前添加一条规则&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -I INPUT &lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; -s 10.211.55.9 -j DROP
iptables -nvL INPUT --line
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;236&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;23798&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
num   pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;252&lt;/span&gt; ACCEPT     all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;1344&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;删除规则&#34;&gt;删除规则&lt;/h2&gt;
&lt;p&gt;删除规则有两种方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;根据序号删除&lt;/li&gt;
&lt;li&gt;根据匹配条件删除&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;根据序号删除指定规则&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;先查看一下目前所有的规则：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL INPUT --line
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;236&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;23798&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
num   pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;252&lt;/span&gt; ACCEPT     all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;1344&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;删除第4条规则，命令如下:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -D INPUT &lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;
iptables -nvL INPUT --line
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;108&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;8771&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
num   pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;252&lt;/span&gt; ACCEPT     all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;       &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;1344&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;根据匹配条件进行删除&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们删除 &lt;code&gt;target&lt;/code&gt; 是 &lt;code&gt;ACCEPT&lt;/code&gt; 的规则：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -D INPUT -s 10.211.55.9 -j ACCEPT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;79&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;5905&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
   &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;1344&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;删除所有规则&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用 &lt;code&gt;-F, --flush&lt;/code&gt; 选项即可，我们在准备工作中已经操作过。&lt;/p&gt;
&lt;h2 id=&#34;修改规则&#34;&gt;修改规则&lt;/h2&gt;
&lt;p&gt;使用 &lt;code&gt;-R, --replace&lt;/code&gt; 选项替换指定编号规则内容。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;修改规则， &lt;code&gt;target&lt;/code&gt; 从 &lt;code&gt;DROP&lt;/code&gt; 改为 &lt;code&gt;REJECT&lt;/code&gt;，源IP不变&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -R INPUT &lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; -s 10.211.55.9 -j REJECT
iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;282&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;24058&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     all  --  *      *       10.211.55.9          0.0.0.0/0            reject-with icmp-port-unreachable
   &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;1344&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;!-- raw HTML omitted --&gt;
&lt;pre&gt;&lt;code&gt;  注意
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;p&gt;若不指定 &lt;code&gt;-s&lt;/code&gt; 内容，则默认会被设置为 &lt;code&gt;0.0.0.0/0&lt;/code&gt;，这可能会导致严重错误（例如 &lt;code&gt;DROP&lt;/code&gt;  或者 &lt;code&gt;REJECT&lt;/code&gt; 了所有报文，那么当前连接也会断开）。所以使用 &lt;code&gt;-R&lt;/code&gt; 命令可以理解为替换当前顺序的规则的所有内容，这些内容必须写全，而不能写一部分；也可以理解为先删除这条指定的规则，再在同一位置插入新规则。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h3 id=&#34;drop-和-reject-的区别&#34;&gt;&lt;code&gt;DROP&lt;/code&gt; 和 &lt;code&gt;REJECT&lt;/code&gt; 的区别&lt;/h3&gt;
&lt;p&gt;将 &lt;code&gt;DROP&lt;/code&gt; 替换为 &lt;code&gt;REJECT&lt;/code&gt; 后，我们使用测试机 &lt;code&gt;ping&lt;/code&gt; 一下，看看两者的区别：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;ping 10.211.55.19                             
PING 10.211.55.19 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.211.55.19&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; 56&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;84&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; bytes of data.
From 10.211.55.19 &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; Destination Port Unreachable
From 10.211.55.19 &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt; Destination Port Unreachable
From 10.211.55.19 &lt;span class=&#34;nv&#34;&gt;icmp_seq&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; Destination Port Unreachable
^C
--- 10.211.55.19 ping statistics ---
&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt; packets transmitted, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; received, +3 errors, 100% packet loss, &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; 2000ms
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们可以看到，&lt;code&gt;REJECT&lt;/code&gt; 会给出明确的回应，而不像 &lt;code&gt;DROP&lt;/code&gt; 体现为毫无反应。&lt;/p&gt;
&lt;p&gt;此时我们再看看规则情况，第一条规则报文量也发生了改变，说明匹配到了这条规则：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;2844&lt;/span&gt; packets, 248K bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;252&lt;/span&gt; REJECT     all  --  *      *       10.211.55.9          0.0.0.0/0            reject-with icmp-port-unreachable
   &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;1344&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id=&#34;修改默认策略&#34;&gt;修改默认策略&lt;/h3&gt;
&lt;p&gt;使用 &lt;code&gt;-P, --policy&lt;/code&gt; 选项修改链的默认策略，当链中没有规则，或者所有规则都未匹配时，则使用默认规则：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -P FORWARD DROP
iptables -nvL
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;112&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;8478&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;   &lt;span class=&#34;m&#34;&gt;252&lt;/span&gt; REJECT     all  --  *      *       10.211.55.9          0.0.0.0/0            reject-with icmp-port-unreachable
   &lt;span class=&#34;m&#34;&gt;16&lt;/span&gt;  &lt;span class=&#34;m&#34;&gt;1344&lt;/span&gt; DROP       all  --  *      *       10.211.55.9          0.0.0.0/0

Chain FORWARD &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy DROP &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  *      virbr0  0.0.0.0/0            192.168.122.0/24     ctstate RELATED,ESTABLISHED
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  virbr0 *       192.168.122.0/24     0.0.0.0/0
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  virbr0 virbr0  0.0.0.0/0            0.0.0.0/0
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     all  --  *      virbr0  0.0.0.0/0            0.0.0.0/0            reject-with icmp-port-unreachable
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     all  --  virbr0 *       0.0.0.0/0            0.0.0.0/0            reject-with icmp-port-unreachable

Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;80&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;7108&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     udp  --  *      virbr0  0.0.0.0/0            0.0.0.0/0            udp dpt:68
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;保存规则&#34;&gt;保存规则&lt;/h2&gt;
&lt;p&gt;通常情况下，我们对 &lt;code&gt;iptables&lt;/code&gt; 做的操作都是临时的。就好比在 &lt;code&gt;word&lt;/code&gt; 中写了一段话，假如强制关闭了 &lt;code&gt;word&lt;/code&gt; 或者重启了电脑，那么刚才所些的那段话也会消失。当我们保存成 &lt;code&gt;word&lt;/code&gt; 文件后， 那段话就不会丢失。同理，设置 &lt;code&gt;iptables&lt;/code&gt; 后也需要保存。&lt;/p&gt;
&lt;p&gt;因为在 &lt;code&gt;centos 7&lt;/code&gt; 中，默认的防火墙服务是 &lt;code&gt;firewalld&lt;/code&gt; ，而不是 &lt;code&gt;iptables&lt;/code&gt; 。所以我们需要安装 &lt;code&gt;iptables&lt;/code&gt; 服务，替换默认防火墙。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;yum install -y iptables-services
&lt;span class=&#34;c1&#34;&gt;# 关闭 firewalld 服务&lt;/span&gt;
systemctl stop firewalld
&lt;span class=&#34;c1&#34;&gt;# 禁止 firewalld 服务开机启动&lt;/span&gt;
systemctl disable firewalld
&lt;span class=&#34;c1&#34;&gt;# 开启 iptables 服务&lt;/span&gt;
systemctl start iptables
&lt;span class=&#34;c1&#34;&gt;# 设置 iptables 服务开机启动&lt;/span&gt;
systemctl &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt; iptables
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;安装 iptables 服务成功后，即可保存规则：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;service iptables save
iptables: Saving firewall rules to /etc/sysconfig/iptables:&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;  OK  &lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;我们可以看到，所有的规则被写入了 &lt;code&gt;/etc/sysconfig/iptables&lt;/code&gt; 文件。&lt;/p&gt;
&lt;p&gt;这里还有一种手动保存方法，当我们运行 &lt;code&gt;iptables-save&lt;/code&gt; 命令时，会有将所有当前的规则输出：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;20
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;iptables-save
# Generated by iptables-save v1.4.21 on Fri Aug 17 16:51:10 2018
*security
:INPUT ACCEPT [2409:202386]
...
*raw
:PREROUTING ACCEPT [2413:203008]
...
*mangle
:PREROUTING ACCEPT [2413:203008]
...
*nat
:PREROUTING ACCEPT [4:622]
...
*filter
...
-A INPUT -s 10.211.55.9/32 -j REJECT --reject-with icmp-port-unreachable
-A INPUT -s 10.211.55.9/32 -j DROP
...
COMMIT
# Completed on Fri Aug 17 16:51:10 2018
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;所以只要将输出重定向到 &lt;code&gt;/etc/sysconfig/iptables&lt;/code&gt; 文件，也可以完成保存操作:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables-save &amp;gt; /etc/sysconfig/iptables
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;同时可以使用 &lt;code&gt;iptables-restore&lt;/code&gt; 命令恢复 &lt;code&gt;iptables&lt;/code&gt; 规则，现有规则会被覆盖:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables-restore &amp;lt; /etc/sysconfig/iptables
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      
    </item>
    
    <item>
      <title>iptables详解-2-查询命令</title>
      <link>https://kiyonlin.github.io/post/work/iptables/iptables-2-%E6%9F%A5%E8%AF%A2%E5%91%BD%E4%BB%A4/</link>
      <pubDate>Wed, 15 Aug 2018 11:37:05 +0000</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/iptables/iptables-2-%E6%9F%A5%E8%AF%A2%E5%91%BD%E4%BB%A4/</guid>
      
        <description>&lt;p&gt;本章介绍 &lt;code&gt;iptables&lt;/code&gt; 常用的查询命令。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;pre&gt;&lt;code&gt;  注意
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;p&gt;本系列文章测试环境为 &lt;code&gt;centos 7&lt;/code&gt;，&lt;code&gt;iptables&lt;/code&gt; 版本 &lt;code&gt;1.4.21&lt;/code&gt;。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;查看表规则&#34;&gt;查看表规则&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; 内置了5张表，分别是 &lt;code&gt;raw&lt;/code&gt;、 &lt;code&gt;nat&lt;/code&gt;、 &lt;code&gt;mangle&lt;/code&gt;、 &lt;code&gt;filter&lt;/code&gt;、 &lt;code&gt;security&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;我们要查看 &lt;code&gt;filter&lt;/code&gt; 表下的所有规则，可以使用如下命令：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -t filter -L
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
ACCEPT     udp  --  anywhere             anywhere             udp dpt:domain
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:domain
ACCEPT     udp  --  anywhere             anywhere             udp dpt:bootps
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:bootps

Chain FORWARD &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
ACCEPT     all  --  anywhere             192.168.122.0/24     ctstate RELATED,ESTABLISHED
ACCEPT     all  --  192.168.122.0/24     anywhere
ACCEPT     all  --  anywhere             anywhere
REJECT     all  --  anywhere             anywhere             reject-with icmp-port-unreachable
REJECT     all  --  anywhere             anywhere             reject-with icmp-port-unreachable

Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
ACCEPT     udp  --  anywhere             anywhere             udp dpt:bootpc
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;其他查看其他表的命令也是类似的。当省略 &lt;code&gt;-t filter&lt;/code&gt; 时，默认列出的是 &lt;code&gt;filter&lt;/code&gt; 表的规则。&lt;/p&gt;
&lt;h2 id=&#34;查看表的指定链&#34;&gt;查看表的指定链&lt;/h2&gt;
&lt;p&gt;参数中可以增加链名，指定查看表的具体链规则：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nL INPUT
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
target     prot opt &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:53
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:53
ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0            udp dpt:67
ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:67
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;查看规则详情&#34;&gt;查看规则详情&lt;/h2&gt;
&lt;p&gt;使用 &lt;code&gt;-v, --verbose&lt;/code&gt; 选项列出更详细的信息：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -vL
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT 724K packets, 80M bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     udp  --  virbr0 any     anywhere             anywhere             udp dpt:domain
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     tcp  --  virbr0 any     anywhere             anywhere             tcp dpt:domain
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     udp  --  virbr0 any     anywhere             anywhere             udp dpt:bootps
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     tcp  --  virbr0 any     anywhere             anywhere             tcp dpt:bootps

Chain FORWARD &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  any    virbr0  anywhere             192.168.122.0/24     ctstate RELATED,ESTABLISHED
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  virbr0 any     192.168.122.0/24     anywhere
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     all  --  virbr0 virbr0  anywhere             anywhere
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     all  --  any    virbr0  anywhere             anywhere             reject-with icmp-port-unreachable
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; REJECT     all  --  virbr0 any     anywhere             anywhere             reject-with icmp-port-unreachable

Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT 710K packets, 74M bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; ACCEPT     udp  --  any    virbr0  anywhere             anywhere             udp dpt:bootpc
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;字段含义：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;pkts: 报文数量&lt;/li&gt;
&lt;li&gt;bytes: 报文大小&lt;/li&gt;
&lt;li&gt;target: target动作&lt;/li&gt;
&lt;li&gt;port: 协议&lt;/li&gt;
&lt;li&gt;opt: 选项&lt;/li&gt;
&lt;li&gt;in: 入口网卡&lt;/li&gt;
&lt;li&gt;out: 出口网卡&lt;/li&gt;
&lt;li&gt;source: 源ip/ip段&lt;/li&gt;
&lt;li&gt;destination: 目标ip/ip段&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;链策略-policy和精确报文数据&#34;&gt;链策略 Policy和精确报文数据&lt;/h3&gt;
&lt;p&gt;表中每个链都包含了策略 &lt;code&gt;Policy&lt;/code&gt;，总报文数和总报文大小。&lt;code&gt;policy ACCEPT&lt;/code&gt;表示链的默认策略为 &lt;code&gt;ACCEPT&lt;/code&gt;。 总报文数和总报文大小可以使用 &lt;code&gt;-x, --exact&lt;/code&gt; 选项显示精确的数据:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -vnxL -t nat
Chain PREROUTING &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;6856&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;469577&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
    pkts      bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination

Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;6661&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;424028&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
    pkts      bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination

Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;97282&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;5880717&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
    pkts      bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination

Chain POSTROUTING &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;97282&lt;/span&gt; packets, &lt;span class=&#34;m&#34;&gt;5880717&lt;/span&gt; bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
    pkts      bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
       &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; RETURN     all  --  *      *       192.168.122.0/24     224.0.0.0/24
       &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; RETURN     all  --  *      *       192.168.122.0/24     255.255.255.255
       &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; MASQUERADE  tcp  --  *      *       192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
       &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; MASQUERADE  udp  --  *      *       192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
       &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;        &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; MASQUERADE  all  --  *      *       192.168.122.0/24    !192.168.122.0/24
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;名称解析&#34;&gt;名称解析&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; 默认进行了名称解析，这会牺牲效率，可以使用 &lt;code&gt;-n, --numeric&lt;/code&gt; 选项直接显示ip。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -nvL -t nat
Chain PREROUTING &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;6854&lt;/span&gt; packets, 469K bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination

Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;6659&lt;/span&gt; packets, 424K bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination

Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;96829&lt;/span&gt; packets, 5853K bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination

Chain POSTROUTING &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT &lt;span class=&#34;m&#34;&gt;96829&lt;/span&gt; packets, 5853K bytes&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
 pkts bytes target     prot opt in     out     &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; RETURN     all  --  *      *       192.168.122.0/24     224.0.0.0/24
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; RETURN     all  --  *      *       192.168.122.0/24     255.255.255.255
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; MASQUERADE  tcp  --  *      *       192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; MASQUERADE  udp  --  *      *       192.168.122.0/24    !192.168.122.0/24     masq ports: 1024-65535
    &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt;     &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; MASQUERADE  all  --  *      *       192.168.122.0/24    !192.168.122.0/24
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id=&#34;显示规则序号&#34;&gt;显示规则序号&lt;/h2&gt;
&lt;p&gt;使用 &amp;ndash;line[-number] 展示规则行号：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt; 1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 7
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 8
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt; 9
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;10
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;11
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;12
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;13
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;14
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;15
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;16
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;17
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;18
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;19
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;iptables -L --line
Chain INPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
num  target     prot opt &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;    ACCEPT     udp  --  anywhere             anywhere             udp dpt:domain
&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;    ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:domain
&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;    ACCEPT     udp  --  anywhere             anywhere             udp dpt:bootps
&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;    ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:bootps

Chain FORWARD &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
num  target     prot opt &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;    ACCEPT     all  --  anywhere             192.168.122.0/24     ctstate RELATED,ESTABLISHED
&lt;span class=&#34;m&#34;&gt;2&lt;/span&gt;    ACCEPT     all  --  192.168.122.0/24     anywhere
&lt;span class=&#34;m&#34;&gt;3&lt;/span&gt;    ACCEPT     all  --  anywhere             anywhere
&lt;span class=&#34;m&#34;&gt;4&lt;/span&gt;    REJECT     all  --  anywhere             anywhere             reject-with icmp-port-unreachable
&lt;span class=&#34;m&#34;&gt;5&lt;/span&gt;    REJECT     all  --  anywhere             anywhere             reject-with icmp-port-unreachable

Chain OUTPUT &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;policy ACCEPT&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
num  target     prot opt &lt;span class=&#34;nb&#34;&gt;source&lt;/span&gt;               destination
&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt;    ACCEPT     udp  --  anywhere             anywhere             udp dpt:bootpc
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      
    </item>
    
    <item>
      <title>iptables详解-1-简介</title>
      <link>https://kiyonlin.github.io/post/work/iptables/iptables-1-%E7%AE%80%E4%BB%8B/</link>
      <pubDate>Wed, 15 Aug 2018 08:45:33 +0000</pubDate>
      
      <guid>https://kiyonlin.github.io/post/work/iptables/iptables-1-%E7%AE%80%E4%BB%8B/</guid>
      
        <description>&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; 是各种防火墙的基础，需要系统学习。本篇先了解 &lt;code&gt;iptables&lt;/code&gt; 的一些基本概念。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;pre&gt;&lt;code&gt;  注意
&lt;/code&gt;&lt;/pre&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;p&gt;本系列文章测试环境为 &lt;code&gt;centos 7&lt;/code&gt;，&lt;code&gt;iptables&lt;/code&gt; 版本 &lt;code&gt;1.4.21&lt;/code&gt;。&lt;/p&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;!-- raw HTML omitted --&gt;
&lt;h2 id=&#34;说明&#34;&gt;说明&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; 是用来设置、维护和检查 &lt;code&gt;Linux&lt;/code&gt; 内核的 &lt;code&gt;IP&lt;/code&gt; 包过滤规则的。用户可以定义不同的表，每个表都包含几个内部的链，也能包含用户定义的链。每个链都是一个规则列表，与对应的包进行匹配：每条规则指定应当如何处理与之相匹配的包，这被称作 &lt;code&gt;target&lt;/code&gt;（目标），也可以跳向同一个表内的用户定义的链。&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;一般 &lt;code&gt;iptables&lt;/code&gt; 设置都是从 &lt;code&gt;表&lt;/code&gt; 出发，到 &lt;code&gt;链 &lt;/code&gt;再到 &lt;code&gt;规则&lt;/code&gt;。&lt;/p&gt;
&lt;h2 id=&#34;表&#34;&gt;表&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;iptables&lt;/code&gt; 的核心之一就是 &lt;code&gt;table&lt;/code&gt;，它是具有相同功能的规则集合。iptables 提供了5张表：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;filter&lt;/code&gt; - 主要提供过滤功能，是默认的表。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nat&lt;/code&gt; - 主要提供网络地址转换功能，实现数据包转发，修改源地址与端口和目标地址与端口。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mangle&lt;/code&gt; - 主要提供修改报文的功能。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;raw&lt;/code&gt; - 应用在不需要做 &lt;code&gt;nat&lt;/code&gt; 的情况下，以提高性能。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;security&lt;/code&gt; - 主要提供 &lt;code&gt;MAC&lt;/code&gt; 网络规则。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;链&#34;&gt;链&lt;/h2&gt;
&lt;p&gt;报文需要经过一系列阶段，每个阶段可以应用各种规则，这样的一个阶段在 iptables 中称为&lt;code&gt;链(chain)&lt;/code&gt;。各种阶段串起来就是一条&lt;code&gt;长链&lt;/code&gt;。 iptables 中有5条默认的链：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PREROUTING&lt;/li&gt;
&lt;li&gt;INPUT&lt;/li&gt;
&lt;li&gt;FORWARD&lt;/li&gt;
&lt;li&gt;OUTPUT&lt;/li&gt;
&lt;li&gt;POSTROUTING&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;用户也可以创建自定义链，但是只能被默认链调用。&lt;/p&gt;
&lt;h2 id=&#34;报文生命周期&#34;&gt;报文生命周期&lt;/h2&gt;
&lt;p&gt;报文从网卡进入，从网卡流出，会经过5个生命周期，也就是上述的5条默认链。每条链上可以使用&lt;code&gt;相应的表组合&lt;/code&gt;起来进行报文处理。当不同表处于同一条链上时，处理报文就会涉及到顺序，执行的优先级如下：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;raw&lt;/code&gt; -&amp;gt; &lt;code&gt;mangle&lt;/code&gt; -&amp;gt; &lt;code&gt;nat&lt;/code&gt; -&amp;gt; &lt;code&gt;filter&lt;/code&gt; -&amp;gt; &lt;code&gt;security&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;总结成一张图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://kiyonlin.github.io/iptables/lifecycle.jpg&#34; alt=&#34;报文生命周期&#34;&gt;&lt;/p&gt;
&lt;p&gt;同时，我们也可以总结出5张表分别包含了哪些链：
|表|链|
|&amp;mdash;|&amp;mdash;|
|raw|PREROUTING, OUTPUT|
|mangle|PREROUTING, INPUT, FORWARD, OUTPUT, POSTROUTING|
|nat|PREROUTING, INPUT, OUTPUT, POSTROUTING|
|filter|INPUT, FORWARD, OUTPUT|
|security|INPUT, FORWARD, OUTPUT|&lt;/p&gt;
&lt;h2 id=&#34;规则&#34;&gt;规则&lt;/h2&gt;
&lt;p&gt;规则包含了匹配条件和 &lt;code&gt;target&lt;/code&gt;，用来匹配报文信息。匹配不成功，则进行下一条规则检测；匹配成功后执行规则对应的 &lt;code&gt;target&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;target&lt;/code&gt; 可以是基本 &lt;code&gt;target&lt;/code&gt;，用户自定义链，或者扩展中的 &lt;code&gt;target&lt;/code&gt;&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;基本 &lt;code&gt;target&lt;/code&gt; 包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ACCEPT: 让报文通过。&lt;/li&gt;
&lt;li&gt;DROP: 丢弃报文。&lt;/li&gt;
&lt;li&gt;RETURN: 停止遍历本链中的规则，将下一条规则置为&lt;code&gt;前一条&lt;/code&gt;调用的链。&lt;/li&gt;
&lt;/ul&gt;
&lt;section class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;man iptables 8 &lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34; role=&#34;doc-endnote&#34;&gt;
&lt;p&gt;man iptables-extensions 8 &lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
      
    </item>
    
  </channel>
</rss>
