<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title><![CDATA[ZealSingerの博客啦~]]></title> 
<atom:link href="http://8.154.40.120:24180/rss.php" rel="self" type="application/rss+xml" />
<description><![CDATA[]]></description>
<link>http://8.154.40.120:24180/</link>
<language>zh-cn</language>
<generator>www.emlog.net</generator>
<item>
    <title>Python深度学习入门-day01-Python语法基础</title>
    <link>http://8.154.40.120:24180/?post=57</link>
    <description><![CDATA[<div>
<blockquote>
<p class="md-end-block md-p md-focus"><span class="md-plain md-expand">主要的参考资料来源</span> <span class="md-plain">《深度学习入门：基于Python的理论与实现 (斋藤康毅) 》（鱼书）</span> <span class="md-plain">B站Up主爆肝杰哥的相关视频</span> <span class="md-plain">主要考虑到是入门以及快速能上手工程，没有基础，不能在数学等其余问题上过多的纠结，本身掌握程度不够，所以不会以李沐老师的课成为第一课程，后续才会去看</span> <span class="md-plain">此外，因为是为了深度学习才接触的Python，并非专攻Python方向，所以对于Python的很多其他的特性也不会涉及，一切以深度学习的需要进行学习</span></p>
</blockquote>
<h1 class="md-end-block md-heading"><span class="md-plain">前言</span></h1>
<p class="md-end-block md-p"><span class="md-plain">在深度学习领域，我们主要是利用Python以及几个库进行，分别是</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>NumPy</code></span><span class="md-plain">-为Python加上了关键的数组变量类型</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>Pandas</code></span><span class="md-plain">-在NumPy的基础上添加了类似Excel的行列标签</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>Matplotlib</code></span><span class="md-plain">-借鉴</span><span class="md-pair-s" spellcheck="false"><code>Matlab</code></span><span class="md-plain">，为Python提供了绘图能力</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>Scikit-learn</code></span><span class="md-plain">-机器学习库，包含了分类，回归，聚类，将为等多种算法</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>Tensorflow</code></span><span class="md-plain">-Goole研发的深度学习框架</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>PyTorch</code></span><span class="md-plain">-FaceBook研发的深度学习框架</span></p>
</li>
</ul>
<h2 class="md-end-block md-heading"><span class="md-plain">深度学习的相关概念</span></h2>
<p class="md-end-block md-p"><span class="md-plain">大家可能听说过人工智能；机器学习；神经网络；深度学习等多个专有名词，实际上这几个都是有联系的</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">人工智能是最大的一个概念，最顶层的，其中一个重要的分支就是机器学习</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">机器学习的算法多种多样，其中核心就是神经网络</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">神经网络的隐藏层如果够深，就可以被称之为深层神经网络，这也属于我们所说的深度学习</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">深度学习包括了深度神经网络；卷积神经网络；循环神经网络</span></p>
</li>
</ul>
<h1 class="md-end-block md-heading"><span class="md-plain">变量类型与输出语句</span></h1>
<h2 class="md-end-block md-heading"><span class="md-plain">总览</span></h2>
<p class="md-end-block md-p"><span class="md-plain">学习Python首先需要注意如下几点</span></p>
<ol class="ol-list" start="">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">Python的注释是采用</span><span class="md-pair-s" spellcheck="false"><code>#</code></span><span class="md-plain">而不是 </span><span class="md-pair-s" spellcheck="false"><code>//</code></span><span class="md-plain">，多行注释要采用三个双引号</span><span class="md-pair-s" spellcheck="false"><code>"""..."""</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>Python是动态语言类型不是静态</code></span><span class="md-plain">，在声明变量的时候不需要指定变量类型，</span><span class="md-pair-s" spellcheck="false"><code>如果需要指定数据类型，可以使用类型注解</code></span><span class="md-plain">（但是即使使用，也只是作为提示作用，不会受到编译器强制校验）</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">Python</span><span class="md-pair-s" spellcheck="false"><code>没有末尾分号</code></span><span class="md-plain">，但是有比较烦人的</span><span class="md-pair-s" spellcheck="false"><code>缩进</code></span><span class="md-plain">，所以不要在Python中随便使用空格</span></p>
</li>
</ol>
<p class="md-end-block md-p"><span class="md-plain">首先我们来看看如何定义一个变量</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span class="cm-comment"># 直接声明和初始化即可</span><br><span role="presentation"><span class="cm-variable">a</span> <span class="cm-operator">=</span> <span class="cm-number">2</span> &nbsp;</span><br><span class="cm-comment"># 如果需要指定类型 可以使用类型注解，即name:type的形式</span><br><span role="presentation"><span class="cm-variable">b</span>:<span class="cm-builtin">int</span> <span class="cm-operator">=</span> <span class="cm-number">2</span> &nbsp;</span><br><span class="cm-comment"># 但是类型注解只是起到提示的作用，编译器并不会校验，所以如下代码也不会报错</span><br><span role="presentation"><span class="cm-variable">c</span>:<span class="cm-builtin">int</span> <span class="cm-operator">=</span> <span class="cm-string">"abc"</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">Python原生的数据类型分为两大类：</span><span class="md-pair-s" spellcheck="false"><code>基本变量类型</code></span><span class="md-plain">和</span><span class="md-pair-s" spellcheck="false"><code>高级变量类型</code></span><span class="md-plain">，一共七种数据类型</span> <span class="md-pair-s" spellcheck="false"><code>基本数据类型包括：字符串，数字，布尔类型</code></span><span class="md-plain"> ； </span><span class="md-pair-s" spellcheck="false"><code>高级变量类型包括：集合，元组，列表，字典</code></span> <span class="md-plain">七种类型展示如下，需要注意一下几点</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">Python不能够声明但是不赋值，这是因为在Python中变量只有被赋值的时候才是真正被创建，在其他语言中，例如Java中允许</span><span class="md-pair-s" spellcheck="false"><code>int a;</code></span><span class="md-plain">这是因为在Java中声明即创建了对应的内存空间，a已经存在只是没有数值</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">Python中的布尔类型严格遵循大小写，只能是</span><span class="md-pair-s" spellcheck="false"><code>True</code></span><span class="md-plain">和</span><span class="md-pair-s" spellcheck="false"><code>False</code></span><span class="md-plain">，而不能是</span><span class="md-pair-s" spellcheck="false"><code>true</code></span><span class="md-plain">或</span><span class="md-pair-s" spellcheck="false"><code>false</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">Python四大高级变量类型，</span><span class="md-pair-s" spellcheck="false"><code>set集合是使用大括号{} ； list列表使用中括号[] ； tuple元组使用的小括号() ； dict字典是大括号但为Key-Value的形式</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">list集合：</span><span class="md-pair-s" spellcheck="false"><code>有序</code></span><span class="md-plain">，</span><span class="md-pair-s" spellcheck="false"><code>支持下标访问</code></span><span class="md-plain">，</span><span class="md-pair-s" spellcheck="false"><code>地址不可变但是内容可变</code></span><span class="md-plain">，</span><span class="md-pair-s" spellcheck="false"><code>支持增删改</code></span><span class="md-plain">，</span><span class="md-pair-s" spellcheck="false"><code>允许重复</code></span><span class="md-plain">，空列表使用</span><span class="md-pair-s" spellcheck="false"><code>[]或者list()</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">tuple元组：</span><span class="md-pair-s" spellcheck="false"><code>有序</code></span><span class="md-plain">，同列表</span><span class="md-pair-s" spellcheck="false"><code>可下标访问</code></span><span class="md-plain">；</span><strong><span class="md-plain">不可变，一旦确认不能修改，不能增删改</span></strong><span class="md-plain">；</span><span class="md-pair-s" spellcheck="false"><code>允许重复</code></span><span class="md-plain">；空元组使用</span><span class="md-pair-s" spellcheck="false"><code>()或者tuple()</code></span><span class="md-plain">；</span><strong><span class="md-plain">如果是只有一个元素的元组创建，必须加一个逗号(1,)</span></strong></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">set集合：</span><span class="md-pair-s" spellcheck="false"><code>无序</code></span><span class="md-plain">,不能下标查找；</span><span class="md-pair-s" spellcheck="false"><code>可变，支持增删，但不能修改已有元素</code></span><span class="md-plain">；</span><strong><span class="md-plain">不可重复，自动去重</span></strong><span class="md-plain">；空集合使用</span><span class="md-pair-s" spellcheck="false"><code>set()</code></span><span class="md-plain">；</span><strong><span class="md-plain">支持集合运算符交集(&amp;),并集( | ),差集(-)</span></strong><span class="md-plain">等</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">dict字典：</span><strong><span class="md-plain">3.7版本及其以上默认按照插入顺序排序；3.7之前无序</span></strong><span class="md-plain"> ； 可变 </span><span class="md-pair-s" spellcheck="false"><code>Key唯一，重复会覆盖原本的</code></span><span class="md-plain"> ； Value可重复 ；</span><span class="md-pair-s" spellcheck="false"><code>Key必须是不可变类型(例如int,str,tuple；列表/集合/字典就不能作为Key)</code></span><span class="md-plain"> ； 空字典使用</span><span class="md-pair-s" spellcheck="false"><code>dict()或者{}</code></span><span class="md-plain">，只能通过键值对访问而不能通过索引访问</span></p>
</li>
</ul>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span class="cm-comment"># 声明但不赋值 这个在python中是不允许的</span><br><span role="presentation"><span class="cm-variable">s</span>:<span class="cm-builtin">int</span></span><br><span role="presentation">​</span><br><span class="cm-comment"># 基本变量类型 &nbsp;</span><br><span role="presentation"><span class="cm-variable">s</span> <span class="cm-operator">=</span> <span class="cm-string">"zeal"</span> <span class="cm-comment"># 字符串 &nbsp;</span></span><br><span class="cm-comment"># 数字类型 &nbsp;</span><br><span role="presentation"><span class="cm-variable">age</span> <span class="cm-operator">=</span> <span class="cm-number">22</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">height</span> <span class="cm-operator">=</span> <span class="cm-number">1.75</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">weight</span> <span class="cm-operator">=</span> <span class="cm-number">120.0</span> &nbsp;</span><br><span class="cm-comment"># 布尔类型 &nbsp;</span><br><span role="presentation"><span class="cm-variable">bool_t</span> <span class="cm-operator">=</span> <span class="cm-keyword">True</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">bool_f</span> <span class="cm-operator">=</span> <span class="cm-keyword">False</span> &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment"># 高级变量类型 &nbsp;</span><br><span class="cm-comment">#集合set 利用大括号包裹{} &nbsp;</span><br><span role="presentation"><span class="cm-variable">set_v</span> <span class="cm-operator">=</span> {<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>} &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">set_v</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment cm-error"># 元组tuple 利用 小括号包裹() &nbsp;</span><br><span role="presentation"><span class="cm-variable">tuple_v</span> <span class="cm-operator">=</span> (<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">tuple_v</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment"># 列表 &nbsp;</span><br><span role="presentation"><span class="cm-variable">list_v</span> <span class="cm-operator">=</span> [<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-number">4</span>,<span class="cm-number">5</span>] &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment"># 字典dict &nbsp;</span><br><span role="presentation"><span class="cm-variable">dict_v</span> <span class="cm-operator">=</span> {<span class="cm-string">"name"</span>:<span class="cm-string">"zeal"</span>,<span class="cm-string">"age"</span>:<span class="cm-number">22</span>} &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">dict_v</span>)</span></pre>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/20260128152551255.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/20260128152551255.png" alt="image.png"></span></p>
<figure class="md-table-fig table-figure">
<table class="md-table">
<thead>
<tr class="md-end-block">
<th><span class="md-plain">特性</span></th>
<th><span class="md-plain">list（列表）</span></th>
<th><span class="md-plain">tuple（元组）</span></th>
<th><span class="md-plain">set（集合）</span></th>
<th><span class="md-plain">dict（字典）</span></th>
</tr>
</thead>
<tbody>
<tr class="md-end-block">
<td><span class="md-plain">有序性</span></td>
<td><span class="md-plain">是</span></td>
<td><span class="md-plain">是</span></td>
<td><span class="md-plain">否</span></td>
<td><span class="md-plain">3.7+是，3.6及以下否</span></td>
</tr>
<tr class="md-end-block">
<td><span class="md-plain">可变性</span></td>
<td><span class="md-plain">是（支持增/删/改元素）</span></td>
<td><span class="md-plain">否（创建后不可修改）</span></td>
<td><span class="md-plain">是（支持增/删元素，无修改）</span></td>
<td><span class="md-plain">是（支持增/删/改键值对）</span></td>
</tr>
<tr class="md-end-block">
<td><span class="md-plain">元素重复性</span></td>
<td><span class="md-plain">允许重复元素</span></td>
<td><span class="md-plain">允许重复元素</span></td>
<td><span class="md-plain">不允许重复元素（自动去重）</span></td>
<td><span class="md-plain">键唯一（重复覆盖），值允许重复</span></td>
</tr>
<tr class="md-end-block">
<td><span class="md-plain">访问方式</span></td>
<td><span class="md-plain">通过索引（下标）访问</span></td>
<td><span class="md-plain">通过索引（下标）访问</span></td>
<td><span class="td-span"><span class="md-plain">无索引（仅</span><span class="md-pair-s" spellcheck="false"><code>in</code></span><span class="md-plain">判断存在）</span></span></td>
<td><span class="md-plain">通过键（key）访问值</span></td>
</tr>
<tr class="md-end-block">
<td><span class="md-plain">定义符号</span></td>
<td><span class="td-span"><span class="md-pair-s" spellcheck="false"><code>[]</code></span><span class="md-plain">（空列表：</span><span class="md-pair-s" spellcheck="false"><code>[]</code></span><span class="md-plain">/</span><span class="md-pair-s" spellcheck="false"><code>list()</code></span><span class="md-plain">）</span></span></td>
<td><span class="td-span"><span class="md-pair-s" spellcheck="false"><code>()</code></span><span class="md-plain">（空元组：</span><span class="md-pair-s" spellcheck="false"><code>()</code></span><span class="md-plain">/</span><span class="md-pair-s" spellcheck="false"><code>tuple()</code></span><span class="md-plain">）</span></span></td>
<td><span class="td-span"><span class="md-pair-s" spellcheck="false"><code>{}</code></span><span class="md-plain">（空集合：</span><span class="md-pair-s" spellcheck="false"><code>set()</code></span><span class="md-plain">）</span></span></td>
<td><span class="td-span"><span class="md-pair-s" spellcheck="false"><code>{key: value}</code></span><span class="md-plain">（空字典：</span><span class="md-pair-s" spellcheck="false"><code>{}</code></span><span class="md-plain">/</span><span class="md-pair-s" spellcheck="false"><code>dict()</code></span><span class="md-plain">）</span></span></td>
</tr>
<tr class="md-end-block">
<td><span class="md-plain">键/元素要求</span></td>
<td><span class="md-plain">无特殊要求</span></td>
<td><span class="md-plain">无特殊要求</span></td>
<td><span class="md-plain">元素需为不可变类型</span></td>
<td><span class="md-plain">键需为不可变类型，值无限制</span></td>
</tr>
<tr class="md-end-block">
<td><span class="md-plain">核心用途</span></td>
<td><span class="md-plain">动态有序的序列数据</span></td>
<td><span class="md-plain">固定不变的有序数据</span></td>
<td><span class="md-plain">数据去重/集合运算</span></td>
<td><span class="md-plain">键值对映射（如用户信息、配置）</span></td>
</tr>
<tr class="md-end-block">
<td><span class="md-plain">查找效率</span></td>
<td><span class="md-plain">O(n)（按索引O(1)）</span></td>
<td><span class="md-plain">O(n)（按索引O(1)）</span></td>
<td><span class="td-span"><span class="md-plain">O(1)（</span><span class="md-pair-s" spellcheck="false"><code>in</code></span><span class="md-plain">判断）</span></span></td>
<td><span class="md-plain">O(1)（按键查找）</span></td>
</tr>
</tbody>
</table>
</figure>
<h2 class="md-end-block md-heading"><span class="md-plain">基本变量类型</span></h2>
<h3 class="md-end-block md-heading"><span class="md-plain">字符串类型</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Python中字符串类型，也就是我们别的编程语言中说的String类型，</span><span class="md-pair-s" spellcheck="false"><code>可以使用双引号，也可以使用单引号</code></span><span class="md-plain">,支持两种方式的原因是为了保证当字符串本身有单引号或者双引号的时候可以灵活处理，如果字符串本身同时具备单引号和双引号，那么和别的编程语言一样，就需要使用转义了。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span class="cm-comment"># 当变量中本身具备单引号 &nbsp;</span><br><span role="presentation"><span class="cm-variable">s1</span> <span class="cm-operator">=</span> <span class="cm-string">"I'm a student."</span> &nbsp;<span class="cm-comment"># 使用双引号包裹字符串 防止字符串本身的单引号截断 &nbsp;</span></span><br><span class="cm-comment"># 当变量中本身具备双引号 &nbsp;</span><br><span role="presentation"><span class="cm-variable">s2</span> <span class="cm-operator">=</span> <span class="cm-string">'He said, "I am a student."'</span> &nbsp;<span class="cm-comment"># 使用单引号包裹字符串 防止字符串本身的双引号截断 &nbsp;</span></span><br><span class="cm-comment"># 当变量中同时具备单引号和双引号 &nbsp;</span><br><span role="presentation"><span class="cm-variable">s3</span> <span class="cm-operator">=</span> <span class="cm-string">"He said, \"I'm a student.\""</span> &nbsp;<span class="cm-comment"># 使用转义字符 \ 来表示字符串中的双引号,当然，也可以转义单引号</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">当我们要想组合字符串类型的变量的时候，可以利用 </span><span class="md-pair-s" spellcheck="false"><code>f字符串</code></span><span class="md-plain">，将需要引入的字符串变量在f后的字符串中通过大括号{...}包裹起来从而实现引用，在输出中我们常这么使用</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">name</span> <span class="cm-operator">=</span> <span class="cm-string">"zeal"</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">love</span> <span class="cm-operator">=</span> <span class="cm-string">"play game"</span> &nbsp;</span><br><span class="cm-comment"># 组合字符串为一个新字符串</span><br><span role="presentation"><span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-string">f"My name is </span>{<span class="cm-variable">name</span>}<span class="cm-string"> and I love </span>{<span class="cm-variable">love</span>}<span class="cm-string">"</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">result</span>)</span><br><span class="cm-comment"># 直接在打印语句中组合字符串 &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"My name is </span>{<span class="cm-variable">name</span>}<span class="cm-string"> and I love </span>{<span class="cm-variable">love</span>}<span class="cm-string">"</span>)</span></pre>
<p class="md-end-block md-p"><span class="md-plain">当然，</span><span class="md-pair-s" spellcheck="false"><code>print</code></span><span class="md-plain">还有另外一个用法，直接利用逗号</span><span class="md-pair-s" spellcheck="false"><code>,</code></span><span class="md-plain">进行拼接，但是因为默认</span><span class="md-pair-s" spellcheck="false"><code>,</code></span><span class="md-plain">会用一个空格隔开，我们可以在最后用</span><span class="md-pair-s" spellcheck="false"><code>sep=</code></span><span class="md-plain">设置间隔符从而实现无间隔拼接</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"my name is"</span>,<span class="cm-variable">name</span>,<span class="cm-string">"and age is"</span>,<span class="cm-variable">age</span>,<span class="cm-variable">sep</span><span class="cm-operator">=</span><span class="cm-string">" "</span>)</span></pre>
<h3 class="md-end-block md-heading"><span class="md-plain">数字类型</span></h3>
<p class="md-end-block md-p"><span class="md-plain">数字类型分为整型和浮点型，具体的区别我们在NumPy中再去介绍。</span> <span class="md-plain">Python中常见运算规则如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python " spellcheck="false"><span class="cm-comment"># 1. 算术运算符</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">a</span> <span class="cm-operator">=</span> <span class="cm-number">10</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">b</span> <span class="cm-operator">=</span> <span class="cm-number">3</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"a = </span>{<span class="cm-variable">a</span>}<span class="cm-string">, b = </span>{<span class="cm-variable">b</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"加法: a + b = </span>{<span class="cm-variable">a</span>}<span class="cm-string"> + </span>{<span class="cm-variable">b</span>}<span class="cm-string"> = </span>{<span class="cm-variable">a</span> <span class="cm-operator">+</span> <span class="cm-variable">b</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"减法: a - b = </span>{<span class="cm-variable">a</span>}<span class="cm-string"> - </span>{<span class="cm-variable">b</span>}<span class="cm-string"> = </span>{<span class="cm-variable">a</span> <span class="cm-operator">-</span> <span class="cm-variable">b</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"乘法: a * b = </span>{<span class="cm-variable">a</span>}<span class="cm-string"> * </span>{<span class="cm-variable">b</span>}<span class="cm-string"> = </span>{<span class="cm-variable">a</span> <span class="cm-operator">*</span> <span class="cm-variable">b</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span class="cm-comment">#需要注意python中的单个/是完整的除法 也就是会计算完</span><br><span class="cm-comment">#别的语言中 / 是整除，python中 // 才是整除</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"除法: a / b = </span>{<span class="cm-variable">a</span>}<span class="cm-string"> / </span>{<span class="cm-variable">b</span>}<span class="cm-string"> = </span>{<span class="cm-variable">a</span> <span class="cm-operator">/</span> <span class="cm-variable">b</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"整除: a // b = </span>{<span class="cm-variable">a</span>}<span class="cm-string"> // </span>{<span class="cm-variable">b</span>}<span class="cm-string"> = </span>{<span class="cm-variable">a</span> <span class="cm-operator">//</span> <span class="cm-variable">b</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"取余: a % b = </span>{<span class="cm-variable">a</span>}<span class="cm-string"> % </span>{<span class="cm-variable">b</span>}<span class="cm-string"> = </span>{<span class="cm-variable">a</span> <span class="cm-operator">%</span> <span class="cm-variable">b</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"幂运算: a ** b = </span>{<span class="cm-variable">a</span>}<span class="cm-string"> ** </span>{<span class="cm-variable">b</span>}<span class="cm-string"> = </span>{<span class="cm-variable">a</span> <span class="cm-operator">**</span> <span class="cm-variable">b</span>}<span class="cm-string">"</span>) &nbsp;<span class="cm-comment"># a的b次方,即a^b</span></span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment"># 2. 比较运算符</span><br><span class="cm-comment"># 等于为== 不等于为!= 可以直接使用&gt;= 和 &lt;=</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">x</span> <span class="cm-operator">=</span> <span class="cm-number">15</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">y</span> <span class="cm-operator">=</span> <span class="cm-number">20</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"x = </span>{<span class="cm-variable">x</span>}<span class="cm-string">, y = </span>{<span class="cm-variable">y</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"等于: x == y -&gt; </span>{<span class="cm-variable">x</span>}<span class="cm-string"> == </span>{<span class="cm-variable">y</span>}<span class="cm-string"> = </span>{<span class="cm-variable">x</span> <span class="cm-operator">==</span> <span class="cm-variable">y</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"不等于: x != y -&gt; </span>{<span class="cm-variable">x</span>}<span class="cm-string"> != </span>{<span class="cm-variable">y</span>}<span class="cm-string"> = </span>{<span class="cm-variable">x</span> <span class="cm-operator">!=</span> <span class="cm-variable">y</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"大于: x &gt; y -&gt; </span>{<span class="cm-variable">x</span>}<span class="cm-string"> &gt; </span>{<span class="cm-variable">y</span>}<span class="cm-string"> = </span>{<span class="cm-variable">x</span> <span class="cm-operator">&gt;</span> <span class="cm-variable">y</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"小于: x &lt; y -&gt; </span>{<span class="cm-variable">x</span>}<span class="cm-string"> &lt; </span>{<span class="cm-variable">y</span>}<span class="cm-string"> = </span>{<span class="cm-variable">x</span> <span class="cm-operator">&lt;</span> <span class="cm-variable">y</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"大于等于: x &gt;= y -&gt; </span>{<span class="cm-variable">x</span>}<span class="cm-string"> &gt;= </span>{<span class="cm-variable">y</span>}<span class="cm-string"> = </span>{<span class="cm-variable">x</span> <span class="cm-operator">&gt;=</span> <span class="cm-variable">y</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"小于等于: x &lt;= y -&gt; </span>{<span class="cm-variable">x</span>}<span class="cm-string"> &lt;= </span>{<span class="cm-variable">y</span>}<span class="cm-string"> = </span>{<span class="cm-variable">x</span> <span class="cm-operator">&lt;=</span> <span class="cm-variable">y</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment"># 3. 赋值运算符 (Assignment Operators)print("\n【3】赋值运算符 (Assignment Operators)") &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">c</span> <span class="cm-operator">=</span> <span class="cm-number">5</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"初始值: c = </span>{<span class="cm-variable">c</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable">c</span> <span class="cm-operator">+=</span> <span class="cm-number">3</span> &nbsp; &nbsp; &nbsp;<span class="cm-comment"># c = c + 3 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"加法赋值: c += 3 -&gt; c = </span>{<span class="cm-variable">c</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable">c</span> <span class="cm-operator">-=</span> <span class="cm-number">2</span> &nbsp; &nbsp; &nbsp;<span class="cm-comment"># c = c - 2 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"减法赋值: c -= 2 -&gt; c = </span>{<span class="cm-variable">c</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable">c</span> <span class="cm-operator">*=</span> <span class="cm-number">4</span> &nbsp; &nbsp; &nbsp;<span class="cm-comment"># c = c * 4 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"乘法赋值: c *= 4 -&gt; c = </span>{<span class="cm-variable">c</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable">c</span> <span class="cm-operator">/=</span> <span class="cm-number">2</span> &nbsp; &nbsp; &nbsp;<span class="cm-comment"># c = c / 2 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"除法赋值: c /= 2 -&gt; c = </span>{<span class="cm-variable">c</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable">c</span> <span class="cm-operator">//=</span> <span class="cm-number">3</span> &nbsp; &nbsp; <span class="cm-comment"># c = c // 3 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"整除赋值: c //= 3 -&gt; c = </span>{<span class="cm-variable">c</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable">c</span> <span class="cm-operator">%=</span> <span class="cm-number">4</span> &nbsp; &nbsp; &nbsp;<span class="cm-comment"># c = c % 4 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"取余赋值: c %= 4 -&gt; c = </span>{<span class="cm-variable">c</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable">c</span> <span class="cm-operator">**=</span> <span class="cm-number">3</span> &nbsp; &nbsp; <span class="cm-comment"># c = c ** 3 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"幂运算赋值: c **= 3 -&gt; c = </span>{<span class="cm-variable">c</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment"># 4. 位运算符 </span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">m</span> <span class="cm-operator">=</span> <span class="cm-number">12</span> &nbsp; &nbsp; &nbsp;<span class="cm-comment"># 二进制: 1100 &nbsp;</span></span><br><span role="presentation"><span class="cm-variable">n</span> <span class="cm-operator">=</span> <span class="cm-number">10</span> &nbsp; &nbsp; &nbsp;<span class="cm-comment"># 二进制: 1010 &nbsp;</span></span><br><span class="cm-comment"># 利用bin(number) 可以得到number的二进制</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"m = </span>{<span class="cm-variable">m</span>}<span class="cm-string"> (二进制: </span>{<span class="cm-builtin">bin</span>(<span class="cm-variable">m</span>)}<span class="cm-string">), n = </span>{<span class="cm-variable">n</span>}<span class="cm-string"> (二进制: </span>{<span class="cm-builtin">bin</span>(<span class="cm-variable">n</span>)}<span class="cm-string">)"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"按位与: m &amp; n = </span>{<span class="cm-variable">m</span>}<span class="cm-string"> &amp; </span>{<span class="cm-variable">n</span>}<span class="cm-string"> = </span>{<span class="cm-variable">m</span> <span class="cm-operator">&amp;</span> <span class="cm-variable">n</span>}<span class="cm-string"> (二进制: </span>{<span class="cm-builtin">bin</span>(<span class="cm-variable">m</span> <span class="cm-operator">&amp;</span> <span class="cm-variable">n</span>)}<span class="cm-string">)"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"按位或: m | n = </span>{<span class="cm-variable">m</span>}<span class="cm-string"> | </span>{<span class="cm-variable">n</span>}<span class="cm-string"> = </span>{<span class="cm-variable">m</span> <span class="cm-operator">|</span> <span class="cm-variable">n</span>}<span class="cm-string"> (二进制: </span>{<span class="cm-builtin">bin</span>(<span class="cm-variable">m</span> <span class="cm-operator">|</span> <span class="cm-variable">n</span>)}<span class="cm-string">)"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"按位异或: m ^ n = </span>{<span class="cm-variable">m</span>}<span class="cm-string"> ^ </span>{<span class="cm-variable">n</span>}<span class="cm-string"> = </span>{<span class="cm-variable">m</span> <span class="cm-operator">^</span> <span class="cm-variable">n</span>}<span class="cm-string"> (二进制: </span>{<span class="cm-builtin">bin</span>(<span class="cm-variable">m</span> <span class="cm-operator">^</span> <span class="cm-variable">n</span>)}<span class="cm-string">)"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"按位取反: ~m = ~</span>{<span class="cm-variable">m</span>}<span class="cm-string"> = </span>{<span class="cm-operator">~</span><span class="cm-variable">m</span>}<span class="cm-string"> (二进制: </span>{<span class="cm-builtin">bin</span>(<span class="cm-operator">~</span><span class="cm-variable">m</span> <span class="cm-operator">&amp;</span> <span class="cm-number">0b1111</span>)}<span class="cm-string">)"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"左移: m &lt;&lt; 2 = </span>{<span class="cm-variable">m</span>}<span class="cm-string"> &lt;&lt; 2 = </span>{<span class="cm-variable">m</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-number">2</span>}<span class="cm-string"> (二进制: </span>{<span class="cm-builtin">bin</span>(<span class="cm-variable">m</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-number">2</span>)}<span class="cm-string">)"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"右移: m &gt;&gt; 2 = </span>{<span class="cm-variable">m</span>}<span class="cm-string"> &gt;&gt; 2 = </span>{<span class="cm-variable">m</span> <span class="cm-operator">&gt;&gt;</span> <span class="cm-number">2</span>}<span class="cm-string"> (二进制: </span>{<span class="cm-builtin">bin</span>(<span class="cm-variable">m</span> <span class="cm-operator">&gt;&gt;</span> <span class="cm-number">2</span>)}<span class="cm-string">)"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment"># 5. 逻辑运算符 这里需要注意和别的语言的区别,python的与或非使用的为 and or not 而不是&amp; | !</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">p</span> <span class="cm-operator">=</span> <span class="cm-keyword">True</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">q</span> <span class="cm-operator">=</span> <span class="cm-keyword">False</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"p = </span>{<span class="cm-variable">p</span>}<span class="cm-string">, q = </span>{<span class="cm-variable">q</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"逻辑与: p and q = </span>{<span class="cm-variable">p</span>}<span class="cm-string"> and </span>{<span class="cm-variable">q</span>}<span class="cm-string"> = </span>{<span class="cm-variable">p</span> <span class="cm-keyword">and</span> <span class="cm-variable">q</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"逻辑或: p or q = </span>{<span class="cm-variable">p</span>}<span class="cm-string"> or </span>{<span class="cm-variable">q</span>}<span class="cm-string"> = </span>{<span class="cm-variable">p</span> <span class="cm-keyword">or</span> <span class="cm-variable">q</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"逻辑非: not p = not </span>{<span class="cm-variable">p</span>}<span class="cm-string"> = </span>{<span class="cm-keyword">not</span> <span class="cm-variable">p</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment"># 6. 成员运算符和身份运算符 </span><br><span class="cm-comment"># 主要用于高级变量类型，检查是否在集合内，in 和 not in</span><br><span class="cm-comment"># 元组，列表，集合，字典都可以使用，对于字典而言，检查的是key不是value</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">num_list</span> <span class="cm-operator">=</span> [<span class="cm-number">1</span>, <span class="cm-number">2</span>, <span class="cm-number">3</span>, <span class="cm-number">4</span>, <span class="cm-number">5</span>] &nbsp;</span><br><span role="presentation"><span class="cm-variable">num1</span> <span class="cm-operator">=</span> <span class="cm-number">10</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">num2</span> <span class="cm-operator">=</span> <span class="cm-number">10</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"列表: </span>{<span class="cm-variable">num_list</span>}<span class="cm-string">, num1 = </span>{<span class="cm-variable">num1</span>}<span class="cm-string">, num2 = </span>{<span class="cm-variable">num2</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"成员运算符 in: 3 in num_list = </span>{<span class="cm-number">3</span> <span class="cm-keyword">in</span> <span class="cm-variable">num_list</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"成员运算符 not in: 6 not in num_list = </span>{<span class="cm-number">6</span> <span class="cm-keyword">not</span> <span class="cm-keyword">in</span> <span class="cm-variable">num_list</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"身份运算符 is: num1 is num2 = </span>{<span class="cm-variable">num1</span> <span class="cm-keyword">is</span> <span class="cm-variable">num2</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"身份运算符 is not: num1 is not num_list = </span>{<span class="cm-variable">num1</span> <span class="cm-keyword">is</span> <span class="cm-keyword">not</span> <span class="cm-variable">num_list</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment"># 7. 运算符优先级示例 &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"\n【7】运算符优先级示例"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">result1</span> <span class="cm-operator">=</span> <span class="cm-number">10</span> <span class="cm-operator">+</span> <span class="cm-number">5</span> <span class="cm-operator">*</span> <span class="cm-number">3</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">result2</span> <span class="cm-operator">=</span> (<span class="cm-number">10</span> <span class="cm-operator">+</span> <span class="cm-number">5</span>) <span class="cm-operator">*</span> <span class="cm-number">3</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">result3</span> <span class="cm-operator">=</span> <span class="cm-number">2</span> <span class="cm-operator">**</span> <span class="cm-number">3</span> <span class="cm-operator">*</span> <span class="cm-number">4</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">result4</span> <span class="cm-operator">=</span> <span class="cm-number">2</span> <span class="cm-operator">**</span> (<span class="cm-number">3</span> <span class="cm-operator">*</span> <span class="cm-number">4</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"乘法优先于加法: 10 + 5 * 3 = </span>{<span class="cm-variable">result1</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"括号改变优先级: (10 + 5) * 3 = </span>{<span class="cm-variable">result2</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"幂运算优先于乘法: 2 ** 3 * 4 = </span>{<span class="cm-variable">result3</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"括号改变优先级: 2 ** (3 * 4) = </span>{<span class="cm-variable">result4</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment"># 8. 不同数字类型之间的运算 &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"\n【8】不同数字类型之间的运算"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">int_num</span> <span class="cm-operator">=</span> <span class="cm-number">10</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">float_num</span> <span class="cm-operator">=</span> <span class="cm-number">3.5</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">complex_num</span> <span class="cm-operator">=</span> <span class="cm-number">2</span> <span class="cm-operator">+</span> <span class="cm-number">3j</span> &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"整数: </span>{<span class="cm-variable">int_num</span>}<span class="cm-string">, 浮点数: </span>{<span class="cm-variable">float_num</span>}<span class="cm-string">, 复数: </span>{<span class="cm-variable">complex_num</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"整数 + 浮点数: </span>{<span class="cm-variable">int_num</span>}<span class="cm-string"> + </span>{<span class="cm-variable">float_num</span>}<span class="cm-string"> = </span>{<span class="cm-variable">int_num</span> <span class="cm-operator">+</span> <span class="cm-variable">float_num</span>}<span class="cm-string"> (类型: </span>{<span class="cm-builtin">type</span>(<span class="cm-variable">int_num</span> <span class="cm-operator">+</span> <span class="cm-variable">float_num</span>)}<span class="cm-string">)"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"整数 + 复数: </span>{<span class="cm-variable">int_num</span>}<span class="cm-string"> + </span>{<span class="cm-variable">complex_num</span>}<span class="cm-string"> = </span>{<span class="cm-variable">int_num</span> <span class="cm-operator">+</span> <span class="cm-variable">complex_num</span>}<span class="cm-string"> (类型: </span>{<span class="cm-builtin">type</span>(<span class="cm-variable">int_num</span> <span class="cm-operator">+</span> <span class="cm-variable">complex_num</span>)}<span class="cm-string">)"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"浮点数 * 复数: </span>{<span class="cm-variable">float_num</span>}<span class="cm-string"> * </span>{<span class="cm-variable">complex_num</span>}<span class="cm-string"> = </span>{<span class="cm-variable">float_num</span> <span class="cm-operator">*</span> <span class="cm-variable">complex_num</span>}<span class="cm-string"> (类型: </span>{<span class="cm-builtin">type</span>(<span class="cm-variable">float_num</span> <span class="cm-operator">*</span> <span class="cm-variable">complex_num</span>)}<span class="cm-string">)"</span>)</span></pre>
<h3 class="md-end-block md-heading"><span class="md-plain">类型转换</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Python中，字符串，浮点数，整数，布尔类型之间都可以转换</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">需要转化为字符串，使用</span><span class="md-pair-s" spellcheck="false"><code>str()</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">需要转化为整数，使用</span><span class="md-pair-s" spellcheck="false"><code>int()</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">需要转化为浮点数，使用</span><span class="md-pair-s" spellcheck="false"><code>float()</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">需要转化为布尔类型，使用</span><span class="md-pair-s" spellcheck="false"><code>bool()</code></span> <span class="md-plain">需要注意，上述的这些属于内置工具函数，而不是别的语言中的强转，但也要注意</span> <span class="md-plain">浮点数字符串不能</span><span class="md-pair-s" spellcheck="false"><code>int()</code></span><span class="md-plain">; </span> <strong><span class="md-plain">非0数字和非空字符串(包括空格组成的字符串)</span></strong><span class="md-plain"> 使用</span><span class="md-pair-s" spellcheck="false"><code>bool()</code></span><span class="md-plain">返回</span><span class="md-pair-s" spellcheck="false"><code>True</code></span><span class="md-plain">;</span> <span class="md-plain">0和串(</span><span class="md-pair-s" spellcheck="false"><code>""</code></span><span class="md-plain">)使用</span><span class="md-pair-s" spellcheck="false"><code>bool()</code></span><span class="md-plain">返回</span><span class="md-pair-s" spellcheck="false"><code>False</code></span></p>
</li>
</ul>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">str_v</span> <span class="cm-operator">=</span> <span class="cm-string">"123.123"</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-builtin">float</span>(<span class="cm-variable">str_v</span>)) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-builtin">bool</span>(<span class="cm-variable">str_v</span>)) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable">number1</span> <span class="cm-operator">=</span> <span class="cm-number">99</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">number2</span> <span class="cm-operator">=</span> <span class="cm-number">0</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-builtin">str</span>(<span class="cm-variable">number1</span>)) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-builtin">str</span>(<span class="cm-variable">number2</span>)) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-builtin">bool</span>(<span class="cm-variable">number2</span>)) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable">bool_v1</span> <span class="cm-operator">=</span> <span class="cm-keyword">True</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">bool_v2</span> <span class="cm-operator">=</span> <span class="cm-keyword">False</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-builtin">str</span>(<span class="cm-variable">bool_v1</span>)) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-builtin">str</span>(<span class="cm-variable">bool_v2</span>)) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-builtin">int</span>(<span class="cm-variable">bool_v1</span>)) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-builtin">int</span>(<span class="cm-variable">bool_v2</span>))</span><br><span role="presentation">​</span></pre>
<h2 class="md-end-block md-heading"><span class="md-plain">高级变量类型</span></h2>
<h3 class="md-end-block md-heading"><span class="md-plain">Set集合</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Set集合的特点：无序 ； 不可重复 ；可以使用</span><span class="md-pair-s" spellcheck="false"><code>-</code></span><span class="md-plain">；</span><span class="md-pair-s" spellcheck="false"><code>|</code></span><span class="md-plain">；</span><span class="md-pair-s" spellcheck="false"><code>&amp;</code></span><span class="md-plain">进行集合的运算；不能修改，只能删除和新增</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">set_1</span> <span class="cm-operator">=</span> {<span class="cm-string">"湖"</span>,<span class="cm-string">"南"</span>,<span class="cm-string">"科"</span>,<span class="cm-string">"专"</span>,<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>} &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">set_1</span>) <span class="cm-comment"># 输出 {1, 2, 3, '南', '专', '湖', '科'}  体现无序</span></span><br><span role="presentation"><span class="cm-variable">set_2</span> <span class="cm-operator">=</span> <span class="cm-builtin">set</span>([<span class="cm-string">"湖"</span>,<span class="cm-string">"南"</span>,<span class="cm-string">"科"</span>,<span class="cm-string">"科"</span>,<span class="cm-number">4</span>,<span class="cm-number">5</span>,<span class="cm-number">6</span>]) &nbsp;<span class="cm-comment"># 类似上面的类型转换，可有元组得到</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">set_2</span>) <span class="cm-comment"># 输出 {4, 5, 6, '南', '湖', '科'} 体现自动去重</span></span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">set_1</span><span class="cm-operator">-</span><span class="cm-variable">set_2</span>) <span class="cm-comment"># set1中有但是set2中没有的 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">set_1</span><span class="cm-operator">|</span><span class="cm-variable">set_2</span>) <span class="cm-comment"># set1或set2中的所有元素 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">set_1</span><span class="cm-operator">&amp;</span><span class="cm-variable">set_2</span>) <span class="cm-comment"># set1和set2中的交集元素</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">我们可以使用 </span><span class="md-pair-s" spellcheck="false"><code>in 和 not in</code></span><span class="md-plain">判断元素是否存在于集合中</span></p>
<p class="md-end-block md-p"><span class="md-plain">可以通过</span><span class="md-pair-s" spellcheck="false"><code>list(set)或者tuple(set)</code></span><span class="md-plain">的方式将集合转换为列表或者元组，从而实现遍历和查找元素</span> <strong><span class="md-plain">如果要修改元素，因为不支持修改，我们只能先删除再添加</span></strong><span class="md-plain">，删除我们常用的语法是</span><span class="md-pair-s" spellcheck="false"><code>remove()和discard()</code></span><span class="md-plain">（前者删除不存在的元素的时候会报错，后者不会更加安全），新增则用</span><span class="md-pair-s" spellcheck="false"><code>add()</code></span></p>
<p class="md-end-block md-p"><span class="md-plain">集合在深度学习的过程中遇到的概率比较低，会被Pands替代，所以不过多讲述</span></p>
<h3 class="md-end-block md-heading"><span class="md-plain">List列表</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Python中的列表就可以比作Java中的ArrayList，边长的数组，可重复，有序，下标访问，只是基于Python动态语言的特性，一个List中可以包含任何数据类型，不存在类型限制，也可以List套List</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">list_v</span> <span class="cm-operator">=</span> [<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-string">"ZhangSan"</span>,<span class="cm-keyword">True</span>,[<span class="cm-number">4</span>,<span class="cm-number">5</span>,<span class="cm-number">6</span>]] &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>)</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>[<span class="cm-number">0</span>])</span></pre>
<p class="md-end-block md-p"><span class="md-plain">虽然List没有限制，但是实际上底层List还是会需要存储每个类型的数据类型，导致数组其实占用空间增大了。在NumPy中我们会用NumPy数组（仅仅接纳一种数据类型的列表）代替List，提升运行速度，降低内存压力</span></p>
<p class="md-end-block md-p"><span class="md-plain">List支持负数下标索引，我们知道0索引位置对应的List的第一个元素，那么-1其实就是倒数第一个元素，以此类推，-2就是倒数第二个元素</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">list_v</span> <span class="cm-operator">=</span> [<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-string">"ZhangSan"</span>,<span class="cm-keyword">True</span>,[<span class="cm-number">4</span>,<span class="cm-number">5</span>,<span class="cm-number">6</span>]] &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>[<span class="cm-operator">-</span><span class="cm-number">1</span>],<span class="cm-variable">list_v</span>[<span class="cm-operator">-</span><span class="cm-number">2</span>],<span class="cm-variable">list_v</span>[<span class="cm-operator">-</span><span class="cm-number">3</span>]) &nbsp;</span><br><span role="presentation"><span class="cm-variable">list_v</span>[<span class="cm-operator">-</span><span class="cm-number">2</span>]<span class="cm-operator">=</span><span class="cm-number">100</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>[<span class="cm-operator">-</span><span class="cm-number">1</span>],<span class="cm-variable">list_v</span>[<span class="cm-operator">-</span><span class="cm-number">2</span>],<span class="cm-variable">list_v</span>[<span class="cm-operator">-</span><span class="cm-number">3</span>])</span></pre>
<p class="md-end-block md-p"><span class="md-plain">Python的List有着和Golang的数组一样的切片的特性，切片就是列表一部分，可以利用</span><span class="md-pair-s" spellcheck="false"><code>[start:end]</code></span><span class="md-plain">的形式截取列表中的 start~end 之间的部分，</span><span class="md-pair-s" spellcheck="false"><code>注意左闭右开</code></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">list_v</span> <span class="cm-operator">=</span> [<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-string">"ZhangSan"</span>,<span class="cm-keyword">True</span>,[<span class="cm-number">4</span>,<span class="cm-number">5</span>,<span class="cm-number">6</span>]] &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>[<span class="cm-number">1</span>:]) <span class="cm-comment"># 第二个元素到最后 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>[:]) <span class="cm-comment"># 全部元素 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>[<span class="cm-number">1</span>:<span class="cm-number">2</span>]) <span class="cm-comment"># 只有第二个元素 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>[:<span class="cm-number">5</span>]) <span class="cm-comment"># 前四个元素 等同于 print(list_v[:-1])</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">除此之外，Python的切片还能支持间隔切片。当我们明确隔n个元素采样一次的时候，可以使用</span><span class="md-pair-s" spellcheck="false"><code>[star:end:n]</code></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">list_v</span> <span class="cm-operator">=</span> [<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-number">4</span>,<span class="cm-number">5</span>,<span class="cm-number">6</span>,<span class="cm-number">7</span>,<span class="cm-number">8</span>,<span class="cm-number">9</span>,<span class="cm-number">10</span>,<span class="cm-number">11</span>,<span class="cm-number">12</span>,<span class="cm-number">13</span>,<span class="cm-number">14</span>,<span class="cm-number">15</span>] &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>[<span class="cm-number">1</span>:<span class="cm-operator">-</span><span class="cm-number">1</span>:<span class="cm-number">2</span>]) <span class="cm-comment"># [2, 4, 6, 8, 10, 12, 14]</span></span></pre>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>特别注意，Python的切片和原列表对象是完全独立的，也就是两个列表了实际上，所以对于几百万数据的大列表进行切片操作，对于主机而言是一个很大的考验，这点要和Go的切片区分开来，Go中的切片和原来的是共享的</code></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">list_v</span> <span class="cm-operator">=</span> [<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-number">4</span>,<span class="cm-number">5</span>,<span class="cm-number">6</span>,<span class="cm-number">7</span>,<span class="cm-number">8</span>,<span class="cm-number">9</span>,<span class="cm-number">10</span>,<span class="cm-number">11</span>,<span class="cm-number">12</span>,<span class="cm-number">13</span>,<span class="cm-number">14</span>,<span class="cm-number">15</span>] &nbsp;</span><br><span role="presentation"><span class="cm-variable">list_v2</span> <span class="cm-operator">=</span> <span class="cm-variable">list_v</span>[<span class="cm-number">1</span>:<span class="cm-operator">-</span><span class="cm-number">1</span>:<span class="cm-number">2</span>] &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v2</span>) &nbsp; <span class="cm-comment"># [2, 4, 6, 8, 10, 12, 14]</span></span><br><span role="presentation"><span class="cm-variable">list_v2</span>[<span class="cm-number">0</span>] <span class="cm-operator">=</span> <span class="cm-number">99</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v2</span>) &nbsp;<span class="cm-comment"># [99, 4, 6, 8, 10, 12, 14]</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>) <span class="cm-comment"># [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">在NumPy的数组中这个就被舍弃了，NumPy中的切片和原数组是共享的，修改切片也会修改原数组，如果真的需要完全拷贝一份新的，就是借助</span><span class="md-pair-s" spellcheck="false"><code>copy()</code></span><span class="md-plain">函数</span></p>
<p class="md-end-block md-p"><span class="md-plain">List可以通过</span><span class="md-pair-s" spellcheck="false"><code>append()</code></span><span class="md-plain">函数添加元素，也可以通过加法和乘法扩充和复制</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">list_v</span> <span class="cm-operator">=</span> [<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-number">4</span>] &nbsp;</span><br><span role="presentation"><span class="cm-variable">list_v</span>.<span class="cm-property">append</span>(<span class="cm-number">5</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span>) &nbsp;<span class="cm-comment">#12345</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span><span class="cm-operator">+</span>[<span class="cm-number">6</span>]) &nbsp; <span class="cm-comment">#123456</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span><span class="cm-operator">+</span>[<span class="cm-number">6</span>,<span class="cm-number">7</span>,<span class="cm-number">8</span>]) &nbsp;<span class="cm-comment">#12345678</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">list_v</span><span class="cm-operator">*</span><span class="cm-number">2</span>) <span class="cm-comment">#1234512345</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">列表还有一个列表推导式，可以了解和学习一下</span> <span class="md-plain">正常的我们创建由已知列表开平方得到新列表，我们需要借助</span><span class="md-pair-s" spellcheck="false"><code>for循环</code></span><span class="md-plain">，代码如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">result</span> <span class="cm-operator">=</span> []</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">i</span> <span class="cm-keyword">in</span> [<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-number">4</span>]:</span><br><span role="presentation">    <span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-variable">result</span><span class="cm-operator">+</span>[<span class="cm-variable">i</span><span class="cm-operator">**</span><span class="cm-number">2</span>]</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">result</span>)</span></pre>
<p class="md-end-block md-p"><span class="md-plain">使用列表推导式之后可以这么写</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">result</span> <span class="cm-operator">=</span> [<span class="cm-variable">i</span><span class="cm-operator">**</span><span class="cm-number">2</span> <span class="cm-keyword">for</span> <span class="cm-variable">i</span> <span class="cm-keyword">in</span> [<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-number">4</span>]]</span></pre>
<p class="md-end-block md-p"><span class="md-plain">也还是加一点的</span><span class="md-pair-s" spellcheck="false"><code>if判断</code></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">result</span> <span class="cm-operator">=</span> []</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">i</span> <span class="cm-keyword">in</span> [<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-number">4</span>]:</span><br><span role="presentation">    <span class="cm-keyword">if</span> <span class="cm-variable">i</span><span class="cm-operator">&lt;</span><span class="cm-number">3</span>:</span><br><span role="presentation">        <span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-variable">result</span><span class="cm-operator">+</span>[<span class="cm-variable">i</span><span class="cm-operator">**</span><span class="cm-number">2</span>]</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">result</span>)</span><br><span role="presentation">​</span><br><span class="cm-comment"># 列表推导式写法</span><br><span role="presentation"><span class="cm-variable">result</span> <span class="cm-operator">=</span> [<span class="cm-variable">i</span><span class="cm-operator">**</span><span class="cm-number">2</span> <span class="cm-keyword">for</span> <span class="cm-variable">i</span> <span class="cm-keyword">in</span> [<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-number">4</span>] <span class="cm-keyword">if</span> <span class="cm-variable">i</span><span class="cm-operator">&lt;</span><span class="cm-number">3</span>]</span></pre>
<h3 class="md-end-block md-heading"><span class="md-plain">tuple元组</span></h3>
<p class="md-end-block md-p"><span class="md-plain">元组的特点：有序；可重复；单个元素不可修改但是可重新赋值替换；下标访问</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">tuple_age</span> <span class="cm-operator">=</span> (<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>) &nbsp;</span><br><span class="cm-comment"># 这种创建方式极其简单方便</span><br><span class="cm-comment"># 同时也可以看到，python中对于类型的要求没那么强</span><br><span role="presentation"><span class="cm-variable">tuple_name</span> <span class="cm-operator">=</span> <span class="cm-string">"张三"</span>,<span class="cm-string">"李四"</span>,<span class="cm-number">99</span> &nbsp;<span class="cm-variable">tuple_null</span> <span class="cm-operator">=</span> () &nbsp;<span class="cm-comment"># 创建空元组</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">tuple_age</span>) &nbsp;</span><br><span class="cm-comment"># tuple_age[0]=10 在运行的时候会报错</span><br><span role="presentation"><span class="cm-variable">tuple_age</span> <span class="cm-operator">=</span> <span class="cm-number">3</span>,<span class="cm-number">3</span>,<span class="cm-number">3</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">tuple_age</span>[<span class="cm-number">0</span>]) <span class="cm-comment"># 下标访问单个元素</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">tuple_name</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">tuple_null</span>)</span></pre>
<p class="md-end-block md-p"><span class="md-plain">除此之外，可以利用元组进行批量赋值，或者说就是Python的批量赋值的特性</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">a</span>,<span class="cm-variable">b</span>,<span class="cm-variable">c</span> <span class="cm-operator">=</span> <span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">e</span>,<span class="cm-variable">f</span>,<span class="cm-variable">g</span> <span class="cm-operator">=</span> (<span class="cm-number">11</span>,<span class="cm-number">12.2</span>,<span class="cm-number">13.33</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">a</span>,<span class="cm-variable">b</span>,<span class="cm-variable">c</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">e</span>,<span class="cm-variable">f</span>,<span class="cm-variable">g</span>)</span></pre>
<p class="md-end-block md-p"><span class="md-plain">当有多个数值的列表，</span><strong><span class="md-plain">然后你只需要部分数值的时候，你可以利用元组的快捷构造的特性进行截取</span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">result1</span>,<span class="cm-variable">result2</span>,<span class="cm-operator">*</span><span class="cm-variable">other</span>,<span class="cm-variable">result3</span> <span class="cm-operator">=</span><span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>,<span class="cm-number">4</span>,<span class="cm-number">5</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">result1</span>) &nbsp;<span class="cm-comment">#1</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">result2</span>) &nbsp;<span class="cm-comment">#2</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">other</span>) &nbsp;<span class="cm-comment">#[3,4]</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">result3</span>) <span class="cm-comment">#5</span></span></pre>
<h3 class="md-end-block md-heading"><span class="md-plain">dict字典</span></h3>
<p class="md-end-block md-p"><span class="md-plain">一个Key-Value结构，不可重复，重复会覆盖；无序 ；Key唯一 ； Value可以为任何数据类型，Key只能是不可变的元素（数字，字符串，布尔，元组，不可变集合）</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">dict_v</span> <span class="cm-operator">=</span> { &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"name"</span>:<span class="cm-string">"zhangsan"</span>, &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"age"</span>:<span class="cm-number">18</span>, &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-number">1</span>:(<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>), &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-number">2</span>:[<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>], &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-number">3</span>:{<span class="cm-number">1</span>,<span class="cm-number">2</span>,<span class="cm-number">3</span>}, &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"4"</span>:<span class="cm-keyword">True</span> &nbsp;</span><br><span role="presentation">} &nbsp;</span><br><span class="cm-comment"># {'name': 'zhangsan', 'age': 18, 1: (1, 2, 3), 2: [1, 2, 3], 3: {1, 2, 3}, '4': True}</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">dict_v</span>)</span></pre>
<p class="md-end-block md-p"><span class="md-plain">字典添加元素，可以直接通过</span><span class="md-pair-s" spellcheck="false"><code>dict[newKey]=newValue</code></span><span class="md-plain">的方式进行新增</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">dict_v</span> <span class="cm-operator">=</span> { &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"name"</span>:<span class="cm-string">"zhangsan"</span>, &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"age"</span>:<span class="cm-number">18</span>, &nbsp;</span><br><span role="presentation">} &nbsp;</span><br><span role="presentation"><span class="cm-variable">dict_v</span>[<span class="cm-string">"newKey"</span>] <span class="cm-operator">=</span> <span class="cm-string">"newValue"</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">dict_v</span>)</span></pre>
<h3 class="md-end-block md-heading"><span class="md-plain">类型转换</span></h3>
<p class="md-end-block md-p"><span class="md-plain">集合，列表，元组，字典之间也可以进行转换，用到如下四个函数</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">转化为集合使用</span><span class="md-pair-s" spellcheck="false"><code>set()</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">转化为元组使用</span><span class="md-pair-s" spellcheck="false"><code>tuple()</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">转化为列表使用</span><span class="md-pair-s" spellcheck="false"><code>list()</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">转化为字典使用</span><span class="md-pair-s" spellcheck="false"><code>dist()</code></span> <span class="md-plain">这里就展示图片，不附带代码了，可自行尝试，需要注意的是，其余的转化为字典的时候，因为字典是双列的，所以肯定还会需要一个单列集合，这个时候就需要借助</span><span class="md-pair-s" spellcheck="false"><code>zip(A,B)</code></span><span class="md-plain">函数，该函数会将入参A和B进行一一对应，然后让A[0]作为Key，B[0]作为Value从而组成dict[0]，这里需要注意，传入的A和B如果是集合这种无序的，那么会造成匹配不一样按照预期一样</span> <span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/20260128213156572.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/20260128213156572.png" alt="image.png"></span></p>
</li>
</ul>
<h2 class="md-end-block md-heading"><span class="md-plain">流程控制</span></h2>
<h3 class="md-end-block md-heading"><span class="md-plain">if语句</span></h3>
<p class="md-end-block md-p"><span class="md-plain">在Python中，</span><span class="md-pair-s" spellcheck="false"><code>if语句</code></span><span class="md-plain">的使用格式为如下，需要注意：</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">Python中的函数，循环，if条件判断等都不会使用end来表示代码块的结束</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">Python中使用缩进来表示代码块的范围</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">每一个判断条件的最后都有一个冒号，不要遗漏</span></p>
</li>
</ul>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">if</span> <span class="cm-variable">判断条件</span>:</span><br><span role="presentation">    <span class="cm-variable">逻辑处理一</span></span><br><span role="presentation"><span class="cm-keyword">elif</span> <span class="cm-variable">判断条件</span>:</span><br><span role="presentation">    <span class="cm-variable">逻辑处理二</span></span><br><span role="presentation"><span class="cm-keyword">else</span>:</span><br><span role="presentation">    <span class="cm-variable">逻辑处理三</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">简单的分数区间判断案例</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">source</span> <span class="cm-operator">=</span> <span class="cm-number">85</span> &nbsp;</span><br><span role="presentation"><span class="cm-keyword">if</span> <span class="cm-variable">source</span> <span class="cm-operator">==</span> <span class="cm-number">100</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">"满昏"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">elif</span> <span class="cm-variable">source</span> <span class="cm-operator">&gt;=</span> <span class="cm-number">90</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">"优秀"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">elif</span> <span class="cm-variable">source</span> <span class="cm-operator">&gt;=</span> <span class="cm-number">60</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">"及格"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">else</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">"不及格"</span>)</span></pre>
<h3 class="md-end-block md-heading"><span class="md-plain">循环语句</span></h3>
<p class="md-end-block md-p"><span class="md-plain">Python中for循环的基本使用如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">变量名</span> <span class="cm-keyword">in</span> <span class="cm-variable">可迭代对象</span>:</span><br><span role="presentation">    <span class="cm-variable">循环体逻辑</span></span><br>    <br>    <br><span class="cm-comment"># 遍历列表，依次取出每个元素 </span><br><span role="presentation"><span class="cm-variable">fruits</span> <span class="cm-operator">=</span> [<span class="cm-string">"苹果"</span>, <span class="cm-string">"香蕉"</span>, <span class="cm-string">"橙子"</span>] </span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">fruit</span> <span class="cm-keyword">in</span> <span class="cm-variable">fruits</span>: </span><br><span role="presentation">    <span class="cm-builtin">print</span>(<span class="cm-string">f"我喜欢吃：</span>{<span class="cm-variable">fruit</span>}<span class="cm-string">"</span>) </span><br><span class="cm-comment"># 输出结果： # 我喜欢吃：苹果 # 我喜欢吃：香蕉 # 我喜欢吃：橙子</span></pre>
<p class="md-end-block md-p"><span class="md-plain">如果需要计数式循环，即例如Java中</span><span class="md-pair-s" spellcheck="false"><code>for(int i=0;i&lt;10;i++)</code></span><span class="md-plain">,可以借助</span><span class="md-pair-s" spellcheck="false"><code>range(n)</code></span><span class="md-plain">，该函数会创建一个0~n-1的整数序列供for循环使用,需要注意，</span><span class="md-pair-s" spellcheck="false"><code>range()函数也是左闭右开的区间范围</code></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span class="cm-comment"># 用法1：range(n) &rarr; 生成 0 ~ n-1 的整数序列 &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">i</span> <span class="cm-keyword">in</span> <span class="cm-builtin">range</span>(<span class="cm-number">5</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-variable">i</span>) &nbsp;<span class="cm-comment"># 输出：0 1 2 3 4 &nbsp;</span></span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment cm-error"># 用法2：range(start, end) &rarr; 生成 start ~ end-1 的整数序列（左闭右开） &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">i</span> <span class="cm-keyword">in</span> <span class="cm-builtin">range</span>(<span class="cm-number">2</span>, <span class="cm-number">7</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-variable">i</span>) &nbsp;<span class="cm-comment"># 输出：2 3 4 5 6 &nbsp;</span></span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment cm-error"># 用法3：range(start, end, step) &rarr; 步长为step，生成等差序列 &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">i</span> <span class="cm-keyword">in</span> <span class="cm-builtin">range</span>(<span class="cm-number">0</span>, <span class="cm-number">10</span>, <span class="cm-number">2</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-variable">i</span>) &nbsp;<span class="cm-comment"># 输出：0 2 4 6 8（偶数） &nbsp;</span></span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment cm-error"># 示例：通过索引遍历列表 &nbsp;</span><br><span role="presentation"><span class="cm-variable">fruits</span> <span class="cm-operator">=</span> [<span class="cm-string">"苹果"</span>, <span class="cm-string">"香蕉"</span>, <span class="cm-string">"橙子"</span>] &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">i</span> <span class="cm-keyword">in</span> <span class="cm-builtin">range</span>(<span class="cm-builtin">len</span>(<span class="cm-variable">fruits</span>)): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"索引</span>{<span class="cm-variable">i</span>}<span class="cm-string">：</span>{<span class="cm-variable">fruits</span>[<span class="cm-variable">i</span>]}<span class="cm-string">"</span>)</span></pre>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>for循环</code></span><span class="md-plain">也常用于高级变量类型的遍历</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span class="cm-comment"># 1. 遍历列表 (List)print("\n【1】遍历列表 (List)") &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">fruits</span> <span class="cm-operator">=</span> [<span class="cm-string">"苹果"</span>, <span class="cm-string">"香蕉"</span>, <span class="cm-string">"橙子"</span>, <span class="cm-string">"葡萄"</span>, <span class="cm-string">"草莓"</span>] &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"列表内容: </span>{<span class="cm-variable">fruits</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"方法1: 直接遍历元素"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">fruit</span> <span class="cm-keyword">in</span> <span class="cm-variable">fruits</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  - </span>{<span class="cm-variable">fruit</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin cm-error">print</span>(<span class="cm-string">"\n方法2: 使用enumerate()获取索引和值"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">index</span>, <span class="cm-variable">fruit</span> <span class="cm-keyword">in</span> <span class="cm-builtin">enumerate</span>(<span class="cm-variable">fruits</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  索引: </span>{<span class="cm-variable">index</span>}<span class="cm-string">, 值: </span>{<span class="cm-variable">fruit</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin cm-error">print</span>(<span class="cm-string">"\n方法3: 使用range(len())遍历索引"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">i</span> <span class="cm-keyword">in</span> <span class="cm-builtin">range</span>(<span class="cm-builtin">len</span>(<span class="cm-variable">fruits</span>)): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  索引: </span>{<span class="cm-variable">i</span>}<span class="cm-string">, 值: </span>{<span class="cm-variable">fruits</span>[<span class="cm-variable">i</span>]}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment cm-error"># 2. 遍历元组 (Tuple)print("\n【2】遍历元组 (Tuple)") &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">colors</span> <span class="cm-operator">=</span> (<span class="cm-string">"红色"</span>, <span class="cm-string">"绿色"</span>, <span class="cm-string">"蓝色"</span>, <span class="cm-string">"黄色"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"元组内容: </span>{<span class="cm-variable">colors</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"方法1: 直接遍历元素"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">color</span> <span class="cm-keyword">in</span> <span class="cm-variable">colors</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  - </span>{<span class="cm-variable">color</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin cm-error">print</span>(<span class="cm-string">"\n方法2: 使用enumerate()获取索引和值"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">index</span>, <span class="cm-variable">color</span> <span class="cm-keyword">in</span> <span class="cm-builtin">enumerate</span>(<span class="cm-variable">colors</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  索引: </span>{<span class="cm-variable">index</span>}<span class="cm-string">, 值: </span>{<span class="cm-variable">color</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment cm-error"># 3. 遍历集合 (Set)print("\n【3】遍历集合 (Set)") &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">numbers</span> <span class="cm-operator">=</span> {<span class="cm-number">1</span>, <span class="cm-number">2</span>, <span class="cm-number">3</span>, <span class="cm-number">4</span>, <span class="cm-number">5</span>, <span class="cm-number">3</span>, <span class="cm-number">2</span>} &nbsp;<span class="cm-comment"># 注意：集合会自动去除重复元素 &nbsp;</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"集合内容: </span>{<span class="cm-variable">numbers</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"方法1: 直接遍历元素"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">num</span> <span class="cm-keyword">in</span> <span class="cm-variable">numbers</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  - </span>{<span class="cm-variable">num</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin cm-error">print</span>(<span class="cm-string">"\n方法2: 对集合排序后遍历"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">num</span> <span class="cm-keyword">in</span> <span class="cm-builtin">sorted</span>(<span class="cm-variable">numbers</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  - </span>{<span class="cm-variable">num</span>}<span class="cm-string"> (排序后)"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment cm-error"># 4. 遍历字典 (Dictionary)print("\n【4】遍历字典 (Dictionary)") &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">student_scores</span> <span class="cm-operator">=</span> { &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"张三"</span>: <span class="cm-number">95</span>, &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"李四"</span>: <span class="cm-number">87</span>, &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"王五"</span>: <span class="cm-number">92</span>, &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"赵六"</span>: <span class="cm-number">78</span> &nbsp;</span><br><span role="presentation">} &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"字典内容: </span>{<span class="cm-variable">student_scores</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"方法1: 遍历键(keys)"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">name</span> <span class="cm-keyword">in</span> <span class="cm-variable">student_scores</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  学生: </span>{<span class="cm-variable">name</span>}<span class="cm-string">, 分数: </span>{<span class="cm-variable">student_scores</span>[<span class="cm-variable">name</span>]}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin cm-error">print</span>(<span class="cm-string">"\n方法2: 明确遍历键(keys)"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">name</span> <span class="cm-keyword">in</span> <span class="cm-variable">student_scores</span>.<span class="cm-property">keys</span>(): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  学生: </span>{<span class="cm-variable">name</span>}<span class="cm-string">, 分数: </span>{<span class="cm-variable">student_scores</span>[<span class="cm-variable">name</span>]}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin cm-error">print</span>(<span class="cm-string">"\n方法3: 遍历值(values)"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">score</span> <span class="cm-keyword">in</span> <span class="cm-variable">student_scores</span>.<span class="cm-property">values</span>(): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  分数: </span>{<span class="cm-variable">score</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin cm-error">print</span>(<span class="cm-string">"\n方法4: 同时遍历键和值(items)"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">name</span>, <span class="cm-variable">score</span> <span class="cm-keyword">in</span> <span class="cm-variable">student_scores</span>.<span class="cm-property">items</span>(): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  学生: </span>{<span class="cm-variable">name</span>}<span class="cm-string">, 分数: </span>{<span class="cm-variable">score</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment cm-error"># 5. 嵌套数据结构的遍历 &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"\n【5】嵌套数据结构的遍历"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">nested_data</span> <span class="cm-operator">=</span> { &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"班级A"</span>: [<span class="cm-number">85</span>, <span class="cm-number">90</span>, <span class="cm-number">78</span>], &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"班级B"</span>: [<span class="cm-number">92</span>, <span class="cm-number">88</span>, <span class="cm-number">84</span>], &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"班级C"</span>: [<span class="cm-number">79</span>, <span class="cm-number">85</span>, <span class="cm-number">91</span>] &nbsp;</span><br><span role="presentation">} &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"遍历嵌套字典和列表"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">class_name</span>, <span class="cm-variable">scores</span> <span class="cm-keyword">in</span> <span class="cm-variable">nested_data</span>.<span class="cm-property">items</span>(): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f" &nbsp;</span>{<span class="cm-variable">class_name</span>}<span class="cm-string">:"</span>) &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">for</span> <span class="cm-variable">i</span>, <span class="cm-variable">score</span> <span class="cm-keyword">in</span> <span class="cm-builtin">enumerate</span>(<span class="cm-variable">scores</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f" &nbsp;  学生</span>{<span class="cm-variable">i</span><span class="cm-operator">+</span><span class="cm-number">1</span>}<span class="cm-string">: </span>{<span class="cm-variable">score</span>}<span class="cm-string">分"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span class="cm-comment cm-error"># 6. 使用条件语句的遍历 &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"\n【6】带条件判断的遍历"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"-"</span> <span class="cm-operator">*</span> <span class="cm-number">40</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">mixed_list</span> <span class="cm-operator">=</span> [<span class="cm-number">1</span>, <span class="cm-string">"hello"</span>, <span class="cm-number">3.14</span>, <span class="cm-string">"world"</span>, <span class="cm-number">42</span>, <span class="cm-keyword">True</span>, <span class="cm-string">"python"</span>] &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">f"混合列表: </span>{<span class="cm-variable">mixed_list</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"只打印字符串类型的元素:"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">item</span> <span class="cm-keyword">in</span> <span class="cm-variable">mixed_list</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> <span class="cm-builtin">isinstance</span>(<span class="cm-variable">item</span>, <span class="cm-builtin">str</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  字符串: </span>{<span class="cm-variable">item</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin cm-error">print</span>(<span class="cm-string">"\n只打印数字类型的元素:"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-keyword">for</span> <span class="cm-variable">item</span> <span class="cm-keyword">in</span> <span class="cm-variable">mixed_list</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> <span class="cm-builtin">isinstance</span>(<span class="cm-variable">item</span>, (<span class="cm-builtin">int</span>, <span class="cm-builtin">float</span>)): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">f"  数字: </span>{<span class="cm-variable">item</span>}<span class="cm-string">"</span>) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin cm-error">print</span>(<span class="cm-string">"\n"</span> <span class="cm-operator">+</span> <span class="cm-string">"="</span><span class="cm-operator">*</span><span class="cm-number">60</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"Python for循环遍历演示完成！"</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"="</span><span class="cm-operator">*</span><span class="cm-number">60</span>)</span></pre>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>while循环</code></span><span class="md-plain">的基本格式如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">while</span> <span class="cm-variable">条件</span>:</span><br><span role="presentation">    <span class="cm-variable">循环体</span></span></pre>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>break和continue</code></span><span class="md-plain">的使用和其他语言一致，这里不过多讲述</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">函数</span></h2>
<p class="md-end-block md-p"><span class="md-plain">Python中的函数关键字为</span><span class="md-pair-s" spellcheck="false"><code>def</code></span><span class="md-plain">，其格式如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">def</span> <span class="cm-def">函数名</span>(<span class="cm-variable">入参列表</span>):</span><br><span role="presentation">    <span class="cm-string">""" 文档说明字符串 """</span></span><br><span role="presentation">    <span class="cm-variable">函数体</span></span><br><span role="presentation">    <span class="cm-keyword">return</span> <span class="cm-variable">反参</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">首先我们来解释一下这个一开始文档说明字符串，这个需要搭配</span><span class="md-pair-s" spellcheck="false"><code>方法名.__doc__</code></span><span class="md-plain">进行使用，当你在这个方法写了文档说明字符串的时候，你使用</span><span class="md-pair-s" spellcheck="false"><code>方法名.__doc__</code></span><span class="md-plain">就能打印出这些内容，相当于做了方法说明文档，当然，这个不是必须的，如果不写，调用</span><span class="md-pair-s" spellcheck="false"><code>.__doc__</code></span><span class="md-plain">会返回</span><span class="md-pair-s" spellcheck="false"><code>None</code></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">def</span> <span class="cm-def">add</span>(<span class="cm-variable">x</span>, <span class="cm-variable">y</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">"""计算x+y"""</span> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">x</span> <span class="cm-operator">+</span> <span class="cm-variable">y</span> &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">add</span>.<span class="cm-property">__doc__</span>) <span class="cm-comment"># 输出 计算x+y</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">函数的入参和反参可以是七种数据类型中的任意一种，并且Python的入参是值传递，也就是说函数入参是形参，不会对外部数据造成影响</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">def</span> <span class="cm-def">change</span>(<span class="cm-variable">v</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">v</span><span class="cm-operator">=</span><span class="cm-variable">v</span><span class="cm-operator">+</span><span class="cm-number">1</span> &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable cm-error">x</span> <span class="cm-operator">=</span> <span class="cm-number">10</span> &nbsp;</span><br><span role="presentation"><span class="cm-variable">change</span>(<span class="cm-variable">x</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">x</span>) <span class="cm-comment"># 依旧是10</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">Python的函数可以多个入参和多个反参,多个反参可以利用多个变量接收，可以当作一个整体进行接收</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">def</span> <span class="cm-def">my_counter</span>(<span class="cm-variable">a</span>,<span class="cm-variable">b</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">a</span><span class="cm-operator">+</span><span class="cm-variable">b</span>,<span class="cm-variable">a</span><span class="cm-operator">-</span><span class="cm-variable">b</span>,<span class="cm-variable">a</span><span class="cm-operator">*</span><span class="cm-variable">b</span> &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable">a</span>,<span class="cm-variable">b</span>,<span class="cm-variable">c</span><span class="cm-operator">=</span> <span class="cm-variable">my_counter</span>(<span class="cm-number">1</span>,<span class="cm-number">2</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"和为"</span>,<span class="cm-variable">a</span>,<span class="cm-string">"差为"</span>,<span class="cm-variable">b</span>,<span class="cm-string">"积为"</span>,<span class="cm-variable">c</span>) &nbsp;</span><br><span role="presentation"><span class="cm-variable">tuple_v</span> <span class="cm-operator">=</span> <span class="cm-variable">my_counter</span>(<span class="cm-number">1</span>,<span class="cm-number">2</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-string">"和为"</span>,<span class="cm-variable">tuple_v</span>[<span class="cm-number">0</span>],<span class="cm-string">"差为"</span>,<span class="cm-variable">tuple_v</span>[<span class="cm-number">1</span>],<span class="cm-string">"积为"</span>,<span class="cm-variable">tuple_v</span>[<span class="cm-number">2</span>])</span></pre>
<p class="md-end-block md-p"><span class="md-plain">需要传入可变数量的参数的时候，可以利用元组拆分法，利用</span><span class="md-pair-s" spellcheck="false"><code>*name</code></span><span class="md-plain">作为入参 </span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">def</span> <span class="cm-def">my_counter</span>(<span class="cm-operator">*</span><span class="cm-variable">args</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-number">0</span> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">for</span> <span class="cm-variable">arg</span> <span class="cm-keyword">in</span> <span class="cm-variable">args</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">result</span> <span class="cm-operator">+=</span> <span class="cm-variable">arg</span> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">result</span> &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">my_counter</span>(<span class="cm-number">1</span>, <span class="cm-number">2</span>, <span class="cm-number">3</span>, <span class="cm-number">4</span>, <span class="cm-number">5</span>)) &nbsp;<span class="cm-comment">#15</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">my_counter</span>(<span class="cm-number">1</span>, <span class="cm-number">2</span>, <span class="cm-number">3</span>)) <span class="cm-comment"># 6</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">自然，也可以同时指定一个入参和剩下的不确定入参，需要注意的是，</span><span class="md-pair-s" spellcheck="false"><code>可变参数只能作为最后一个参数且一个函数只能有一个可变参数，虽然编译器不会报错，但是运行时会出错，因为无法确定边界</code></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">def</span> <span class="cm-def">my_counter</span>(<span class="cm-variable">off</span>,<span class="cm-operator">*</span><span class="cm-variable">args</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-number">0</span> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">for</span> <span class="cm-variable">arg</span> <span class="cm-keyword">in</span> <span class="cm-variable">args</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">result</span> <span class="cm-operator">+=</span> <span class="cm-variable">arg</span> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">result</span><span class="cm-operator">+</span><span class="cm-variable">off</span> &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">my_counter</span>(<span class="cm-operator">-</span><span class="cm-number">10</span>,<span class="cm-number">1</span>, <span class="cm-number">2</span>, <span class="cm-number">3</span>, <span class="cm-number">4</span>, <span class="cm-number">5</span>)) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">my_counter</span>(<span class="cm-number">10</span>,<span class="cm-number">1</span>, <span class="cm-number">2</span>, <span class="cm-number">3</span>))</span></pre>
<p class="md-end-block md-p"><span class="md-plain">上面通过</span><span class="md-pair-s" spellcheck="false"><code>*name</code></span><span class="md-plain">实现不确定入参，可以理解为创建了一个name的列表然后允许我们一直往里面加东西，除此之外，还可以利用</span><span class="md-pair-s" spellcheck="false"><code>**name</code></span><span class="md-plain">的方式，作为可变参数的键值对/字典</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">def</span> <span class="cm-def">plusDict</span>(<span class="cm-variable">a</span>,<span class="cm-variable">b</span>,<span class="cm-operator">**</span><span class="cm-variable">dict_v</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">""" 加入对Java和Golang的描述，也可以自定义加新的描述键值对 """</span> &nbsp; &nbsp;<span class="cm-variable">dict_v</span>[<span class="cm-string">"Java"</span>] <span class="cm-operator">=</span> <span class="cm-variable">a</span> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">dict_v</span>[<span class="cm-string">"Golang"</span>] <span class="cm-operator">=</span> <span class="cm-variable">b</span> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">dict_v</span> &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">plusDict</span>( <span class="cm-string">"CRUD工程师"</span>,<span class="cm-string">"云原生的宠儿"</span>,<span class="cm-variable">Python</span><span class="cm-operator">=</span><span class="cm-string">"AI时代大哥大"</span>,<span class="cm-variable">Rust</span><span class="cm-operator">=</span><span class="cm-string">"高大上"</span>) )</span></pre>
<p class="md-end-block md-p"><span class="md-plain">在入参的时候，可以指定入参名从而不一定需要按照顺序传入参数</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">def</span> <span class="cm-def">my_counter</span>(<span class="cm-variable">a</span>, <span class="cm-variable">b</span>, <span class="cm-variable">c</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">a</span> <span class="cm-operator">+</span> <span class="cm-variable">b</span> <span class="cm-operator">*</span> <span class="cm-variable">c</span> &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">my_counter</span>(<span class="cm-number">1</span>,<span class="cm-variable">c</span><span class="cm-operator">=</span><span class="cm-number">2</span>,<span class="cm-variable">b</span><span class="cm-operator">=</span><span class="cm-number">3</span>))</span></pre>
<p class="md-end-block md-p"><span class="md-plain">在定义函数的时候，可以指定入参默认值，从而当不传入的时候，可以使用默认值</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">def</span> <span class="cm-def">yourSchool</span>(<span class="cm-variable">name</span>,<span class="cm-variable">level</span><span class="cm-operator">=</span><span class="cm-string">"本科"</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">"your school "</span> <span class="cm-operator">+</span> <span class="cm-variable">name</span> <span class="cm-operator">+</span> <span class="cm-string">"is a "</span> <span class="cm-operator">+</span> <span class="cm-variable">level</span> ) &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-variable cm-error">yourSchool</span>(<span class="cm-string">"HNUST"</span>) &nbsp;<span class="cm-comment"># 不会报错</span></span><br><span role="presentation"><span class="cm-variable">yourSchool</span>(<span class="cm-string">"HNUST"</span>,<span class="cm-string">"985"</span>)</span></pre>
<h2 class="md-end-block md-heading"><span class="md-plain">类</span></h2>
<h3 class="md-end-block md-heading"><span class="md-plain">定义和普通使用</span></h3>
<p class="md-end-block md-p"><span class="md-plain">类的定义我们就不多说了，Python中的类一样具备封装，继承，多态的特性(据说在深度学习领域类一般是用来封装参数传递，其余特性用的少)，并且在Python中其实也是一切皆对象，就连基本数据类型int也是</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">a</span> <span class="cm-operator">=</span> <span class="cm-number">1</span></span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-builtin">type</span>(<span class="cm-variable">a</span>)) <span class="cm-comment"># 输出：&lt;class 'int'&gt;</span></span></pre>
<p class="md-end-block md-p"><span class="md-plain">Python中类中的函数称之为方法，且约定俗成类的名称一般大写，Python的每个类必须包含一个</span><span class="md-pair-s" spellcheck="false"><code>__init__</code></span><span class="md-plain">的初始化方法（简单理解为有参构造方法）</span> <span class="md-pair-s" spellcheck="false"><code>init</code></span><span class="md-plain">方法内的入参通过赋值就可以让类对象拥有成员a,b，也可以直接不通过入参来定义成员，这些成员具备默认值</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">class</span> <span class="cm-def">Counter</span>: &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">""" 一个可以进行加减法的计算器 """</span> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">def</span> <span class="cm-def">__init__</span>(<span class="cm-variable-2">self</span>, <span class="cm-variable">a</span>, <span class="cm-variable">b</span>): &nbsp;<span class="cm-comment"># 用于完成初始化的特殊方法 &nbsp;</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-2">self</span>.<span class="cm-property">a</span> <span class="cm-operator">=</span> <span class="cm-variable">a</span> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-2">self</span>.<span class="cm-property">b</span> <span class="cm-operator">=</span> <span class="cm-variable">b</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-2">self</span>.<span class="cm-property">name</span> <span class="cm-operator">=</span> <span class="cm-string">"超级计算机"</span></span><br><span role="presentation"> &nbsp;</span><br><span class="cm-null cm-error"> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">def</span> <span class="cm-def">plus</span>(<span class="cm-variable-2">self</span>): &nbsp;<span class="cm-comment"># 类中的普通方法 &nbsp;</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable-2">self</span>.<span class="cm-property">a</span> <span class="cm-operator">+</span> <span class="cm-variable-2">self</span>.<span class="cm-property">b</span> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">def</span> <span class="cm-def">minus</span>(<span class="cm-variable-2">self</span>): &nbsp;<span class="cm-comment"># 类中的普通方法 &nbsp;</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable-2">self</span>.<span class="cm-property">a</span> <span class="cm-operator">-</span> <span class="cm-variable-2">self</span>.<span class="cm-property">b</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">def</span> <span class="cm-def">zwjs</span>(<span class="cm-variable-2">self</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">"欢迎使用"</span>, <span class="cm-variable-2">self</span>.<span class="cm-property">name</span>)</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到，无论是自定义普通方法还是init初始化方法，都有一个</span><span class="md-pair-s" spellcheck="false"><code>self参数</code></span><span class="md-plain">，这个参数用于链接类内部和外部的信息，大家可以简单的理解为</span><span class="md-pair-s" spellcheck="false"><code>this指针</code></span><span class="md-plain">，而且从上面也可也看到，Python的类的成员属性貌似可以不要定义，会根据</span><span class="md-pair-s" spellcheck="false"><code>__init__方法</code></span><span class="md-plain">自动定义在</span><span class="md-pair-s" spellcheck="false"><code>self中</code></span><span class="md-plain">，需要的话直接去</span><span class="md-pair-s" spellcheck="false"><code>self</code></span><span class="md-plain">中拿就行了</span></p>
<p class="md-end-block md-p"><span class="md-plain">类定义完成后，就可以创建类的对象以及调用其成员方法和成员属性</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-variable">counter1</span> <span class="cm-operator">=</span> <span class="cm-variable">Counter</span>(<span class="cm-number">1</span>,<span class="cm-number">2</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">counter1</span>.<span class="cm-property">a</span>, <span class="cm-variable">counter1</span>.<span class="cm-property">b</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">counter1</span>.<span class="cm-property">plus</span>()) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">counter1</span>.<span class="cm-property">minus</span>()) &nbsp;</span><br><span role="presentation"><span class="cm-variable">counter1</span>.<span class="cm-property">zwjs</span>()</span></pre>
<h3 class="md-end-block md-heading"><span class="md-plain">继承</span></h3>
<p class="md-end-block md-p"><span class="md-plain">利用继承，可以让子类继承父类的属性和方法</span> <span class="md-plain">Python的继承的表现形式为</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">class</span> <span class="cm-def">类名</span>(<span class="cm-variable">父类名</span>):</span></pre>
<p class="md-end-block md-p"><span class="md-plain">在Python中还能多继承</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">class</span> <span class="cm-def">类名</span>(<span class="cm-variable">fu1</span>,<span class="cm-variable">fu2</span>)</span></pre>
<p class="md-end-block md-p"><span class="md-plain">子类需要在</span><span class="md-pair-s" spellcheck="false"><code>__init__</code></span><span class="md-plain">方法中调用</span><span class="md-pair-s" spellcheck="false"><code>super()函数</code></span><span class="md-plain">从而实现继承属性和方法，对于父类中已有属性和方法子类中再次定义的话就会覆盖重写</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" spellcheck="false"><span role="presentation"><span class="cm-keyword">class</span> <span class="cm-def">Counter2</span>(<span class="cm-variable">Counter</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-string">""" 可以进行加减乘除的计算器 """</span> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">def</span> <span class="cm-def">__init__</span>(<span class="cm-variable-2">self</span>, <span class="cm-variable">a</span>, <span class="cm-variable">b</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-builtin">super</span>().<span class="cm-property">__init__</span>(<span class="cm-variable">a</span>, <span class="cm-variable">b</span>) &nbsp;<span class="cm-comment"># 这个不写其实也没事，不写的话就不能使用父类的东西，只要不使用父类的东西就不会报错</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-2">self</span>.<span class="cm-property">name</span> <span class="cm-operator">=</span> <span class="cm-string">"超级计算机二代"</span> &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-null cm-error"> &nbsp; &nbsp;</span><span class="cm-keyword">def</span> <span class="cm-def">multiply</span>(<span class="cm-variable-2">self</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable-2">self</span>.<span class="cm-property">a</span> <span class="cm-operator">*</span> <span class="cm-variable-2">self</span>.<span class="cm-property">b</span> &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">def</span> <span class="cm-def">divide</span>(<span class="cm-variable-2">self</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable-2">self</span>.<span class="cm-property">a</span> <span class="cm-operator">/</span> <span class="cm-variable-2">self</span>.<span class="cm-property">b</span> &nbsp;</span><br><span role="presentation"> &nbsp;</span><br><span role="presentation"><span class="cm-null cm-error"> &nbsp; &nbsp;</span><span class="cm-keyword">def</span> <span class="cm-def">zwjs</span>(<span class="cm-variable-2">self</span>): &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-builtin">print</span>(<span class="cm-string">"您好,欢迎使用"</span>, <span class="cm-variable-2">self</span>.<span class="cm-property">name</span>)</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"><span class="cm-variable">counter</span> <span class="cm-operator">=</span> <span class="cm-variable">Counter2</span>(<span class="cm-number">1</span>, <span class="cm-number">2</span>) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">counter</span>.<span class="cm-property">plus</span>()) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">counter</span>.<span class="cm-property">minus</span>()) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">counter</span>.<span class="cm-property">multiply</span>()) &nbsp;</span><br><span role="presentation"><span class="cm-builtin">print</span>(<span class="cm-variable">counter</span>.<span class="cm-property">divide</span>()) &nbsp;</span><br><span role="presentation"><span class="cm-variable">counter</span>.<span class="cm-property">zwjs</span>()</span></pre>
</div>]]></description>
    <pubDate>Wed, 28 Jan 2026 23:00:55 +0800</pubDate>
    <dc:creator>ZealSinger</dc:creator>
    <guid>http://8.154.40.120:24180/?post=57</guid>
</item>
<item>
    <title>Nacos补档-客户端心跳同步问题.md</title>
    <link>http://8.154.40.120:24180/?post=56</link>
    <description><![CDATA[<p class="md-end-block md-p md-focus"><span class="md-plain md-expand">我们今天来讨论一下Nacos客户端的心跳同步的一个问题</span></p>
<p class="md-end-block md-p"><span class="md-plain">我们知道如下几个点</span></p>
<ol class="ol-list">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">Nacos客户端会和服务端进行心跳同步</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">Nacos服务端在集群环境下采用单个责任节点+多个从节点的方式管理，责任节点负责负责健康检查，心跳处理等操作，从节点仅仅担任数据同步工作</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">一个微服务实例连接集群的条件下，请求可能会因为负载策略发送到任意一个Nacos节点上</span></p>
</li>
</ol>
<p class="md-end-block md-p"><span class="md-plain">那么综合上述，我们可以引出一个问题：</span><strong><span class="md-plain">如果客户端的心跳请求发送到了非责任节点会怎么处理？</span></strong></p>
<p class="md-end-block md-p"><span class="md-plain">这个地方可以参考Nacos仓库下面的一个issue</span></p>
<p class="md-end-block md-p"><span class="md-meta-i-c  md-link"><a href="https://github.com/alibaba/nacos/issues/5363" target="_blank" rel="noopener"><span class="md-plain">server端收到client beat请求后，不更新last beat时间，导致实际被判定为unhealthy &middot; Issue #5363 &middot; alibaba/nacos</span></a></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" spellcheck="false"><span role="presentation">针对上述问题，我们来举个例子：假设现在我们有三个集群节点，member1、member2、member3，又假设 member1 是负责 sotck-service 健康检查任务的节点。</span><br><span role="presentation">​</span><br><span role="presentation">stock-serivce 客户端发送心跳请求的时候，是随机发送到集群中某一个节点上的，假设 stock-service 没有给 member1 发送心跳请求，如果 lastBeat 属性不同步给集群节点，那么 member1 在做健康检查的时候，是不是会判断 stock-service 为不健康的实例？</span></pre>
<p class="md-end-block md-p"><span class="md-plain">我们可以看一下issue上大佬们关于这个问题的讨论与回复</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124220202300.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124220202300.png" alt="image-20260124220202300"></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124220213185.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124220213185.png" alt="image-20260124220213185"></span></p>
<p class="md-end-block md-p"><span class="md-plain">按照大佬的意思就是：</span><span class="md-pair-s" spellcheck="false"><code>心跳请求应该是随机发送到集群中某一台上，然后通过distro协议转发到该服务的责任节点去统一更新实例心跳时间。且心跳时间不会进行同步。</code></span></p>
<p class="md-end-block md-p"><span class="md-plain">我们可以自己扒一下源码来了解一下</span></p>
<p class="md-end-block md-p"><span class="md-plain">我们来看到服务端对于心跳的处理这一块，可以看到有一个</span><span class="md-pair-s" spellcheck="false"><code>@CanDistro</code></span><span class="md-plain">的注解</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124220613615.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124220613615.png" alt="image-20260124220613615"></span></p>
<p class="md-end-block md-p"><span class="md-plain">这个注解是一个自定义注解，但是他的作用好像不是很明了，我们可以看一下哪个地方用到了这个注解，发现这里有个Filter过滤器中用到了</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124220733729.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124220733729.png" alt="image-20260124220733729"></span></p>
<p class="md-end-block md-p"><span class="md-plain">我们来看一下这个过滤器，可以看到最终其匹配的URL是所有的</span><span class="md-pair-s" spellcheck="false"><code>/v1/ns/*</code></span><span class="md-plain">的路径</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124220851564.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124220851564.png" alt="image-20260124220851564"></span></p>
<p class="md-end-block md-p"><span class="md-plain">接下来我们就可以来看一下Filter中的过滤逻辑，代码很长，但是我们主要看一下重要的部分，即有我写了注释的那一行其实就行了</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">doFilter</span>(<span class="cm-variable">ServletRequest</span> <span class="cm-variable">servletRequest</span>, <span class="cm-variable">ServletResponse</span> <span class="cm-variable">servletResponse</span>, <span class="cm-variable">FilterChain</span> <span class="cm-variable">filterChain</span>)</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">throws</span> <span class="cm-variable">IOException</span>, <span class="cm-variable">ServletException</span> {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">ReuseHttpRequest</span> <span class="cm-variable">req</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">ReuseHttpServletRequest</span>((<span class="cm-variable">HttpServletRequest</span>) <span class="cm-variable">servletRequest</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">HttpServletResponse</span> <span class="cm-variable">resp</span> <span class="cm-operator">=</span> (<span class="cm-variable">HttpServletResponse</span>) <span class="cm-variable">servletResponse</span>;</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">urlString</span> <span class="cm-operator">=</span> <span class="cm-variable">req</span>.<span class="cm-variable">getRequestURI</span>();</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">StringUtils</span>.<span class="cm-variable">isNotBlank</span>(<span class="cm-variable">req</span>.<span class="cm-variable">getQueryString</span>())) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">urlString</span> <span class="cm-operator">+=</span> <span class="cm-string">"?"</span> <span class="cm-operator">+</span> <span class="cm-variable">req</span>.<span class="cm-variable">getQueryString</span>();</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">try</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">path</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">URI</span>(<span class="cm-variable">req</span>.<span class="cm-variable">getRequestURI</span>()).<span class="cm-variable">getPath</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">serviceName</span> <span class="cm-operator">=</span> <span class="cm-variable">req</span>.<span class="cm-variable">getParameter</span>(<span class="cm-variable">CommonParams</span>.<span class="cm-variable">SERVICE_NAME</span>);</span><br><span role="presentation">​</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">StringUtils</span>.<span class="cm-variable">isBlank</span>(<span class="cm-variable">serviceName</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">serviceName</span> <span class="cm-operator">=</span> <span class="cm-variable">req</span>.<span class="cm-variable">getParameter</span>(<span class="cm-string">"dom"</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">StringUtils</span>.<span class="cm-variable">isNotBlank</span>(<span class="cm-variable">serviceName</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">serviceName</span> <span class="cm-operator">=</span> <span class="cm-variable">serviceName</span>.<span class="cm-variable">trim</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Method</span> <span class="cm-variable">method</span> <span class="cm-operator">=</span> <span class="cm-variable">controllerMethodsCache</span>.<span class="cm-variable">getMethod</span>(<span class="cm-variable">req</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">method</span> <span class="cm-operator">==</span> <span class="cm-atom">null</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">NoSuchMethodException</span>(<span class="cm-variable">req</span>.<span class="cm-variable">getMethod</span>() <span class="cm-operator">+</span> <span class="cm-string">" "</span> <span class="cm-operator">+</span> <span class="cm-variable">path</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">groupName</span> <span class="cm-operator">=</span> <span class="cm-variable">req</span>.<span class="cm-variable">getParameter</span>(<span class="cm-variable">CommonParams</span>.<span class="cm-variable">GROUP_NAME</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">StringUtils</span>.<span class="cm-variable">isBlank</span>(<span class="cm-variable">groupName</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">groupName</span> <span class="cm-operator">=</span> <span class="cm-variable">Constants</span>.<span class="cm-variable">DEFAULT_GROUP</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation">​</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">groupedServiceName</span> <span class="cm-operator">=</span> <span class="cm-variable">serviceName</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">StringUtils</span>.<span class="cm-variable">isNotBlank</span>(<span class="cm-variable">serviceName</span>) <span class="cm-operator">&amp;&amp;</span> <span class="cm-operator">!</span><span class="cm-variable">serviceName</span>.<span class="cm-variable">contains</span>(<span class="cm-variable">Constants</span>.<span class="cm-variable">SERVICE_INFO_SPLITER</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">groupedServiceName</span> <span class="cm-operator">=</span> <span class="cm-variable">groupName</span> <span class="cm-operator">+</span> <span class="cm-variable">Constants</span>.<span class="cm-variable">SERVICE_INFO_SPLITER</span> <span class="cm-operator">+</span> <span class="cm-variable">serviceName</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span class="cm-comment">// 判断是否带有CanDistro注解 &amp;&amp; responsible方法即判断是否为责任节点</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">method</span>.<span class="cm-variable">isAnnotationPresent</span>(<span class="cm-variable">CanDistro</span>.<span class="cm-keyword">class</span>) <span class="cm-operator">&amp;&amp;</span> <span class="cm-operator">!</span><span class="cm-variable">distroMapper</span>.<span class="cm-variable">responsible</span>(<span class="cm-variable">groupedServiceName</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">userAgent</span> <span class="cm-operator">=</span> <span class="cm-variable">req</span>.<span class="cm-variable">getHeader</span>(<span class="cm-variable">HttpHeaderConsts</span>.<span class="cm-variable">USER_AGENT_HEADER</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">StringUtils</span>.<span class="cm-variable">isNotBlank</span>(<span class="cm-variable">userAgent</span>) <span class="cm-operator">&amp;&amp;</span> <span class="cm-variable">userAgent</span>.<span class="cm-variable">contains</span>(<span class="cm-variable">UtilsAndCommons</span>.<span class="cm-variable">NACOS_SERVER_HEADER</span>)) {</span><br><span role="presentation">​</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">SRV_LOG</span>.<span class="cm-variable">error</span>(<span class="cm-string">"receive invalid redirect request from peer {}"</span>, <span class="cm-variable">req</span>.<span class="cm-variable">getRemoteAddr</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">resp</span>.<span class="cm-variable">sendError</span>(<span class="cm-variable">HttpServletResponse</span>.<span class="cm-variable">SC_BAD_REQUEST</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-string">"receive invalid redirect request from peer "</span> <span class="cm-operator">+</span> <span class="cm-variable">req</span>.<span class="cm-variable">getRemoteAddr</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 得到目标的真正的责任节点</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">final</span> <span class="cm-variable-3">String</span> <span class="cm-variable">targetServer</span> <span class="cm-operator">=</span> <span class="cm-variable">distroMapper</span>.<span class="cm-variable">mapSrv</span>(<span class="cm-variable">groupedServiceName</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-variable">headerList</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">ArrayList</span><span class="cm-operator">&lt;&gt;</span>(<span class="cm-number">16</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Enumeration</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-variable">headers</span> <span class="cm-operator">=</span> <span class="cm-variable">req</span>.<span class="cm-variable">getHeaderNames</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">while</span> (<span class="cm-variable">headers</span>.<span class="cm-variable">hasMoreElements</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">headerName</span> <span class="cm-operator">=</span> <span class="cm-variable">headers</span>.<span class="cm-variable">nextElement</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">headerList</span>.<span class="cm-variable">add</span>(<span class="cm-variable">headerName</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">headerList</span>.<span class="cm-variable">add</span>(<span class="cm-variable">req</span>.<span class="cm-variable">getHeader</span>(<span class="cm-variable">headerName</span>));</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">final</span> <span class="cm-variable-3">String</span> <span class="cm-variable">body</span> <span class="cm-operator">=</span> <span class="cm-variable">IoUtils</span>.<span class="cm-variable">toString</span>(<span class="cm-variable">req</span>.<span class="cm-variable">getInputStream</span>(), <span class="cm-variable">Charsets</span>.<span class="cm-variable">UTF_8</span>.<span class="cm-variable">name</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">final</span> <span class="cm-variable">Map</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span>, <span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-variable">paramsValue</span> <span class="cm-operator">=</span> <span class="cm-variable">HttpClient</span>.<span class="cm-variable">translateParameterMap</span>(<span class="cm-variable">req</span>.<span class="cm-variable">getParameterMap</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">RestResult</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-variable">HttpClient</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  .<span class="cm-variable">request</span>(<span class="cm-string">"http://"</span> <span class="cm-operator">+</span> <span class="cm-variable">targetServer</span> <span class="cm-operator">+</span> <span class="cm-variable">req</span>.<span class="cm-variable">getRequestURI</span>(), <span class="cm-variable">headerList</span>, <span class="cm-variable">paramsValue</span>, <span class="cm-variable">body</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">PROXY_CONNECT_TIMEOUT</span>, <span class="cm-variable">PROXY_READ_TIMEOUT</span>, <span class="cm-variable">Charsets</span>.<span class="cm-variable">UTF_8</span>.<span class="cm-variable">name</span>(), <span class="cm-variable">req</span>.<span class="cm-variable">getMethod</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">data</span> <span class="cm-operator">=</span> <span class="cm-variable">result</span>.<span class="cm-variable">ok</span>() <span class="cm-operator">?</span> <span class="cm-variable">result</span>.<span class="cm-variable">getData</span>() : <span class="cm-variable">result</span>.<span class="cm-variable">getMessage</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">try</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">WebUtils</span>.<span class="cm-variable">response</span>(<span class="cm-variable">resp</span>, <span class="cm-variable">data</span>, <span class="cm-variable">result</span>.<span class="cm-variable">getCode</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  } <span class="cm-keyword">catch</span> (<span class="cm-variable">Exception</span> <span class="cm-variable">ignore</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">SRV_LOG</span>.<span class="cm-variable">warn</span>(<span class="cm-string">"[DISTRO-FILTER] request failed: "</span> <span class="cm-operator">+</span> <span class="cm-variable">distroMapper</span>.<span class="cm-variable">mapSrv</span>(<span class="cm-variable">groupedServiceName</span>)</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-operator">+</span> <span class="cm-variable">urlString</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  } <span class="cm-keyword">else</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">OverrideParameterRequestWrapper</span> <span class="cm-variable">requestWrapper</span> <span class="cm-operator">=</span> <span class="cm-variable">OverrideParameterRequestWrapper</span>.<span class="cm-variable">buildRequest</span>(<span class="cm-variable">req</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">requestWrapper</span>.<span class="cm-variable">addParameter</span>(<span class="cm-variable">CommonParams</span>.<span class="cm-variable">SERVICE_NAME</span>, <span class="cm-variable">groupedServiceName</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">filterChain</span>.<span class="cm-variable">doFilter</span>(<span class="cm-variable">requestWrapper</span>, <span class="cm-variable">resp</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">catch</span> (<span class="cm-variable">AccessControlException</span> <span class="cm-variable">e</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">resp</span>.<span class="cm-variable">sendError</span>(<span class="cm-variable">HttpServletResponse</span>.<span class="cm-variable">SC_FORBIDDEN</span>, <span class="cm-string">"access denied: "</span> <span class="cm-operator">+</span> <span class="cm-variable">ExceptionUtil</span>.<span class="cm-variable">getAllExceptionMsg</span>(<span class="cm-variable">e</span>));</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">catch</span> (<span class="cm-variable">NoSuchMethodException</span> <span class="cm-variable">e</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">resp</span>.<span class="cm-variable">sendError</span>(<span class="cm-variable">HttpServletResponse</span>.<span class="cm-variable">SC_NOT_IMPLEMENTED</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-string">"no such api:"</span> <span class="cm-operator">+</span> <span class="cm-variable">req</span>.<span class="cm-variable">getMethod</span>() <span class="cm-operator">+</span> <span class="cm-string">":"</span> <span class="cm-operator">+</span> <span class="cm-variable">req</span>.<span class="cm-variable">getRequestURI</span>());</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">catch</span> (<span class="cm-variable">Exception</span> <span class="cm-variable">e</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">resp</span>.<span class="cm-variable">sendError</span>(<span class="cm-variable">HttpServletResponse</span>.<span class="cm-variable">SC_INTERNAL_SERVER_ERROR</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-string">"Server failed,"</span> <span class="cm-operator">+</span> <span class="cm-variable">ExceptionUtil</span>.<span class="cm-variable">getAllExceptionMsg</span>(<span class="cm-variable">e</span>));</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">那么可以看到，所有的标记了@CanDistro注解的请求，都会请求的时候判断：如果当前处理请求的集群节点，不是这个 service 的责任节点，那么这个过滤器就会转发到对应的责任节点上做处理。</span></p>
<p class="md-end-block md-p md-focus"><span class="md-plain">那我们回到最开始的问题上：</span><strong><span class="md-pair-s" spellcheck="false"><code>lastBeat 属性是如何同步的？</code></span></strong></p>
<p class="md-end-block md-p md-focus"><strong><span class="md-plain">心跳检测在请求到达节点后就能自行检测是否责任节点上，如果不在则会直接发送给责任节点，而不是等待全体同步，所以可以理解为心跳时间是定向同步而非全体同步，心跳可能会发送到非责任节点，但是非责任节点一定会检测并且转发给责任节点，责任节点对于心跳的数据不会也没有必要去同步到别的节点上，因为别的节点完全不需要也不负责</span></strong></p>]]></description>
    <pubDate>Sat, 24 Jan 2026 22:21:40 +0800</pubDate>
    <dc:creator>ZealSinger</dc:creator>
    <guid>http://8.154.40.120:24180/?post=56</guid>
</item>
<item>
    <title>Nacos源码学习计划-Day26-Nacos2.x-事件驱动架构</title>
    <link>http://8.154.40.120:24180/?post=55</link>
    <description><![CDATA[<p class="md-end-block md-p md-focus"><span class="md-plain md-expand">我们前面提到了，Nacos2.X版本中出现了很多的事件驱动架构的业务处理，对于很多逻辑，例如客户端注册/下线；服务改变；服务订阅等等都是发起一个Event，然后对应的有个Handler处理器进行处理</span></p>
<h1 class="md-end-block md-heading"><span class="md-plain">整体结构分析</span></h1>
<p class="md-end-block md-p"><span class="md-plain">我们可以来看一下之前我们已经了解过的几个Event类对象，其实可以发现，都是继承自Event这个事件父类</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260123181253736.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260123181253736.png" alt="image-20260123181253736"></span></p>
<p class="md-end-block md-p"><span class="md-plain">同样的，我们再去看一下所有的Handler所在类，也可以发现，有些继承自</span><strong><span class="md-pair-s" spellcheck="false"><code>SmartSubscriber</code></span></strong><span class="md-plain">，并且</span><span class="md-pair-s" spellcheck="false"><code>SmartSubscriber</code></span><span class="md-plain">之上还有个顶级接口</span><span class="md-pair-s" spellcheck="false"><code>Subscriber</code></span><span class="md-plain">，就是订阅者，在这里定义了顶级抽象方法</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-keyword">abstract</span> <span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable">Class</span><span class="cm-operator">&lt;?</span> <span class="cm-keyword">extends</span> <span class="cm-variable">Event</span><span class="cm-operator">&gt;&gt;</span> <span class="cm-def">subscribeTypes</span>();</span></pre>
<p class="md-end-block md-p"><strong><span class="md-plain">该方法的主要作用就是返回该订阅者感兴趣的Event类型的List集合</span></strong></p>
<p class="md-end-block md-p"><span class="md-plain">除此之外在顶级接口中</span><span class="md-pair-s" spellcheck="false"><code>Subscriber</code></span><span class="md-plain">中还有一个</span><span class="md-pair-s" spellcheck="false"><code>onEvent()</code></span><span class="md-plain">方法，该方法就是当对应的Event来了之后进行处理回调的方法</span></p>
<p class="md-end-block md-p"><span class="md-plain">在每个子类的构造方法上，还会调用一个</span><span class="md-pair-s" spellcheck="false"><code>registerSubscriber</code></span><span class="md-plain">方法，该方法是</span><span class="md-pair-s" spellcheck="false"><code>NotifyCenter</code></span><span class="md-plain">中的静态方法</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">registerSubscriber</span>(<span class="cm-keyword">this</span>);</span><br><span role="presentation"><span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">registerSubscriber</span>(<span class="cm-keyword">this</span>, <span class="cm-variable">NamingEventPublisherFactory</span>.<span class="cm-variable">getInstance</span>());</span></pre>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260123181418603.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260123181418603.png" alt="image-20260123181418603"></span></p>
<p class="md-end-block md-p"><span class="md-plain">根据如上的机构，其实我们完全可以自己基于Nacos提供的事件驱动架构，自己写一些事件和订阅者进行运行</span></p>
<p class="md-end-block md-p"><span class="md-plain">如下，我们先写一个</span><span class="md-pair-s" spellcheck="false"><code>TestEvent</code></span><span class="md-plain">测试事件类</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">import</span> <span class="cm-variable">com</span>.<span class="cm-variable">alibaba</span>.<span class="cm-variable">nacos</span>.<span class="cm-variable">common</span>.<span class="cm-variable">notify</span>.<span class="cm-variable">Event</span>;</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-keyword">class</span> <span class="cm-def">TestEvent</span> <span class="cm-keyword">extends</span> <span class="cm-variable">Event</span> {</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">然后写一个对应的订阅者</span><span class="md-pair-s" spellcheck="false"><code>TestSubscriber</code></span><span class="md-plain">，实现</span><span class="md-pair-s" spellcheck="false"><code>subscribeTypes</code></span><span class="md-plain">和</span><span class="md-pair-s" spellcheck="false"><code>onEvent</code></span><span class="md-plain">方法，在构造方法中将该订阅者注册上去</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">import</span> <span class="cm-variable">com</span>.<span class="cm-variable">alibaba</span>.<span class="cm-variable">nacos</span>.<span class="cm-variable">common</span>.<span class="cm-variable">notify</span>.<span class="cm-variable">Event</span>;</span><br><span role="presentation"><span class="cm-keyword">import</span> <span class="cm-variable">com</span>.<span class="cm-variable">alibaba</span>.<span class="cm-variable">nacos</span>.<span class="cm-variable">common</span>.<span class="cm-variable">notify</span>.<span class="cm-variable">NotifyCenter</span>;</span><br><span role="presentation"><span class="cm-keyword">import</span> <span class="cm-variable">com</span>.<span class="cm-variable">alibaba</span>.<span class="cm-variable">nacos</span>.<span class="cm-variable">common</span>.<span class="cm-variable">notify</span>.<span class="cm-variable">listener</span>.<span class="cm-variable">SmartSubscriber</span>;</span><br><span role="presentation"><span class="cm-keyword">import</span> <span class="cm-variable">org</span>.<span class="cm-variable">springframework</span>.<span class="cm-variable">stereotype</span>.<span class="cm-variable">Component</span>;</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">import</span> <span class="cm-variable">java</span>.<span class="cm-variable">util</span>.<span class="cm-variable">LinkedList</span>;</span><br><span role="presentation"><span class="cm-keyword">import</span> <span class="cm-variable">java</span>.<span class="cm-variable">util</span>.<span class="cm-variable">List</span>;</span><br><span role="presentation">​</span><br><span class="cm-meta">@Component</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-keyword">class</span> <span class="cm-def">TestSubscriber</span> <span class="cm-keyword">extends</span> <span class="cm-variable">SmartSubscriber</span> {</span><br><span role="presentation">​</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">public</span> <span class="cm-variable">TestSubscriber</span>() {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">registerSubscriber</span>(<span class="cm-keyword">this</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">​</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-meta">@Override</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">public</span> <span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable">Class</span><span class="cm-operator">&lt;?</span> <span class="cm-keyword">extends</span> <span class="cm-variable">Event</span><span class="cm-operator">&gt;&gt;</span> <span class="cm-variable">subscribeTypes</span>() {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable">Class</span><span class="cm-operator">&lt;?</span> <span class="cm-keyword">extends</span> <span class="cm-variable">Event</span><span class="cm-operator">&gt;&gt;</span> <span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">LinkedList</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">result</span>.<span class="cm-variable">add</span>(<span class="cm-variable">TestEvent</span>.<span class="cm-keyword">class</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">result</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">​</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-meta">@Override</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-variable">onEvent</span>(<span class="cm-variable">Event</span> <span class="cm-variable">event</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">System</span>.<span class="cm-variable">out</span>.<span class="cm-variable">println</span>(<span class="cm-string">"TestSubscriber onEvent"</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">我们写一个测试类，来测试一下上面的代码，可以看到，调用完</span><span class="md-pair-s" spellcheck="false"><code>publishEvent</code></span><span class="md-plain">之后，对应的订阅者的</span><span class="md-pair-s" spellcheck="false"><code>onEvent</code></span><span class="md-plain">方法也成功被调用且打印了输出</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124114757110.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124114757110.png" alt="image-20260124114757110"></span></p>
<h1 class="md-end-block md-heading"><span class="md-plain">源码分析</span></h1>
<h2 class="md-end-block md-heading"><span class="md-plain">事件发布</span></h2>
<p class="md-end-block md-p"><span class="md-plain">上面我们了解一下事件驱动架构的大致结构，我们现在就可以开始分析，我们先从事件的发布这块入手</span></p>
<p class="md-end-block md-p"><span class="md-plain">我们知道，发布事件用的是</span><span class="md-pair-s" spellcheck="false"><code>NotifyCenter.publishEvent</code></span><span class="md-plain">这个方法，我们来看下这个方法的源码</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">TestEvent</span>());</span><br><span role="presentation">​</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-keyword">static</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">publishEvent</span>(<span class="cm-keyword">final</span> <span class="cm-variable">Event</span> <span class="cm-variable">event</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">try</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 跟主线任务</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">publishEvent</span>(<span class="cm-variable">event</span>.<span class="cm-variable">getClass</span>(), <span class="cm-variable">event</span>);</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">catch</span> (<span class="cm-variable">Throwable</span> <span class="cm-variable">ex</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">LOGGER</span>.<span class="cm-variable">error</span>(<span class="cm-string">"There was an exception to the message publishing : "</span>, <span class="cm-variable">ex</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">false</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">static</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">publishEvent</span>(<span class="cm-keyword">final</span> <span class="cm-variable">Class</span><span class="cm-operator">&lt;?</span> <span class="cm-keyword">extends</span> <span class="cm-variable">Event</span><span class="cm-operator">&gt;</span> <span class="cm-variable">eventType</span>, <span class="cm-keyword">final</span> <span class="cm-variable">Event</span> <span class="cm-variable">event</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">ClassUtils</span>.<span class="cm-variable">isAssignableFrom</span>(<span class="cm-variable">SlowEvent</span>.<span class="cm-keyword">class</span>, <span class="cm-variable">eventType</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">INSTANCE</span>.<span class="cm-variable">sharePublisher</span>.<span class="cm-variable">publish</span>(<span class="cm-variable">event</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 根据Event的类型的到事件类型Topic</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">final</span> <span class="cm-variable-3">String</span> <span class="cm-variable">topic</span> <span class="cm-operator">=</span> <span class="cm-variable">ClassUtils</span>.<span class="cm-variable">getCanonicalName</span>(<span class="cm-variable">eventType</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 根据发布事件类型获取 EventPublisher 对象，该对象中会包含订阅者信息</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 订阅者怎么来的？ 从这里可以看到肯定是从一个Map中获取的，具体的在下文章节会讲到，publisherMap其实就是一个以Event全类名为key，EventPublisher对象(该对象成员属性subscribers会保存对应的订阅者类)为value的map，</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">EventPublisher</span> <span class="cm-variable">publisher</span> <span class="cm-operator">=</span> <span class="cm-variable">INSTANCE</span>.<span class="cm-variable">publisherMap</span>.<span class="cm-variable">get</span>(<span class="cm-variable">topic</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">publisher</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 跟主线任务</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">publisher</span>.<span class="cm-variable">publish</span>(<span class="cm-variable">event</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">LOGGER</span>.<span class="cm-variable">warn</span>(<span class="cm-string">"There are no [{}] publishers for this event, please register"</span>, <span class="cm-variable">topic</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">false</span>;</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">​</span><br><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">publish</span>(<span class="cm-variable">Event</span> <span class="cm-variable">event</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">checkIsStart</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 把事件放入到了一个 阻塞队列</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">boolean</span> <span class="cm-variable">success</span> <span class="cm-operator">=</span> <span class="cm-keyword">this</span>.<span class="cm-variable">queue</span>.<span class="cm-variable">offer</span>(<span class="cm-variable">event</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 如果没有放入成功则手动调用receiveEvent方法接收并通知Subscriber处理该事件</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">success</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">LOGGER</span>.<span class="cm-variable">warn</span>(<span class="cm-string">"Unable to plug in due to interruption, synchronize sending time, event : {}"</span>, <span class="cm-variable">event</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">receiveEvent</span>(<span class="cm-variable">event</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到，又是利用到了阻塞队列，自然我们就可以去找到队列中拿任务的逻辑</span></p>
<p class="md-end-block md-p"><span class="md-plain">可以发现就在</span><span class="md-pair-s" spellcheck="false"><code>DefaultPublisher</code></span><span class="md-plain">中，在这一段代码中，我们有看到 </span><span class="md-pair-s" spellcheck="false"><code>subscribers</code></span><span class="md-plain"> 一个 Set 集合，注意，这里并不是全部的订阅者，只是当前发布的事件对应的订阅者，并不会通知全部的订阅者哈</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">for</span> (; ; ) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">shutdown</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">break</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 从阻塞队列取数据</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">final</span> <span class="cm-variable">Event</span> <span class="cm-variable">event</span> <span class="cm-operator">=</span> <span class="cm-variable">queue</span>.<span class="cm-variable">take</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 处理事件</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">receiveEvent</span>(<span class="cm-variable">event</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">UPDATER</span>.<span class="cm-variable">compareAndSet</span>(<span class="cm-keyword">this</span>, <span class="cm-variable">lastEventSequence</span>, <span class="cm-variable">Math</span>.<span class="cm-variable">max</span>(<span class="cm-variable">lastEventSequence</span>, <span class="cm-variable">event</span>.<span class="cm-variable">sequence</span>()));</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">protected</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentHashSet</span><span class="cm-operator">&lt;</span><span class="cm-variable">Subscriber</span><span class="cm-operator">&gt;</span> <span class="cm-variable">subscribers</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashSet</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-variable-3">void</span> <span class="cm-def">receiveEvent</span>(<span class="cm-variable">Event</span> <span class="cm-variable">event</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">final</span> <span class="cm-variable-3">long</span> <span class="cm-variable">currentEventSequence</span> <span class="cm-operator">=</span> <span class="cm-variable">event</span>.<span class="cm-variable">sequence</span>();</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">hasSubscriber</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">LOGGER</span>.<span class="cm-variable">warn</span>(<span class="cm-string">"[NotifyCenter] the {} is lost, because there is no subscriber."</span>, <span class="cm-variable">event</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 循环遍历通知发布事件对应的订阅者</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">for</span> (<span class="cm-variable">Subscriber</span> <span class="cm-variable">subscriber</span> : <span class="cm-variable">subscribers</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// Whether to ignore expiration events</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">subscriber</span>.<span class="cm-variable">ignoreExpireEvent</span>() <span class="cm-operator">&amp;&amp;</span> <span class="cm-variable">lastEventSequence</span> <span class="cm-operator">&gt;</span> <span class="cm-variable">currentEventSequence</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">LOGGER</span>.<span class="cm-variable">debug</span>(<span class="cm-string">"[NotifyCenter] the {} is unacceptable to this subscriber, because had expire"</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">event</span>.<span class="cm-variable">getClass</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">continue</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// Because unifying smartSubscriber and subscriber, so here need to think of compatibility.</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// Remove original judge part of codes.</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">//通知</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">notifySubscriber</span>(<span class="cm-variable">subscriber</span>, <span class="cm-variable">event</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">然后最后调用了 </span><span class="md-pair-s" spellcheck="false"><code>notifySubscriber(subscriber, event);</code></span><span class="md-plain"> 方法来通知订阅者，代码如下:</span></p>
<p class="md-end-block md-p"><span class="md-plain">通过这段代码，我们能够知道，</span><strong><span class="md-plain">最后是使用线程异步的方式</span></strong><span class="md-plain">，来通知订阅者的。 事件发布流程我们就分析完了，核心关键还是利用了 </span><span class="md-pair-s" spellcheck="false"><code>阻塞队列 + 异步任务</code></span><span class="md-plain"> 来进行实现的，那么接下来我们一起来看看，订阅者注册的流程。</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">notifySubscriber</span>(<span class="cm-keyword">final</span> <span class="cm-variable">Subscriber</span> <span class="cm-variable">subscriber</span>, <span class="cm-keyword">final</span> <span class="cm-variable">Event</span> <span class="cm-variable">event</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">LOGGER</span>.<span class="cm-variable">debug</span>(<span class="cm-string">"[NotifyCenter] the {} will received by {}"</span>, <span class="cm-variable">event</span>, <span class="cm-variable">subscriber</span>);</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 创建一个任务，就是调用订阅者的 onEvent 方法</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">final</span> <span class="cm-variable">Runnable</span> <span class="cm-variable">job</span> <span class="cm-operator">=</span> () <span class="cm-operator">-&gt;</span> <span class="cm-variable">subscriber</span>.<span class="cm-variable">onEvent</span>(<span class="cm-variable">event</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">final</span> <span class="cm-variable">Executor</span> <span class="cm-variable">executor</span> <span class="cm-operator">=</span> <span class="cm-variable">subscriber</span>.<span class="cm-variable">executor</span>();</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">executor</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 执行任务</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">executor</span>.<span class="cm-variable">execute</span>(<span class="cm-variable">job</span>);</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">else</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">try</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">job</span>.<span class="cm-variable">run</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  } <span class="cm-keyword">catch</span> (<span class="cm-variable">Throwable</span> <span class="cm-variable">e</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">LOGGER</span>.<span class="cm-variable">error</span>(<span class="cm-string">"Event callback exception: "</span>, <span class="cm-variable">e</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<h2 class="md-end-block md-heading"><span class="md-plain">订阅者注册</span></h2>
<p class="md-end-block md-p"><span class="md-plain">看完了事件的发布，我们来看订阅者这边</span></p>
<p class="md-end-block md-p"><span class="md-plain">每个订阅者在构造方法中都需要先将自己注册，所以我们先来看注册部分的代码，也就是</span><span class="md-pair-s" spellcheck="false"><code>NotifyCenter.registerSubscriber()</code></span></p>
<p class="md-end-block md-p"><span class="md-plain">可以看到，首先这个就是调用</span><span class="md-pair-s" spellcheck="false"><code>subscribeTypes</code></span><span class="md-plain">获取当前订阅者订阅的Event类型的List列表，然后遍历这个列表，调用</span><span class="md-pair-s" spellcheck="false"><code>INSTANCE.sharePublisher.addSubscriber</code></span><span class="md-plain">封装保存或者调用</span><span class="md-pair-s" spellcheck="false"><code>addSubscriber</code></span><span class="md-plain">这个方法(两种情况的底层都将内容保存到了subscribers这个Set中) </span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">TestSubscriber</span>() {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">registerSubscriber</span>(<span class="cm-keyword">this</span>);</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-keyword">static</span> <span class="cm-variable-3">void</span> <span class="cm-def">registerSubscriber</span>(<span class="cm-keyword">final</span> <span class="cm-variable">Subscriber</span> <span class="cm-variable">consumer</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">registerSubscriber</span>(<span class="cm-variable">consumer</span>, <span class="cm-variable">DEFAULT_PUBLISHER_FACTORY</span>);</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-keyword">static</span> <span class="cm-variable-3">void</span> <span class="cm-def">registerSubscriber</span>(<span class="cm-keyword">final</span> <span class="cm-variable">Subscriber</span> <span class="cm-variable">consumer</span>, <span class="cm-keyword">final</span> <span class="cm-variable">EventPublisherFactory</span> <span class="cm-variable">factory</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// If you want to listen to multiple events, you do it separately,</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// based on subclass's subscribeTypes method return list, it can register to publisher.</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">consumer</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">SmartSubscriber</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 在这里会调用 subscribeTypes 方法，来获取我们需要监听的事件，然后进行遍历</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">for</span> (<span class="cm-variable">Class</span><span class="cm-operator">&lt;?</span> <span class="cm-keyword">extends</span> <span class="cm-variable">Event</span><span class="cm-operator">&gt;</span> <span class="cm-variable">subscribeType</span> : ((<span class="cm-variable">SmartSubscriber</span>) <span class="cm-variable">consumer</span>).<span class="cm-variable">subscribeTypes</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// For case, producer: defaultSharePublisher -&gt; consumer: smartSubscriber.</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">ClassUtils</span>.<span class="cm-variable">isAssignableFrom</span>(<span class="cm-variable">SlowEvent</span>.<span class="cm-keyword">class</span>, <span class="cm-variable">subscribeType</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">INSTANCE</span>.<span class="cm-variable">sharePublisher</span>.<span class="cm-variable">addSubscriber</span>(<span class="cm-variable">consumer</span>, <span class="cm-variable">subscribeType</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  } <span class="cm-keyword">else</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// For case, producer: defaultPublisher -&gt; consumer: subscriber.</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 添加订阅者</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">addSubscriber</span>(<span class="cm-variable">consumer</span>, <span class="cm-variable">subscribeType</span>, <span class="cm-variable">factory</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 这里代码省略</span></span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">接下来我们继续看</span><span class="md-pair-s" spellcheck="false"><code>addSubscriber</code></span><span class="md-plain">方法</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">static</span> <span class="cm-variable-3">void</span> <span class="cm-def">addSubscriber</span>(<span class="cm-keyword">final</span> <span class="cm-variable">Subscriber</span> <span class="cm-variable">consumer</span>, <span class="cm-variable">Class</span><span class="cm-operator">&lt;?</span> <span class="cm-keyword">extends</span> <span class="cm-variable">Event</span><span class="cm-operator">&gt;</span> <span class="cm-variable">subscribeType</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">EventPublisherFactory</span> <span class="cm-variable">factory</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">final</span> <span class="cm-variable-3">String</span> <span class="cm-variable">topic</span> <span class="cm-operator">=</span> <span class="cm-variable">ClassUtils</span>.<span class="cm-variable">getCanonicalName</span>(<span class="cm-variable">subscribeType</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">synchronized</span> (<span class="cm-variable">NotifyCenter</span>.<span class="cm-keyword">class</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 这里很关键，会创建 EventPublisher 对象，一个事件会对应一个 EventPublisher 一个对象</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">MapUtil</span>.<span class="cm-variable">computeIfAbsent</span>(<span class="cm-variable">INSTANCE</span>.<span class="cm-variable">publisherMap</span>, <span class="cm-variable">topic</span>, <span class="cm-variable">factory</span>, <span class="cm-variable">subscribeType</span>, <span class="cm-variable">ringBufferSize</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 获取事件对应的 EventPublisher 对象</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">EventPublisher</span> <span class="cm-variable">publisher</span> <span class="cm-operator">=</span> <span class="cm-variable">INSTANCE</span>.<span class="cm-variable">publisherMap</span>.<span class="cm-variable">get</span>(<span class="cm-variable">topic</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">publisher</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">ShardedEventPublisher</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  ((<span class="cm-variable">ShardedEventPublisher</span>) <span class="cm-variable">publisher</span>).<span class="cm-variable">addSubscriber</span>(<span class="cm-variable">consumer</span>, <span class="cm-variable">subscribeType</span>);</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">else</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 往 EventPublisher 对象添加订阅者信息</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">publisher</span>.<span class="cm-variable">addSubscriber</span>(<span class="cm-variable">consumer</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><strong><span class="md-plain">其中</span><span class="md-pair-s" spellcheck="false"><code>MapUtil.computeIfAbsent</code></span><span class="md-plain">的逻辑如下，可以看到，在这里会利用传入的第三个参数，调用其apply方法，利用第四个和第五个参数创建出一个EventPublisher对象，这个对象内部会存储我们的订阅者信息，也就是 </span><span class="md-pair-s" spellcheck="false"><code>subscribers</code></span><span class="md-plain"> 属性</span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@NotThreadSafe</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-keyword">static</span> <span class="cm-operator">&lt;</span><span class="cm-variable">K</span>, <span class="cm-variable">C</span>, <span class="cm-variable">V</span>, <span class="cm-variable">T</span><span class="cm-operator">&gt;</span> <span class="cm-variable">V</span> <span class="cm-def">computeIfAbsent</span>(<span class="cm-variable">Map</span><span class="cm-operator">&lt;</span><span class="cm-variable">K</span>, <span class="cm-variable">V</span><span class="cm-operator">&gt;</span> <span class="cm-variable">target</span>, <span class="cm-variable">K</span> <span class="cm-variable">key</span>, <span class="cm-variable">BiFunction</span><span class="cm-operator">&lt;</span><span class="cm-variable">C</span>, <span class="cm-variable">T</span>, <span class="cm-variable">V</span><span class="cm-operator">&gt;</span> <span class="cm-variable">mappingFunction</span>, <span class="cm-variable">C</span> <span class="cm-variable">param1</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">T</span> <span class="cm-variable">param2</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Objects</span>.<span class="cm-variable">requireNonNull</span>(<span class="cm-variable">target</span>, <span class="cm-string">"target"</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Objects</span>.<span class="cm-variable">requireNonNull</span>(<span class="cm-variable">key</span>, <span class="cm-string">"key"</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Objects</span>.<span class="cm-variable">requireNonNull</span>(<span class="cm-variable">mappingFunction</span>, <span class="cm-string">"mappingFunction"</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Objects</span>.<span class="cm-variable">requireNonNull</span>(<span class="cm-variable">param1</span>, <span class="cm-string">"param1"</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Objects</span>.<span class="cm-variable">requireNonNull</span>(<span class="cm-variable">param2</span>, <span class="cm-string">"param2"</span>);</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">V</span> <span class="cm-variable">val</span> <span class="cm-operator">=</span> <span class="cm-variable">target</span>.<span class="cm-variable">get</span>(<span class="cm-variable">key</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">val</span> <span class="cm-operator">==</span> <span class="cm-atom">null</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">V</span> <span class="cm-variable">ret</span> <span class="cm-operator">=</span> <span class="cm-variable">mappingFunction</span>.<span class="cm-variable">apply</span>(<span class="cm-variable">param1</span>, <span class="cm-variable">param2</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">target</span>.<span class="cm-variable">put</span>(<span class="cm-variable">key</span>, <span class="cm-variable">ret</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">ret</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">val</span>;</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124171514611.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260124171514611.png" alt="image-20260124171514611"></span></p>
<p class="md-end-block md-p"><span class="md-plain">在代码的最后，</span><strong><span class="md-plain">会调用 </span><span class="md-pair-s" spellcheck="false"><code>addSubscriber</code></span><span class="md-plain"> 方法往</span><span class="md-pair-s" spellcheck="false"><code>subscribers</code></span><span class="md-plain"> 属性中添加订阅者，完成注册</span></strong><span class="md-plain">。</span></p>
<h1 class="md-end-block md-heading md-focus"><span class="md-plain">总结</span></h1>
<p class="md-end-block md-p"><span class="md-plain">Nacos2.X中的事件驱动架构，整体基于Event(事件)-Subscriber(订阅者)-EventPublish(事件发布者)-NotifyCenter(事件中心)</span></p>
<p class="md-end-block md-p"><span class="md-plain">订阅者通过注册中心进行注册，通过订阅者</span><span class="md-pair-s" spellcheck="false"><code>subscribeTypes()</code></span><span class="md-plain">方法来获取其关注的Event事件类型的集合，对于非慢事件利用工厂创建对应的</span><span class="md-pair-s" spellcheck="false"><code>EventPublisher</code></span><span class="md-plain">，并且将订阅者保存到</span><span class="md-pair-s" spellcheck="false"><code>EventPublisher</code></span><span class="md-plain"> 的 </span><span class="md-pair-s" spellcheck="false"><code>subscribers</code></span><span class="md-plain"> 集合，在</span><span class="md-pair-s" spellcheck="false"><code>publisherMap</code></span><span class="md-plain">中保存事件-事件发布者的对应关系，</span><span class="md-pair-s" spellcheck="false"><code>EventPublisher</code></span><span class="md-plain"> 初始化时启动异步线程，监听阻塞队列，等待事件到来</span></p>
<p class="md-end-block md-p md-focus"><span class="md-plain">通过事件中心发布事件的时候，</span><span class="md-pair-s" spellcheck="false"><code>根据事件的类型获得其全类名作为topic</code></span><span class="md-plain">，利用</span><span class="md-pair-s" spellcheck="false"><code>topic</code></span><span class="md-plain">从</span><span class="md-pair-s" spellcheck="false"><code>publisherMap</code></span><span class="md-plain">中获取对应的</span><span class="md-pair-s" spellcheck="false"><code>EventPublisher</code></span><span class="md-plain">，利用</span><span class="md-pair-s" spellcheck="false"><code>EventPublisher</code></span><span class="md-plain md-expand">将任务写入到阻塞任务队列中</span></p>]]></description>
    <pubDate>Sat, 24 Jan 2026 17:58:50 +0800</pubDate>
    <dc:creator>ZealSinger</dc:creator>
    <guid>http://8.154.40.120:24180/?post=55</guid>
</item>
<item>
    <title>Nacos源码学习计划-Day25-Nacos2.x-服务的注销</title>
    <link>http://8.154.40.120:24180/?post=54</link>
    <description><![CDATA[<p class="md-end-block md-p md-focus"><span class="md-plain md-expand">今天来看服务下线的源码部分，也就是当注册到Nacos上的服务终止的时候进行的逻辑操作</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">客户端的相关操作</span></h2>
<p class="md-end-block md-p"><span class="md-plain">怎么找到对应的代码块这边我们就不过多表述了，肯定还是主要围绕</span><span class="md-pair-s" spellcheck="false"><code>AbstractAutoServiceRegistration</code></span><span class="md-plain">这个自动注册相关的类进行的操作</span></p>
<p class="md-end-block md-p"><span class="md-plain">我们可以看到，在这个类下，有一个</span><span class="md-pair-s" spellcheck="false"><code>@PreDestroy</code></span><span class="md-plain">注解标识的销毁方法，该方法内部就是调用了</span><span class="md-pair-s" spellcheck="false"><code>stop</code></span><span class="md-plain">方法</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260121171102948.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260121171102948.png" alt="image-20260121171102948"></span></p>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>stop</code></span><span class="md-plain">方法的逻辑如下</span></p>
<p class="md-end-block md-p"><span class="md-plain">可以看到，整体内容和Nacos1.4版本的逻辑操作相差不大，对客户端保存的注册实例遍历然后依次执行一些注销前置操作，注销后置操作等等</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable">RegistrationLifecycle</span><span class="cm-operator">&lt;</span><span class="cm-variable">R</span><span class="cm-operator">&gt;&gt;</span> <span class="cm-variable">registrationLifecycles</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">ArrayList</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">stop</span>() {</span><br><span role="presentation">    <span class="cm-keyword">if</span> (<span class="cm-keyword">this</span>.<span class="cm-variable">getRunning</span>().<span class="cm-variable">compareAndSet</span>(<span class="cm-atom">true</span>, <span class="cm-atom">false</span>) <span class="cm-operator">&amp;&amp;</span> <span class="cm-variable">isEnabled</span>()) {</span><br><span role="presentation">​</span><br><span role="presentation">        <span class="cm-keyword">this</span>.<span class="cm-variable">registrationLifecycles</span>.<span class="cm-variable">forEach</span>(</span><br><span role="presentation">                <span class="cm-variable">registrationLifecycle</span> <span class="cm-operator">-&gt;</span> <span class="cm-variable">registrationLifecycle</span>.<span class="cm-variable">postProcessBeforeStopRegister</span>(<span class="cm-variable">getRegistration</span>()));</span><br><span role="presentation">        <span class="cm-variable">deregister</span>();</span><br><span role="presentation">        <span class="cm-keyword">this</span>.<span class="cm-variable">registrationLifecycles</span>.<span class="cm-variable">forEach</span>(</span><br><span role="presentation">                <span class="cm-variable">registrationLifecycle</span> <span class="cm-operator">-&gt;</span> <span class="cm-variable">registrationLifecycle</span>.<span class="cm-variable">postProcessAfterStopRegister</span>(<span class="cm-variable">getRegistration</span>()));</span><br><span role="presentation">        <span class="cm-keyword">if</span> (<span class="cm-variable">shouldRegisterManagement</span>()) {</span><br><span role="presentation">            <span class="cm-keyword">this</span>.<span class="cm-variable">registrationManagementLifecycles</span></span><br><span role="presentation">                    .<span class="cm-variable">forEach</span>(<span class="cm-variable">registrationManagementLifecycle</span> <span class="cm-operator">-&gt;</span> <span class="cm-variable">registrationManagementLifecycle</span></span><br><span role="presentation">                            .<span class="cm-variable">postProcessBeforeStopRegisterManagement</span>(<span class="cm-variable">getManagementRegistration</span>()));</span><br><span role="presentation">            <span class="cm-variable">deregisterManagement</span>();</span><br><span role="presentation">            <span class="cm-keyword">this</span>.<span class="cm-variable">registrationManagementLifecycles</span></span><br><span role="presentation">                    .<span class="cm-variable">forEach</span>(<span class="cm-variable">registrationManagementLifecycle</span> <span class="cm-operator">-&gt;</span> <span class="cm-variable">registrationManagementLifecycle</span></span><br><span role="presentation">                            .<span class="cm-variable">postProcessAfterStopRegisterManagement</span>(<span class="cm-variable">getManagementRegistration</span>()));</span><br><span role="presentation">        }</span><br><span role="presentation">        <span class="cm-keyword">this</span>.<span class="cm-variable">serviceRegistry</span>.<span class="cm-variable">close</span>();</span><br><span role="presentation">    }</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">在最后</span><span class="md-pair-s" spellcheck="false"><code>deregisterManagement()</code></span><span class="md-plain">中最后会发送一个Rpc的请求发送给服务端</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260121172019699.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260121172019699.png" alt="image-20260121172019699"></span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">服务端处理Rpc请求</span></h2>
<p class="md-end-block md-p"><span class="md-plain">依旧是先找到对应的Handler，依旧是熟悉的switch-case对于不同类型的区分处理</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260121172226490.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260121172226490.png" alt="image-20260121172226490"></span></p>
<p class="md-end-block md-p"><span class="md-plain">对应的</span><span class="md-pair-s" spellcheck="false"><code>deregisterInstance()</code></span><span class="md-plain">方法的代码如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable">InstanceResponse</span> <span class="cm-def">deregisterInstance</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable">InstanceRequest</span> <span class="cm-variable">request</span>, <span class="cm-variable">RequestMeta</span> <span class="cm-variable">meta</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">clientOperationService</span>.<span class="cm-variable">deregisterInstance</span>(<span class="cm-variable">service</span>, <span class="cm-variable">request</span>.<span class="cm-variable">getInstance</span>(), <span class="cm-variable">meta</span>.<span class="cm-variable">getConnectionId</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">InstanceResponse</span>(<span class="cm-variable">NamingRemoteConstants</span>.<span class="cm-variable">DE_REGISTER_INSTANCE</span>);</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">​</span><br><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">deregisterInstance</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable">Instance</span> <span class="cm-variable">instance</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">ServiceManager</span>.<span class="cm-variable">getInstance</span>().<span class="cm-variable">containSingleton</span>(<span class="cm-variable">service</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">SRV_LOG</span>.<span class="cm-variable">warn</span>(<span class="cm-string">"remove instance from non-exist service: {}"</span>, <span class="cm-variable">service</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Service</span> <span class="cm-variable">singleton</span> <span class="cm-operator">=</span> <span class="cm-variable">ServiceManager</span>.<span class="cm-variable">getInstance</span>().<span class="cm-variable">getSingleton</span>(<span class="cm-variable">service</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 获取 client 对象</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Client</span> <span class="cm-variable">client</span> <span class="cm-operator">=</span> <span class="cm-variable">clientManager</span>.<span class="cm-variable">getClient</span>(<span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">clientIsLegal</span>(<span class="cm-variable">client</span>, <span class="cm-variable">clientId</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 移除 instance 信息，在这里面会操作 publishers 那个Map，以及发 ClientChangedEvent 事件，来同步集群节点</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">InstancePublishInfo</span> <span class="cm-variable">removedInstance</span> <span class="cm-operator">=</span> <span class="cm-variable">client</span>.<span class="cm-variable">removeServiceInstance</span>(<span class="cm-variable">singleton</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">client</span>.<span class="cm-variable">setLastUpdatedTime</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">!=</span> <span class="cm-variable">removedInstance</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 发布客户端注销事件</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">ClientOperationEvent</span>.<span class="cm-variable">ClientDeregisterServiceEvent</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">clientId</span>));</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">new</span> <span class="cm-variable">MetadataEvent</span>.<span class="cm-variable">InstanceMetadataEvent</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">removedInstance</span>.<span class="cm-variable">getMetadataId</span>(), <span class="cm-atom">true</span>));</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">对于客户端注销事件的处理</span><span class="md-pair-s" spellcheck="false"><code>ClientDeregisterServiceEvent</code></span><span class="md-plain">，同样的搜索对应的handler的相关逻辑</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260121172758504.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260121172758504.png" alt="image-20260121172758504"></span></p>
<p class="md-end-block md-p"><span class="md-plain">剩下的部分也就是对于内存注册表，订阅表等的一些remove操作以及同步集群了</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">总结-Nacos2.x注册中心部分</span></h2>
<ol class="ol-list">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">对于临时实例，会采用RPC的方式进行通讯</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">内存注册表的结构变化。由原本的嵌套式Map转化为了平铺式Map，结构更加i清晰</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">客户端查询微服务实例列表的时候，调用的是客户端订阅的请求，在服务端会有订阅表，当我们被订阅的服务发生了变动，会来通知订阅者。</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p md-focus"><span class="md-plain">可以明显感觉到，2.0中的事件发布操作明显更多了</span></p>
</li>
</ol>]]></description>
    <pubDate>Wed, 21 Jan 2026 17:35:51 +0800</pubDate>
    <dc:creator>ZealSinger</dc:creator>
    <guid>http://8.154.40.120:24180/?post=54</guid>
</item>
<item>
    <title>Nacos源码学习计划-Day24-Nacos2.x-服务端心跳健康实例检测</title>
    <link>http://8.154.40.120:24180/?post=53</link>
    <description><![CDATA[<p class="md-end-block md-p md-focus"><span class="md-plain md-expand">今天我们的主要内容就是Nacos2.x版本下，服务端的健康检测逻辑。因为Nacos升级后使用了gRPC，导致这个部分逻辑肯定时发生了较大的变化。</span></p>
<p class="md-end-block md-p"><span class="md-plain">注意，这里只是说服务端，即服务都安如何去检测客户端的健康状态，而不是客户端的心跳以及也不是集群中对于每个节点的健康检测。</span></p>
<p class="md-end-block md-p"><span class="md-plain">我们可以来看到</span><span class="md-pair-s" spellcheck="false"><code>ConnectionManager</code></span><span class="md-plain">，在这个类的内部有一个</span><span class="md-pair-s" spellcheck="false"><code>Map</code></span><span class="md-plain">保存了所有的了所有的</span><span class="md-pair-s" spellcheck="false"><code>Connection</code></span><span class="md-plain">连接对象，连接对象有个元数据的成员对象</span><span class="md-pair-s" spellcheck="false"><code>ConnectionMeta</code></span><span class="md-plain">，他有一个成员属性为</span><span class="md-pair-s" spellcheck="false"><code>lastActiveTime(最后存活时间)</code></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260120211513211.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260120211513211.png" alt="image-20260120211513211"></span></p>
<p class="md-end-block md-p"><span class="md-plain">在</span><span class="md-pair-s" spellcheck="false"><code>ConnectionManager</code></span><span class="md-plain">对象中，有个</span><span class="md-pair-s" spellcheck="false"><code>@PostConstruct</code></span><span class="md-plain">注解标记的</span><span class="md-pair-s" spellcheck="false"><code>start</code></span><span class="md-plain">方法，该方法会开启一个</span><span class="md-pair-s" spellcheck="false"><code>run任务</code></span><span class="md-plain">，在该方法中，</span><span class="md-pair-s" spellcheck="false"><code>遍历上述的map并且用now和元数据中的lastActiveTime进行比较判断是否存活/过时,如果发现过时了，就会放入到outDatedConnections这个Set集合中</code></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260120212234516.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260120212234516.png" alt="image-20260120212234516"></span></p>
<p class="md-end-block md-p"><strong><span class="md-pair-s" spellcheck="false"><code>outDatedConnections</code></span><span class="md-plain">在没有超出上限的时候，不会立马被清除，而是会统一在最后进行二次判断是否真正下线，如果没有下线，则会刷新activeTime并且加入到存活对象集合中，这个过程为了保证并发安全还是用了countDownLatch，这个被称之为</span><span class="md-pair-s" spellcheck="false"><code>探活机制</code></span></strong></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260120212652183.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260120212652183.png" alt="image-20260120212652183"></span></p>
<p class="md-end-block md-p"><strong><span class="md-plain">如果上述二次请求遍历完毕后依旧集合中依旧还有数据，则调用</span><span class="md-pair-s" spellcheck="false"><code>unregister()</code></span><span class="md-plain">取消注册进行删除</span></strong></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260120212840093.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20260120212840093.png" alt="image-20260120212840093"></span></p>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>unregister</code></span><span class="md-plain">源码如下，首先会从</span><span class="md-pair-s" spellcheck="false"><code>connections</code></span><span class="md-plain">这个管理了所有的连接的Map中remove，remove的时候会返回对应的Connection连接实例信息，根据实例信息中的Ip信息操作连接数，最后通知客户端注销连接</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-keyword">synchronized</span> <span class="cm-variable-3">void</span> <span class="cm-def">unregister</span>(<span class="cm-variable-3">String</span> <span class="cm-variable">connectionId</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 移除客户端信息</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Connection</span> <span class="cm-variable">remove</span> <span class="cm-operator">=</span> <span class="cm-keyword">this</span>.<span class="cm-variable">connections</span>.<span class="cm-variable">remove</span>(<span class="cm-variable">connectionId</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">remove</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">clientIp</span> <span class="cm-operator">=</span> <span class="cm-variable">remove</span>.<span class="cm-variable">getMetaInfo</span>().<span class="cm-variable">clientIp</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">AtomicInteger</span> <span class="cm-variable">atomicInteger</span> <span class="cm-operator">=</span> <span class="cm-variable">connectionForClientIp</span>.<span class="cm-variable">get</span>(<span class="cm-variable">clientIp</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">atomicInteger</span> <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">int</span> <span class="cm-variable">count</span> <span class="cm-operator">=</span> <span class="cm-variable">atomicInteger</span>.<span class="cm-variable">decrementAndGet</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">count</span> <span class="cm-operator">&lt;=</span> <span class="cm-number">0</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">connectionForClientIp</span>.<span class="cm-variable">remove</span>(<span class="cm-variable">clientIp</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">remove</span>.<span class="cm-variable">close</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">REMOTE_DIGEST</span>.<span class="cm-variable">info</span>(<span class="cm-string">"[{}]Connection unregistered successfully. "</span>, <span class="cm-variable">connectionId</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 通知客户端注销连接</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">clientConnectionEventListenerRegistry</span>.<span class="cm-variable">notifyClientDisConnected</span>(<span class="cm-variable">remove</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">来看一下通知客户端注销连接的逻辑,可以看到最后是发布了一个</span><span class="md-pair-s" spellcheck="false"><code>ClientDisconnectEvent</code></span><span class="md-plain">的事件，这个事件的处理我们在上一节的内容中分析过了，客户端会注销，同步到集群，当前节点关于该链接的数据，订阅者列表，注册表信息等等也都会被删除</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-comment">// 1</span><br><span role="presentation"><span class="cm-variable">clientConnectionEventListenerRegistry</span>.<span class="cm-variable">notifyClientDisConnected</span>(<span class="cm-variable">remove</span>);</span><br><span role="presentation">​</span><br><span class="cm-comment">// 2</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">notifyClientDisConnected</span>(<span class="cm-keyword">final</span> <span class="cm-variable">Connection</span> <span class="cm-variable">connection</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">for</span> (<span class="cm-variable">ClientConnectionEventListener</span> <span class="cm-variable">clientConnectionEventListener</span> : <span class="cm-variable">clientConnectionEventListeners</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">try</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 客户端注销，我们看这个实现类，ConnectionBasedClientManager</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">clientConnectionEventListener</span>.<span class="cm-variable">clientDisConnected</span>(<span class="cm-variable">connection</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  } <span class="cm-keyword">catch</span> (<span class="cm-variable">Throwable</span> <span class="cm-variable">throwable</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">REMOTE</span>.<span class="cm-variable">info</span>(<span class="cm-string">"[NotifyClientDisConnected] failed for listener {}"</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">clientConnectionEventListener</span>.<span class="cm-variable">getName</span>(), <span class="cm-variable">throwable</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span class="cm-comment">// 3 ConnectionBasedClientManager 实现类</span><br><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">clientDisConnected</span>(<span class="cm-variable">Connection</span> <span class="cm-variable">connect</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">clientDisconnected</span>(<span class="cm-variable">connect</span>.<span class="cm-variable">getMetaInfo</span>().<span class="cm-variable">getConnectionId</span>());</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span class="cm-comment">// 4</span><br><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">clientDisconnected</span>(<span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">SRV_LOG</span>.<span class="cm-variable">info</span>(<span class="cm-string">"Client connection {} disconnect, remove instances and subscribers"</span>, <span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">ConnectionBasedClient</span> <span class="cm-variable">client</span> <span class="cm-operator">=</span> <span class="cm-variable">clients</span>.<span class="cm-variable">remove</span>(<span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">client</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">client</span>.<span class="cm-variable">release</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 最后发布客户端注销事件</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">ClientEvent</span>.<span class="cm-variable">ClientDisconnectEvent</span>(<span class="cm-variable">client</span>));</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>]]></description>
    <pubDate>Tue, 20 Jan 2026 21:45:46 +0800</pubDate>
    <dc:creator>ZealSinger</dc:creator>
    <guid>http://8.154.40.120:24180/?post=53</guid>
</item>
<item>
    <title>Nacos源码学习计划-Day23-Nacos2.x-服务实例信息变化如何同步到集群节点</title>
    <link>http://8.154.40.120:24180/?post=52</link>
    <description><![CDATA[<p class="md-end-block md-p md-focus"><span class="md-plain md-expand">前面我们了解了Nacos2.X版本下以gRpc为客户端和服务端之间的通讯下，客户端如何注册和查询实例，以及服务端如何处理注册请求，如何将服务变更通知到订阅的客户端，也在这几个过程中加深了对于Nacos2.X版本下内存注册表的结构。</span></p>
<p class="md-end-block md-p"><span class="md-plain">在看Nacos服务端处理服务注册的时候，我们最后是探索到了</span><span class="md-pair-s" spellcheck="false"><code>AbstractClient类下的addServiceInstance()方法</code></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">addServiceInstance</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable">InstancePublishInfo</span> <span class="cm-variable">instancePublishInfo</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 把 instance 信息放入到 publishers 中</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">publishers</span>.<span class="cm-variable">put</span>(<span class="cm-variable">service</span>, <span class="cm-variable">instancePublishInfo</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">MetricsMonitor</span>.<span class="cm-variable">incrementInstanceCount</span>();</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 同步集群节点数据</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">ClientEvent</span>.<span class="cm-variable">ClientChangedEvent</span>(<span class="cm-keyword">this</span>));</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">SRV_LOG</span>.<span class="cm-variable">info</span>(<span class="cm-string">"Client change for service {}, {}"</span>, <span class="cm-variable">service</span>, <span class="cm-variable">getClientId</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">在该方法的最后那个</span><span class="md-pair-s" spellcheck="false"><code>pushlishEvent()</code></span><span class="md-plain">方法中，发布了一个</span><span class="md-pair-s" spellcheck="false"><code>ClientChangedEvent</code></span><span class="md-plain">，从命名和业务逻辑来看，这一步肯定是发布客户端改变事件用于同步别的集群节点，所以我们这一篇的内容就是来了解一下这个</span></p>
<h1 class="md-end-block md-heading"><span class="md-plain">处理ClientChangedEvent</span></h1>
<p class="md-end-block md-p"><span class="md-plain">还是老样子，我们对于Event事件类型的处理，都是查找对应的处理Event的地方，利用Idea的全局搜索轻松实现</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable-3">void</span> <span class="cm-def">syncToAllServer</span>(<span class="cm-variable">ClientEvent</span> <span class="cm-variable">event</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Client</span> <span class="cm-variable">client</span> <span class="cm-operator">=</span> <span class="cm-variable">event</span>.<span class="cm-variable">getClient</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// Only ephemeral data sync by Distro, persist client should sync by raft.</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 临时实例是走 distro 协议，持久化实例走 raft 协议</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// isResponsibleClient 这里判断了只有该 client 的责任节点有权利进行集群数据同步</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">client</span> <span class="cm-operator">||</span> <span class="cm-operator">!</span><span class="cm-variable">client</span>.<span class="cm-variable">isEphemeral</span>() <span class="cm-operator">||</span> <span class="cm-operator">!</span><span class="cm-variable">clientManager</span>.<span class="cm-variable">isResponsibleClient</span>(<span class="cm-variable">client</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 客户端注销集群节点同步事件</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">event</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">ClientEvent</span>.<span class="cm-variable">ClientDisconnectEvent</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 这里有常量默认值TYPE，其数值为 Nacos:Naming:v2:ClientData </span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">DistroKey</span> <span class="cm-variable">distroKey</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">DistroKey</span>(<span class="cm-variable">client</span>.<span class="cm-variable">getClientId</span>(), <span class="cm-variable">TYPE</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">distroProtocol</span>.<span class="cm-variable">sync</span>(<span class="cm-variable">distroKey</span>, <span class="cm-variable">DataOperation</span>.<span class="cm-variable">DELETE</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 客户端改变集群节点同步事件</span></span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">else</span> <span class="cm-keyword">if</span> (<span class="cm-variable">event</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">ClientEvent</span>.<span class="cm-variable">ClientChangedEvent</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; <span class="cm-comment">// 这里有常量默认值TYPE，其数值为 Nacos:Naming:v2:ClientData </span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">DistroKey</span> <span class="cm-variable">distroKey</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">DistroKey</span>(<span class="cm-variable">client</span>.<span class="cm-variable">getClientId</span>(), <span class="cm-variable">TYPE</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">distroProtocol</span>.<span class="cm-variable">sync</span>(<span class="cm-variable">distroKey</span>, <span class="cm-variable">DataOperation</span>.<span class="cm-variable">CHANGE</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">通过上面这一段代码我们可以得知，不管是客户端注销了，还是客户端改变了，都会来同步集群节点，它们两个都是调用了 </span><span class="md-pair-s" spellcheck="false"><code>distroProtocol.sync</code></span><span class="md-plain"> 来进行同步，唯一的区别就是参数一个是 </span><span class="md-pair-s" spellcheck="false"><code>DELETE</code></span><span class="md-plain">、一个是</span><span class="md-pair-s" spellcheck="false"><code>CHANGE</code></span><span class="md-plain">。那么我们接下来一个看下同步的具体逻辑吧，代码如下：</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">sync</span>(<span class="cm-variable">DistroKey</span> <span class="cm-variable">distroKey</span>, <span class="cm-variable">DataOperation</span> <span class="cm-variable">action</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">sync</span>(<span class="cm-variable">distroKey</span>, <span class="cm-variable">action</span>, <span class="cm-variable">DistroConfig</span>.<span class="cm-variable">getInstance</span>().<span class="cm-variable">getSyncDelayMillis</span>());</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">sync</span>(<span class="cm-variable">DistroKey</span> <span class="cm-variable">distroKey</span>, <span class="cm-variable">DataOperation</span> <span class="cm-variable">action</span>, <span class="cm-variable-3">long</span> <span class="cm-variable">delay</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 遍历集群其他节点，除开自身节点</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">for</span> (<span class="cm-variable">Member</span> <span class="cm-variable">each</span> : <span class="cm-variable">memberManager</span>.<span class="cm-variable">allMembersWithoutSelf</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">syncToTarget</span>(<span class="cm-variable">distroKey</span>, <span class="cm-variable">action</span>, <span class="cm-variable">each</span>.<span class="cm-variable">getAddress</span>(), <span class="cm-variable">delay</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">syncToTarget</span>(<span class="cm-variable">DistroKey</span> <span class="cm-variable">distroKey</span>, <span class="cm-variable">DataOperation</span> <span class="cm-variable">action</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">targetServer</span>, <span class="cm-variable-3">long</span> <span class="cm-variable">delay</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">DistroKey</span> <span class="cm-variable">distroKeyWithTarget</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">DistroKey</span>(<span class="cm-variable">distroKey</span>.<span class="cm-variable">getResourceKey</span>(), <span class="cm-variable">distroKey</span>.<span class="cm-variable">getResourceType</span>(),</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">targetServer</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 创建了 DistroDelayTask 任务，并且把 targetServer(即集群节点的address) 包装成 DistroKey 当作参数传入了进去</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">DistroDelayTask</span> <span class="cm-variable">distroDelayTask</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">DistroDelayTask</span>(<span class="cm-variable">distroKeyWithTarget</span>, <span class="cm-variable">action</span>, <span class="cm-variable">delay</span>);</span><br><span role="presentation"> &nbsp; <span class="cm-comment">// 往执行引擎中添加任务</span></span><br><span role="presentation"> &nbsp; <span class="cm-variable">distroTaskEngineHolder</span>.<span class="cm-variable">getDelayTaskExecuteEngine</span>().<span class="cm-variable">addTask</span>(<span class="cm-variable">distroKeyWithTarget</span>, <span class="cm-variable">distroDelayTask</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">Loggers</span>.<span class="cm-variable">DISTRO</span>.<span class="cm-variable">isDebugEnabled</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">DISTRO</span>.<span class="cm-variable">debug</span>(<span class="cm-string">"[DISTRO-SCHEDULE] {} to {}"</span>, <span class="cm-variable">distroKey</span>, <span class="cm-variable">targetServer</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到，这里最后利用</span><span class="md-pair-s" spellcheck="false"><code>distroTaskEngineHolder.getDelayTaskExecuteEngine()</code></span><span class="md-plain">去执行</span><span class="md-pair-s" spellcheck="false"><code>distroDelayTask</code></span><span class="md-plain">任务，这个地方的逻辑我们在上面就说过，实际上执行的就需要看对应的</span><span class="md-pair-s" spellcheck="false"><code>processor</code></span><span class="md-plain">，而</span><span class="md-pair-s" spellcheck="false"><code>DistroTaskEngineHolder</code></span><span class="md-plain">中的执行器就是</span><span class="md-pair-s" spellcheck="false"><code>DistroDelayTaskProcessor</code></span><span class="md-plain">，我们看看对应的处理方法</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">process</span>(<span class="cm-variable">NacosTask</span> <span class="cm-variable">task</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span>(<span class="cm-variable">task</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">DistroDelayTask</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">DistroDelayTask</span> <span class="cm-variable">distroDelayTask</span> <span class="cm-operator">=</span> (<span class="cm-variable">DistroDelayTask</span>) <span class="cm-variable">task</span>;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">DistroKey</span> <span class="cm-variable">distroKey</span> <span class="cm-operator">=</span> <span class="cm-variable">distroDelayTask</span>.<span class="cm-variable">getDistroKey</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">switch</span> (<span class="cm-variable">distroDelayTask</span>.<span class="cm-variable">getAction</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case</span> <span class="cm-variable">DELETE</span>:</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 删除任务</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">DistroSyncDeleteTask</span> <span class="cm-variable">syncDeleteTask</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">DistroSyncDeleteTask</span>(<span class="cm-variable">distroKey</span>, <span class="cm-variable">distroComponentHolder</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">distroTaskEngineHolder</span>.<span class="cm-variable">getExecuteWorkersManager</span>().<span class="cm-variable">addTask</span>(<span class="cm-variable">distroKey</span>, <span class="cm-variable">syncDeleteTask</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case</span> <span class="cm-variable">CHANGE</span>:</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case</span> <span class="cm-variable">ADD</span>:</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 改变、新增都会走这一段逻辑，封装为DistroSyncChangeTask</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">DistroSyncChangeTask</span> <span class="cm-variable">syncChangeTask</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">DistroSyncChangeTask</span>(<span class="cm-variable">distroKey</span>, <span class="cm-variable">distroComponentHolder</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">distroTaskEngineHolder</span>.<span class="cm-variable">getExecuteWorkersManager</span>().<span class="cm-variable">addTask</span>(<span class="cm-variable">distroKey</span>, <span class="cm-variable">syncChangeTask</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">default</span>:</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">false</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">这一段逻辑，不管是删除，还是改变和新增的操作，都是创建了一个任务，添加到了 distro 任务执行引擎，这个引擎和之前看的延时任务执行实现有点不一样。</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227211423702.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227211423702.png" alt="image-20251227211423702"></span></p>
<p class="md-end-block md-p"><span class="md-plain">从逻辑上找下去，我们首先需要看到</span><span class="md-pair-s" spellcheck="false"><code>DistroExecuteTaskExecuteEngine</code></span><span class="md-plain">中的</span><span class="md-pair-s" spellcheck="false"><code>addTask()</code></span><span class="md-plain">的逻辑</span></p>
<p class="md-end-block md-p"><span class="md-plain">可以发现，就是</span><strong><span class="md-plain">将</span><span class="md-pair-s" spellcheck="false"><code>task</code></span><span class="md-plain">加入到</span><span class="md-pair-s" spellcheck="false"><code>TaskExecuteWorker类型的数组中</code></span><span class="md-plain">，然后调用</span><span class="md-pair-s" spellcheck="false"><code>TaskExecuteWorker.process方法执行task</code></span></strong></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227212310543.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227212310543.png" alt="image-20251227212310543"></span></p>
<p class="md-end-block md-p"><span class="md-plain">然后我们来看看</span><span class="md-pair-s" spellcheck="false"><code>process</code></span><span class="md-plain">中的逻辑</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227214537141.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227214537141.png" alt="image-20251227214537141"></span></p>
<p class="md-end-block md-p"><strong><span class="md-plain">可以看到这里面的主要逻辑就是将</span><span class="md-pair-s" spellcheck="false"><code>task</code></span><span class="md-plain">放入到队列中。</span></strong></p>
<p class="md-end-block md-p"><strong><span class="md-plain">在延时任务执行引擎实现原理是有一个 Map tasks 任务池，很多对应的任务处理器，然后定时从任务池获取任务，执行任务。而这个 </span><span class="md-pair-s" spellcheck="false"><code>distroTaskEngineHolder实现原理使用阻塞队列 + 异步任务</code></span><span class="md-plain">的方式来实现的，所以最终这个任务肯定会被拿取并且执行</span></strong></p>
<h2 class="md-end-block md-heading"><span class="md-plain">DistroSyncChangeTask</span></h2>
<p class="md-end-block md-p"><span class="md-plain">我们回到</span><span class="md-pair-s" spellcheck="false"><code>DistroDelayTaskProcessor</code></span><span class="md-plain">中的</span><span class="md-pair-s" spellcheck="false"><code>process</code></span><span class="md-plain">逻辑，可以知道无论是</span><span class="md-pair-s" spellcheck="false"><code>ADD还是CHANGE</code></span><span class="md-plain">对应的都是封装为了</span><span class="md-pair-s" spellcheck="false"><code>DistroSyncChangeTask</code></span><span class="md-plain">任务类型，我们查看其设计，可以发现继承自</span><span class="md-pair-s" spellcheck="false"><code>AbstractDistroExecuteTask</code></span><span class="md-plain">，继续往上看其实就是实现了</span><span class="md-pair-s" spellcheck="false"><code>Runnable接口</code></span><span class="md-plain">，所以我们来看看对应的</span><span class="md-pair-s" spellcheck="false"><code>run</code></span><span class="md-plain">逻辑，因为我们是</span><span class="md-pair-s" spellcheck="false"><code>CHANGE</code></span><span class="md-plain">类型，所以我们这里也肯定去看</span><span class="md-pair-s" spellcheck="false"><code>DistroSyncChangeTask下的doExecuteWithCallback的逻辑</code></span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227220202242.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227220202242.png" alt="image-20251227220202242"></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">protected</span> <span class="cm-variable-3">void</span> <span class="cm-def">doExecuteWithCallback</span>(<span class="cm-variable">DistroCallback</span> <span class="cm-variable">callback</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">type</span> <span class="cm-operator">=</span> <span class="cm-variable">getDistroKey</span>().<span class="cm-variable">getResourceType</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 获取请求数据</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">DistroData</span> <span class="cm-variable">distroData</span> <span class="cm-operator">=</span> <span class="cm-variable">getDistroData</span>(<span class="cm-variable">type</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">distroData</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">DISTRO</span>.<span class="cm-variable">warn</span>(<span class="cm-string">"[DISTRO] {} with null data to sync, skip"</span>, <span class="cm-variable">toString</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// syncData 同步集群节点</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">getDistroComponentHolder</span>().<span class="cm-variable">findTransportAgent</span>(<span class="cm-variable">type</span>)</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  .<span class="cm-variable">syncData</span>(<span class="cm-variable">distroData</span>, <span class="cm-variable">getDistroKey</span>().<span class="cm-variable">getTargetServer</span>(), <span class="cm-variable">callback</span>);</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">此时我们应该去看</span><span class="md-pair-s" spellcheck="false"><code>syncData</code></span><span class="md-plain">的逻辑，该方法也是接口中的方法，需要在对应的实现类中查找对应的实现，这里有两个实现类</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227224407283.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227224407283.png" alt="image-20251227224407283"></span></p>
<p class="md-end-block md-p"><span class="md-plain">怎么知道是哪个呢？我们可以回头来看一下，</span><strong><span class="md-plain">调用</span><span class="md-pair-s" spellcheck="false"><code>syncData</code></span><span class="md-plain">的是</span><span class="md-pair-s" spellcheck="false"><code>findTransportAgent(type)</code></span><span class="md-plain">的返回指，这个返回值其实在之前的</span><span class="md-pair-s" spellcheck="false"><code>run</code></span><span class="md-plain">的逻辑中也有被调用，其返回类型是</span><span class="md-pair-s" spellcheck="false"><code>DistroTransportAgent</code></span><span class="md-plain">，而</span><span class="md-pair-s" spellcheck="false"><code>DistroClientTransportAgent</code></span><span class="md-plain">是</span><span class="md-pair-s" spellcheck="false"><code>DistroTransportAgent</code></span><span class="md-plain">的子类</span></strong></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227224351093.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251227224351093.png" alt="image-20251227224351093"></span></p>
<p class="md-end-block md-p"><span class="md-plain">看到这里我们知道了，</span><strong><span class="md-plain">接下来要看的内容是</span><span class="md-pair-s" spellcheck="false"><code>DistroClientTransportAgent的syncData()的逻辑</code></span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">syncData</span>(<span class="cm-variable">DistroData</span> <span class="cm-variable">data</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">targetServer</span>, <span class="cm-variable">DistroCallback</span> <span class="cm-variable">callback</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">isNoExistTarget</span>(<span class="cm-variable">targetServer</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">callback</span>.<span class="cm-variable">onSuccess</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 创建请求对象</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">DistroDataRequest</span> <span class="cm-variable">request</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">DistroDataRequest</span>(<span class="cm-variable">data</span>, <span class="cm-variable">data</span>.<span class="cm-variable">getType</span>());</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 找到集群节点</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Member</span> <span class="cm-variable">member</span> <span class="cm-operator">=</span> <span class="cm-variable">memberManager</span>.<span class="cm-variable">find</span>(<span class="cm-variable">targetServer</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">try</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 发送 rpc 异步请求</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">clusterRpcClientProxy</span>.<span class="cm-variable">asyncRequest</span>(<span class="cm-variable">member</span>, <span class="cm-variable">request</span>, <span class="cm-keyword">new</span> <span class="cm-variable">DistroRpcCallbackWrapper</span>(<span class="cm-variable">callback</span>, <span class="cm-variable">member</span>));</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">catch</span> (<span class="cm-variable">NacosException</span> <span class="cm-variable">nacosException</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">callback</span>.<span class="cm-variable">onFailed</span>(<span class="cm-variable">nacosException</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">所以看到这里我们差不多知道了整个</span><span class="md-pair-s" spellcheck="false"><code>某个节点上的服务实例修改后，如果通知到其余节点</code></span><span class="md-plain">的过程，捋一捋，</span><strong><span class="md-plain">一开始我们是从 </span><span class="md-pair-s" spellcheck="false"><code>ClientChangedEvent</code></span><span class="md-plain"> 事件开始分析，我们找到了处理这个事件的逻辑，调用了 </span><span class="md-pair-s" spellcheck="false"><code>sync</code></span><span class="md-plain"> 方法，在这个方法中会遍历其他集群节点进行数据同步，同步的逻辑如下。</span></strong></p>
<ol class="ol-list" start="">
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-plain">先是创建了 DistroDelayTask 延迟任务，放入到了延迟任务执行引擎，由 </span><span class="md-pair-s" spellcheck="false"><code>DistroDelayTaskProcessor</code></span><span class="md-plain"> 处理器来处理。</span></strong></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-plain">在 </span><span class="md-pair-s" spellcheck="false"><code>DistroDelayTaskProcessor</code></span><span class="md-plain"> 处理器，处理的逻辑中又创建了 </span><span class="md-pair-s" spellcheck="false"><code>DistroSyncChangeTask</code></span><span class="md-plain"> 线程任务，执行引擎是先调用了 </span><span class="md-pair-s" spellcheck="false"><code>AbstractDistroExecuteTask</code></span><span class="md-plain"> 父类中的 </span><span class="md-pair-s" spellcheck="false"><code>run</code></span><span class="md-plain"> 方法，在 </span><span class="md-pair-s" spellcheck="false"><code>run</code></span><span class="md-plain"> 方法中又调用了子类的</span><span class="md-pair-s" spellcheck="false"><code>doExecuteWithCallback</code></span><span class="md-plain"> 方法。</span></strong></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-pair-s" spellcheck="false"><code>doExecuteWithCallback</code></span><span class="md-plain"> 方法中会去获取最新的微服务实例列表，通过 rpc 发起异步请求进行数据同步。</span></strong></p>
</li>
</ol>
<h1 class="md-end-block md-heading"><span class="md-plain">集群其余节点处理DistroDataRequest</span></h1>
<p class="md-end-block md-p"><span class="md-plain">上述内容中，我们已经清楚了集群节点发其同步请求，那么现在我们来看看别的集群节点如何处理这个同步请求的。我们知道，对应的请求类型是</span><span class="md-pair-s" spellcheck="false"><code>DistroDataRequest</code></span><span class="md-plain">,老规矩，利用IDEA全局搜索找到对应的Handler处理器</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251228102952496.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251228102952496.png" alt="image-20251228102952496"></span></p>
<p class="md-end-block md-p"><span class="md-plain">可以发现，无论是</span><span class="md-pair-s" spellcheck="false"><code>ADD</code></span><span class="md-plain"> 还是 </span><span class="md-pair-s" spellcheck="false"><code>CHANGE</code></span><span class="md-plain"> 还是 </span><span class="md-pair-s" spellcheck="false"><code>DELETE</code></span><span class="md-plain"> 的逻辑，最终都是执行的</span><span class="md-pair-s" spellcheck="false"><code>handlerSyncData(...)</code></span><span class="md-plain">的逻辑，跟下去整个逻辑链路大概是</span><span class="md-pair-s" spellcheck="false"><code>handleSyncData()-&gt;onReceive()-&gt;processData-&gt;handlerClientSyncData()</code></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable">DistroDataResponse</span> <span class="cm-def">handleSyncData</span>(<span class="cm-variable">DistroData</span> <span class="cm-variable">distroData</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 创建response对象</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">DistroDataResponse</span> <span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">DistroDataResponse</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// onReceive方法处理请求数据distroData</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">distroProtocol</span>.<span class="cm-variable">onReceive</span>(<span class="cm-variable">distroData</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">result</span>.<span class="cm-variable">setErrorCode</span>(<span class="cm-variable">ResponseCode</span>.<span class="cm-variable">FAIL</span>.<span class="cm-variable">getCode</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">result</span>.<span class="cm-variable">setMessage</span>(<span class="cm-string">"[DISTRO-FAILED] distro data handle failed"</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">result</span>;</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">​</span><br><span class="cm-comment">/**</span><br><span role="presentation"> <span class="cm-comment">* Receive synced distro data, find processor to process.</span></span><br><span role="presentation"> <span class="cm-comment">*</span></span><br><span role="presentation"> <span class="cm-comment">* @param distroData Received data</span></span><br><span role="presentation"> <span class="cm-comment">* @return true if handle receive data successfully, otherwise false</span></span><br><span role="presentation"> <span class="cm-comment">*/</span></span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">onReceive</span>(<span class="cm-variable">DistroData</span> <span class="cm-variable">distroData</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">DISTRO</span>.<span class="cm-variable">info</span>(<span class="cm-string">"[DISTRO] Receive distro data type: {}, key: {}"</span>, <span class="cm-variable">distroData</span>.<span class="cm-variable">getType</span>(),</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">distroData</span>.<span class="cm-variable">getDistroKey</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">resourceType</span> <span class="cm-operator">=</span> <span class="cm-variable">distroData</span>.<span class="cm-variable">getDistroKey</span>().<span class="cm-variable">getResourceType</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 利用type找到对应的processor处理器</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">DistroDataProcessor</span> <span class="cm-variable">dataProcessor</span> <span class="cm-operator">=</span> <span class="cm-variable">distroComponentHolder</span>.<span class="cm-variable">findDataProcessor</span>(<span class="cm-variable">resourceType</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">dataProcessor</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">DISTRO</span>.<span class="cm-variable">warn</span>(<span class="cm-string">"[DISTRO] Can't find data process for received data {}"</span>, <span class="cm-variable">resourceType</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">false</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 调用处理器的processData方法处理请求数据</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">dataProcessor</span>.<span class="cm-variable">processData</span>(<span class="cm-variable">distroData</span>);</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>processData</code></span><span class="md-plain">也是接口中的方法，一样的有两个实现类，这里我们需要根据</span><span class="md-pair-s" spellcheck="false"><code>resourceType</code></span><span class="md-plain">来进行确认，这个</span><span class="md-pair-s" spellcheck="false"><code>resourceType</code></span><span class="md-plain">的值大家可以回过头看这一章的最开始那个地方，在</span><span class="md-pair-s" spellcheck="false"><code>syncToAllServer</code></span><span class="md-plain">中，也就是我们</span><span class="md-pair-s" spellcheck="false"><code>Event</code></span><span class="md-plain">创建和传入的地方，我们当前这个</span><span class="md-pair-s" spellcheck="false"><code>resourceType</code></span><span class="md-plain">和</span><span class="md-pair-s" spellcheck="false"><code>DistroKey</code></span><span class="md-plain">其实一开始就绑定了，那么其实这个</span><span class="md-pair-s" spellcheck="false"><code>resourceType</code></span><span class="md-plain">就是我们上面提到的常量</span><span class="md-pair-s" spellcheck="false"><code>TYPE</code></span></p>
<p class="md-end-block md-p"><span class="md-plain">然后这个地方通过</span><span class="md-pair-s" spellcheck="false"><code>findDataProcessor</code></span><span class="md-plain">查找</span><span class="md-pair-s" spellcheck="false"><code>DistroComponentHolder</code></span><span class="md-plain">中的Map</span><span class="md-pair-s" spellcheck="false"><code>成员dataProcessorMap</code></span><span class="md-plain">，以</span><span class="md-pair-s" spellcheck="false"><code>TYPE</code></span><span class="md-plain">为key的对应的value</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">final</span> <span class="cm-variable">Map</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span>, <span class="cm-variable">DistroDataProcessor</span><span class="cm-operator">&gt;</span> <span class="cm-variable">dataProcessorMap</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">HashMap</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">DistroDataProcessor</span> <span class="cm-def">findDataProcessor</span>(<span class="cm-variable-3">String</span> <span class="cm-variable">processType</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">dataProcessorMap</span>.<span class="cm-variable">get</span>(<span class="cm-variable">processType</span>);</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">到这里我们知道了存储在哪里以及对应的key，但是对应的value该如何查找呢？大家可以继续看到</span><span class="md-pair-s" spellcheck="false"><code>DistroComponentHolder</code></span><span class="md-plain">中，可以发现这个Map是</span><span class="md-pair-s" spellcheck="false"><code>private私有的</code></span><span class="md-plain">，那么就说明外部不能随便进行赋值，有且只能通过</span><span class="md-pair-s" spellcheck="false"><code>registerDataProcessor</code></span><span class="md-plain">方法进行赋值</span></p>
<p class="md-end-block md-p"><span class="md-plain">那么我们查找一下这个方法使用的地方，看看是否能查找到部分线索，可以发现就两个地方</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251228204246865.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251228204246865.png" alt="image-20251228204246865"></span></p>
<p class="md-end-block md-p"><span class="md-plain">我们可以分别看一下这两个地方中，因为我们需要其</span><span class="md-pair-s" spellcheck="false"><code>processType</code></span><span class="md-plain">为</span><span class="md-pair-s" spellcheck="false"><code>TYPE</code></span><span class="md-plain">，那么也就是对应的</span><span class="md-pair-s" spellcheck="false"><code>processType()</code></span><span class="md-plain">方法返回值为</span><span class="md-pair-s" spellcheck="false"><code>TYPE</code></span><span class="md-plain">，可以看到，符合条件的是</span><span class="md-pair-s" spellcheck="false"><code>DistroClientComponentRegistry</code></span><span class="md-plain">,另外一个的返回不满足条件(另外一个的就不贴图了，大家自己可以去看一下,他的Key是另外一个常量)</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251228204526868.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251228204526868.png" alt="image-20251228204526868"></span></p>
<p class="md-end-block md-p"><span class="md-plain">那么确定了对应的返回值类型是</span><span class="md-pair-s" spellcheck="false"><code>DistroClientComponentRegistry</code></span><span class="md-plain">,那么我们就可以看其中的</span><span class="md-pair-s" spellcheck="false"><code>processData()</code></span><span class="md-plain">方法的实现逻辑</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">processData</span>(<span class="cm-variable">DistroData</span> <span class="cm-variable">distroData</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">switch</span> (<span class="cm-variable">distroData</span>.<span class="cm-variable">getType</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case</span> <span class="cm-variable">ADD</span>:</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case</span> <span class="cm-variable">CHANGE</span>:</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 添加和改变走这个逻辑</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 这一步是将请求数据利用序列化器进行对应的反序列化的操作</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">ClientSyncData</span> <span class="cm-variable">clientSyncData</span> <span class="cm-operator">=</span> <span class="cm-variable">ApplicationUtils</span>.<span class="cm-variable">getBean</span>(<span class="cm-variable">Serializer</span>.<span class="cm-keyword">class</span>)</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  .<span class="cm-variable">deserialize</span>(<span class="cm-variable">distroData</span>.<span class="cm-variable">getContent</span>(), <span class="cm-variable">ClientSyncData</span>.<span class="cm-keyword">class</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">handlerClientSyncData</span>(<span class="cm-variable">clientSyncData</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">case</span> <span class="cm-variable">DELETE</span>:</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 删除走这个逻辑</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">deleteClientId</span> <span class="cm-operator">=</span> <span class="cm-variable">distroData</span>.<span class="cm-variable">getDistroKey</span>().<span class="cm-variable">getResourceKey</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">DISTRO</span>.<span class="cm-variable">info</span>(<span class="cm-string">"[Client-Delete] Received distro client sync data {}"</span>, <span class="cm-variable">deleteClientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">clientManager</span>.<span class="cm-variable">clientDisconnected</span>(<span class="cm-variable">deleteClientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">default</span>:</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">false</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到主要的逻辑就是</span><span class="md-pair-s" spellcheck="false"><code>handlerClientSyncData</code></span><span class="md-plain">方法，而在这个方法内的主要逻辑是</span><span class="md-pair-s" spellcheck="false"><code>upgradeClient</code></span><span class="md-plain">方法</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable-3">void</span> <span class="cm-def">handlerClientSyncData</span>(<span class="cm-variable">ClientSyncData</span> <span class="cm-variable">clientSyncData</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">DISTRO</span>.<span class="cm-variable">info</span>(<span class="cm-string">"[Client-Add] Received distro client sync data {}"</span>, <span class="cm-variable">clientSyncData</span>.<span class="cm-variable">getClientId</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">clientManager</span>.<span class="cm-variable">syncClientConnected</span>(<span class="cm-variable">clientSyncData</span>.<span class="cm-variable">getClientId</span>(), <span class="cm-variable">clientSyncData</span>.<span class="cm-variable">getAttributes</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Client</span> <span class="cm-variable">client</span> <span class="cm-operator">=</span> <span class="cm-variable">clientManager</span>.<span class="cm-variable">getClient</span>(<span class="cm-variable">clientSyncData</span>.<span class="cm-variable">getClientId</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">upgradeClient</span>(<span class="cm-variable">client</span>, <span class="cm-variable">clientSyncData</span>);</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span class="cm-comment">// 传入client即客户端ID对应的客户端对象 和 clientSyncData为对应的客户端数据</span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable-3">void</span> <span class="cm-def">upgradeClient</span>(<span class="cm-variable">Client</span> <span class="cm-variable">client</span>, <span class="cm-variable">ClientSyncData</span> <span class="cm-variable">clientSyncData</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-variable">namespaces</span> <span class="cm-operator">=</span> <span class="cm-variable">clientSyncData</span>.<span class="cm-variable">getNamespaces</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-variable">groupNames</span> <span class="cm-operator">=</span> <span class="cm-variable">clientSyncData</span>.<span class="cm-variable">getGroupNames</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-variable">serviceNames</span> <span class="cm-operator">=</span> <span class="cm-variable">clientSyncData</span>.<span class="cm-variable">getServiceNames</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable">InstancePublishInfo</span><span class="cm-operator">&gt;</span> <span class="cm-variable">instances</span> <span class="cm-operator">=</span> <span class="cm-variable">clientSyncData</span>.<span class="cm-variable">getInstancePublishInfos</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 同步数据集合</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Set</span><span class="cm-operator">&lt;</span><span class="cm-variable">Service</span><span class="cm-operator">&gt;</span> <span class="cm-variable">syncedService</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">HashSet</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 遍历传入参数中的所有服务信息</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">for</span> (<span class="cm-variable-3">int</span> <span class="cm-variable">i</span> <span class="cm-operator">=</span> <span class="cm-number">0</span>; <span class="cm-variable">i</span> <span class="cm-operator">&lt;</span> <span class="cm-variable">namespaces</span>.<span class="cm-variable">size</span>(); <span class="cm-variable">i</span><span class="cm-operator">++</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 创建对应的Service对象</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Service</span> <span class="cm-variable">service</span> <span class="cm-operator">=</span> <span class="cm-variable">Service</span>.<span class="cm-variable">newService</span>(<span class="cm-variable">namespaces</span>.<span class="cm-variable">get</span>(<span class="cm-variable">i</span>), <span class="cm-variable">groupNames</span>.<span class="cm-variable">get</span>(<span class="cm-variable">i</span>), <span class="cm-variable">serviceNames</span>.<span class="cm-variable">get</span>(<span class="cm-variable">i</span>));</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 获取内存中保存的对应的service单例对象</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Service</span> <span class="cm-variable">singleton</span> <span class="cm-operator">=</span> <span class="cm-variable">ServiceManager</span>.<span class="cm-variable">getInstance</span>().<span class="cm-variable">getSingleton</span>(<span class="cm-variable">service</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 加入到集合中，方便后续比较</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">syncedService</span>.<span class="cm-variable">add</span>(<span class="cm-variable">singleton</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">InstancePublishInfo</span> <span class="cm-variable">instancePublishInfo</span> <span class="cm-operator">=</span> <span class="cm-variable">instances</span>.<span class="cm-variable">get</span>(<span class="cm-variable">i</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 如果从client中获取的和同步数据中的内容不一样，则发布新增事件，client需要同步新数据</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">instancePublishInfo</span>.<span class="cm-variable">equals</span>(<span class="cm-variable">client</span>.<span class="cm-variable">getInstancePublishInfo</span>(<span class="cm-variable">singleton</span>))) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">client</span>.<span class="cm-variable">addServiceInstance</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">instancePublishInfo</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">new</span> <span class="cm-variable">ClientOperationEvent</span>.<span class="cm-variable">ClientRegisterServiceEvent</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">client</span>.<span class="cm-variable">getClientId</span>()));</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 如果某个服务不在同步数据中（即不在 syncedService 集合中），则从client中移除该服务实例并发布注销事件</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">for</span> (<span class="cm-variable">Service</span> <span class="cm-variable">each</span> : <span class="cm-variable">client</span>.<span class="cm-variable">getAllPublishedService</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">syncedService</span>.<span class="cm-variable">contains</span>(<span class="cm-variable">each</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">client</span>.<span class="cm-variable">removeServiceInstance</span>(<span class="cm-variable">each</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">new</span> <span class="cm-variable">ClientOperationEvent</span>.<span class="cm-variable">ClientDeregisterServiceEvent</span>(<span class="cm-variable">each</span>, <span class="cm-variable">client</span>.<span class="cm-variable">getClientId</span>()));</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">在这里会解析传过来的参数，最后对于新增的数据，还是发布了 </span><span class="md-pair-s" spellcheck="false"><code>ClientRegisterServiceEvent</code></span><span class="md-plain"> 事件，这个事件我们在讲解注册源码的时候已经分析过了，这一块注册逻辑就结束了。</span></p>
<p class="md-end-block md-p"><span class="md-plain">我们会过来看看</span><span class="md-pair-s" spellcheck="false"><code>processData</code></span><span class="md-plain">中对于删除操作的逻辑处理，可以发现核心在于</span><span class="md-pair-s" spellcheck="false"><code>clientManager.clientDisconnected(deleteClientId);</code></span><span class="md-plain">一样得需要找对应的实现类</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" spellcheck="false"><span role="presentation">这个实现类怎么找到的呢，这个有点难度。</span><br><span role="presentation">从代码的角度分析的话，DistroClientComponentRegistry中的这个clientManager，本质上是来源于DistroClientComponentRegistry中的clientManager，而DistroClientComponentRegistry是个Bean对象</span><br><span role="presentation">那么我们来看看clientManager，他对应的接口类型是ClientManagerDelegate，这个ClientManagerDelegate中也同样实现了clientDisconnected这个方法</span><br><span role="presentation">ClientManagerDelegate 持有三种不同的 ClientManager 实现：</span><br><span role="presentation">ConnectionBasedClientManager：基于连接的客户端管理器</span><br><span role="presentation">EphemeralIpPortClientManager：临时IP端口客户端管理器</span><br><span role="presentation">PersistentIpPortClientManager：持久IP端口客户端管理器</span><br><span role="presentation">​</span><br><span role="presentation">而ClientManagerDelegate中对于clientDisconnected的实现如下</span><br><span role="presentation">@Override</span><br><span role="presentation">public boolean clientDisconnected(String clientId) {</span><br><span role="presentation"> &nbsp;  return getClientManagerById(clientId).clientDisconnected(clientId);</span><br><span role="presentation">}</span><br><span role="presentation">与这个方法相关联的方法如下</span><br><span role="presentation">private ClientManager getClientManagerById(String clientId) {</span><br><span role="presentation"> &nbsp;  if (isConnectionBasedClient(clientId)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  return connectionBasedClientManager;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp;  return clientId.endsWith(ClientConstants.PERSISTENT_SUFFIX) ? persistentIpPortClientManager : ephemeralIpPortClientManager;</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">private boolean isConnectionBasedClient(String clientId) {</span><br><span role="presentation"> &nbsp; return !clientId.contains(IpPortBasedClient.ID_DELIMITER);</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">也就是说，我们确定这个clientDisconnected的实现类，最终是要依靠clientId，而本质上这个clientId是客户端注册的时候生成的，比较难追踪，而且你会发现很多的相关的信息都会回到这个clientId上来</span><br><span role="presentation">​</span><br><span role="presentation">所以我们这里其实最好的方法是debug的形式，或者我们也可以从理解的角度来看，一般的客户端都是临时IP端口，所以默认为EphemeralIpPortClientManager的实现</span></pre>
<p class="md-end-block md-p"><strong><span class="md-plain">这个方法我们看 </span><span class="md-pair-s" spellcheck="false"><code>EphemeralIpPortClientManager</code></span><span class="md-plain"> 临时实例的实现类</span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" spellcheck="false"><span role="presentation">@Override</span><br><span role="presentation">public boolean clientDisconnected(String clientId) {</span><br><span role="presentation"> &nbsp;  Loggers.SRV_LOG.info("Client connection {} disconnect, remove instances and subscribers", clientId);</span><br><span role="presentation"> &nbsp;  // 移除客户端信息</span><br><span role="presentation"> &nbsp;  IpPortBasedClient client = clients.remove(clientId);</span><br><span role="presentation"> &nbsp;  if (null == client) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  return true;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp;  // 发布客户端注销事件</span><br><span role="presentation"> &nbsp;  NotifyCenter.publishEvent(new ClientEvent.ClientDisconnectEvent(client));</span><br><span role="presentation"> &nbsp;  client.release();</span><br><span role="presentation"> &nbsp;  return true;</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>ClientDisconnectEvent</code></span><span class="md-plain"> 发布客户端注销事件，会在多个地方处理该事件。</span></p>
<p class="md-end-block md-p"><strong><span class="md-plain">第一个是</span><span class="md-pair-s" spellcheck="false"><code>DistroClientDataProcessor</code></span><span class="md-plain">，客户端注销后会同步集群节点。第二个是</span><span class="md-pair-s" spellcheck="false"><code>ClientServiceIndexesManager</code></span><span class="md-plain">，移除本身节点该 client 的数据。</span></strong></p>
<p class="md-end-block md-p"><span class="md-plain">客户端注销同步集群节点没什么好说的了，和注册同步逻辑类似，我们来看下 </span><span class="md-pair-s" spellcheck="false"><code>ClientServiceIndexesManager</code></span><span class="md-plain"> 类处理注销事件，代码如下：</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" spellcheck="false"><span role="presentation">​</span><br><span role="presentation">@Override</span><br><span role="presentation">public void onEvent(Event event) {</span><br><span role="presentation"> &nbsp;  if (event instanceof ClientEvent.ClientDisconnectEvent) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  // 处理客户端注销</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  handleClientDisconnect((ClientEvent.ClientDisconnectEvent) event);</span><br><span role="presentation"> &nbsp;  } else if (event instanceof ClientOperationEvent) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  handleClientOperation((ClientOperationEvent) event);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">private void handleClientDisconnect(ClientEvent.ClientDisconnectEvent event) {</span><br><span role="presentation"> &nbsp;  Client client = event.getClient();</span><br><span role="presentation"> &nbsp;  for (Service each : client.getAllSubscribeService()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  // 移除订阅者信息 其实就是操作subscriberIndexes</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  removeSubscriberIndexes(each, client.getClientId());</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp;  for (Service each : client.getAllPublishedService()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  // 移除注册表信息 其实就是操作publisherIndexes</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  removePublisherIndexes(each, client.getClientId());</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span></pre>]]></description>
    <pubDate>Mon, 29 Dec 2025 21:47:46 +0800</pubDate>
    <dc:creator>ZealSinger</dc:creator>
    <guid>http://8.154.40.120:24180/?post=52</guid>
</item>
<item>
    <title>Nacos源码学习计划-Day22-Nacos2.x-服务变动如何通知订阅客户端</title>
    <link>http://8.154.40.120:24180/?post=51</link>
    <description><![CDATA[<h1 class="md-end-block md-heading md-focus"><span class="md-plain md-expand">服务变更如何通知订阅服务的客户端</span></h1>
<p class="md-end-block md-p"><span class="md-plain">我们在</span><span class="md-meta-i-c  md-link"><a href="/?post=47" target="_blank" rel="noopener"><span class="md-plain">Nacos源码学习计划-Day20-Nacos2.x-服务端处理客户端gRPC注册请求 </span></a></span><span class="md-plain">的最后面有看到，服务端处理gRPC的注册请求的最后，即</span><span class="md-pair-s" spellcheck="false"><code>addPublisherIndexes()</code></span><span class="md-plain">的最后一行，可以看到是发布了一个</span><span class="md-pair-s" spellcheck="false"><code>ServiceChangedEvent</code></span><span class="md-plain">服务变更的事件</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable-3">void</span> <span class="cm-def">addPublisherIndexes</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 把 clientId 写入注册表</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">publisherIndexes</span>.<span class="cm-variable">computeIfAbsent</span>(<span class="cm-variable">service</span>, (<span class="cm-variable">key</span>) <span class="cm-operator">-&gt;</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashSet</span><span class="cm-operator">&lt;&gt;</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">publisherIndexes</span>.<span class="cm-variable">get</span>(<span class="cm-variable">service</span>).<span class="cm-variable">add</span>(<span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 发布服务改变事件</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">ServiceEvent</span>.<span class="cm-variable">ServiceChangedEvent</span>(<span class="cm-variable">service</span>, <span class="cm-atom">true</span>));</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">今天就围绕这个来看看，服务变更后是如何通知对应的订阅的客户端的</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">初看ServiceChangedEvent</span></h2>
<p class="md-end-block md-p"><span class="md-plain">老规矩，对于Event事件处理，优先需要找到对应的handler处理器，还是利用IDEA的全局搜索能轻松找到</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251224142710962.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251224142710962.png" alt="image-20251224142710962"></span></p>
<p class="md-end-block md-p"><span class="md-plain">看到这个地方，大家可以发现，</span><strong><span class="md-plain">这个</span><span class="md-pair-s" spellcheck="false"><code>if</code></span><span class="md-plain">部分的逻辑其实就是将对应的</span><span class="md-pair-s" spellcheck="false"><code>ServiceChangedEvent</code></span><span class="md-plain">封装成为</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTask类型</code></span><span class="md-plain">，然后调用</span><span class="md-pair-s" spellcheck="false"><code>delayTaskEngine.addTask()</code></span><span class="md-plain">方法，将其放入到任务队列中去执行</span></strong></p>
<p class="md-end-block md-p"><span class="md-plain">如果有阅读了上一节内容的UU就会发现，</span><strong><span class="md-plain">在这里Nacos使用这个的 </span><span class="md-pair-s" spellcheck="false"><code>delayTaskEngine.addTask()</code></span><span class="md-plain">,在上一节的最后，也就是</span><span class="md-pair-s" spellcheck="false"><code>ClientSubscribeServiceEvent客户端订阅服务事件</code></span><span class="md-plain">的处理中也是利用这个，这个其实是Nacos2.X中很重要的延迟任务处理引擎</span></strong></p>
<p class="md-end-block md-p"><span class="md-plain">所以接下来，我们来好好分析一下这个玩意儿</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">PushDelayTaskExecuteEngine</span></h2>
<p class="md-end-block md-p"><span class="md-plain">延迟任务执行引擎，顾名思义是执行延迟任务，可以往执行引擎中添加任务，然后该任务会被延时执行。</span></p>
<p class="md-end-block md-p"><span class="md-plain">这个</span><strong><span class="md-pair-s" spellcheck="false"><code>delayTaskEngine</code></span><span class="md-plain">的数据类型是</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTaskExecuteEngine</code></span></strong><span class="md-plain">,我们可以先来查看一下他的相关的继承和实现关系</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251224144945653.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251224144945653.png" alt="image-20251224144945653"></span></p>
<p class="md-end-block md-p"><span class="md-plain">既然我们知道了最终会将任务放入到一个任务队列中，那么我们尝试找找从队列中取任务的逻辑</span></p>
<p class="md-end-block md-p"><span class="md-plain">但是实际搜索会发现，好像并没有这种一个一个拿的相关逻辑</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251224155809825.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251224155809825.png" alt="image-20251224155809825"></span></p>
<p class="md-end-block md-p"><span class="md-plain">这个时候我们再回来看看整个</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTaskExecuteEngine</code></span><span class="md-plain">的代码，通过方法命名和整个组织的结构来看，</span><strong><span class="md-plain">可以猜测到，整个逻辑任务的实现可能依靠的是</span><span class="md-pair-s" spellcheck="false"><code>processTasks()</code></span><span class="md-plain">这个方法，但是整个链路逻辑还是不是很明了，从代码来看，我们需要网上再去看其父类是如何实现的</span></strong></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251224160527597.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251224160527597.png" alt="image-20251224160527597"></span></p>
<p class="md-end-block md-p"><span class="md-plain">我们来看其父类</span><span class="md-pair-s" spellcheck="false"><code>NacosDelayTaskExecuteEngine</code></span><span class="md-plain">的逻辑</span></p>
<p class="md-end-block md-p"><span class="md-plain">可以从父类的构造方法中看到，构建的时候会利用内部的线程池成员开启</span><span class="md-pair-s" spellcheck="false"><code>ProcessRunnable 线程任务</code></span><span class="md-plain">，而这个线程任务的底层逻辑就是执行</span><span class="md-pair-s" spellcheck="false"><code>processTasks()</code></span><span class="md-plain">方法</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251224163346630.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251224163346630.png" alt="image-20251224163346630"></span></p>
<p class="md-end-block md-p"><span class="md-plain">这个方法在子类中是有重写的，但是我们去看子类中的实现，</span><strong><span class="md-plain">就是加了一定的判断后再调用父类中的</span><span class="md-pair-s" spellcheck="false"><code>processTasks方法</code></span><span class="md-plain">，所以我们直接来看父类中的实现即可</span></strong></p>
<p class="md-end-block md-p"><span class="md-plain">可以看到，</span><strong><span class="md-plain">首先从tasks中获取到所有的任务key进行遍历，通过 taskKey 获取到具体的任务之后，再通过 taskKey 获取对应的处理器，最终调用处理器(通过</span><span class="md-pair-s" spellcheck="false"><code>getProcessor()方法获取</code></span><span class="md-plain">)的</span><span class="md-pair-s" spellcheck="false"><code>process()</code></span><span class="md-plain">处理方法</span></strong></p>
<p class="md-end-block md-p"><strong><span class="md-plain">从</span><span class="md-pair-s" spellcheck="false"><code>getProcessor()</code></span><span class="md-plain">的逻辑可以看到，如果给对应的</span><span class="md-pair-s" spellcheck="false"><code>key</code></span><span class="md-plain">即特定的</span><span class="md-pair-s" spellcheck="false"><code>task</code></span><span class="md-plain">设置了特定的</span><span class="md-pair-s" spellcheck="false"><code>processor处理器</code></span><span class="md-plain">，则会保存在</span><span class="md-pair-s" spellcheck="false"><code>taskProcessors</code></span><span class="md-plain">这个Map中，如果没有则使用默认的处理器，在顶级抽象接口中为</span><span class="md-pair-s" spellcheck="false"><code>NacosTaskProcessor</code></span><span class="md-plain">，而</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTaskExecuteEngine</code></span><span class="md-plain">的构造方法中调用了set方法将这个处理器设置为了</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTaskProcessor</code></span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation">​</span><br><span class="cm-comment">// 任务池</span><br><span role="presentation"><span class="cm-keyword">protected</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">Object</span>, <span class="cm-variable">AbstractDelayTask</span><span class="cm-operator">&gt;</span> <span class="cm-variable">tasks</span>;</span><br><span role="presentation">​</span><br><span class="cm-comment">// 这个不是NacosDelayTaskExecuteEngine的，而是来自顶级抽象类AbstractNacosTaskExecuteEngine  存储了特定的task-processor处理器的映射关系</span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">Object</span>, <span class="cm-variable">NacosTaskProcessor</span><span class="cm-operator">&gt;</span> <span class="cm-variable">taskProcessors</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation">​</span><br><span class="cm-comment">/**</span><br><span role="presentation"> <span class="cm-comment">* process tasks in execute engine.</span></span><br><span role="presentation"> <span class="cm-comment">*/</span></span><br><span role="presentation"><span class="cm-keyword">protected</span> <span class="cm-variable-3">void</span> <span class="cm-def">processTasks</span>() {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 获取全部的任务，进行遍历</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Collection</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">Object</span><span class="cm-operator">&gt;</span> <span class="cm-variable">keys</span> <span class="cm-operator">=</span> <span class="cm-variable">getAllTaskKeys</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">for</span> (<span class="cm-variable-3">Object</span> <span class="cm-variable">taskKey</span> : <span class="cm-variable">keys</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 通过任务 key，获取具体的任务，并且从任务池中移除掉</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">AbstractDelayTask</span> <span class="cm-variable">task</span> <span class="cm-operator">=</span> <span class="cm-variable">removeTask</span>(<span class="cm-variable">taskKey</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">task</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">continue</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 通过任务 key 获取对应的处理器</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">NacosTaskProcessor</span> <span class="cm-variable">processor</span> <span class="cm-operator">=</span> <span class="cm-variable">getProcessor</span>(<span class="cm-variable">taskKey</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">processor</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">getEngineLog</span>().<span class="cm-variable">error</span>(<span class="cm-string">"processor not found for task, so discarded. "</span> <span class="cm-operator">+</span> <span class="cm-variable">task</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">continue</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">try</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 调用处理器的处理方法</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">processor</span>.<span class="cm-variable">process</span>(<span class="cm-variable">task</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">retryFailedTask</span>(<span class="cm-variable">taskKey</span>, <span class="cm-variable">task</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  } <span class="cm-keyword">catch</span> (<span class="cm-variable">Throwable</span> <span class="cm-variable">e</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">getEngineLog</span>().<span class="cm-variable">error</span>(<span class="cm-string">"Nacos task execute error "</span>, <span class="cm-variable">e</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">retryFailedTask</span>(<span class="cm-variable">taskKey</span>, <span class="cm-variable">task</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">​</span><br><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">NacosTaskProcessor</span> <span class="cm-def">getProcessor</span>(<span class="cm-variable-3">Object</span> <span class="cm-variable">key</span>) {</span><br><span role="presentation"> &nbsp;<span class="cm-comment">// 如果处理器中没有对应的 key，那么就返回默认的任务处理器</span></span><br><span role="presentation"> &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">taskProcessors</span>.<span class="cm-variable">containsKey</span>(<span class="cm-variable">key</span>) <span class="cm-operator">?</span> <span class="cm-variable">taskProcessors</span>.<span class="cm-variable">get</span>(<span class="cm-variable">key</span>) : <span class="cm-variable">defaultTaskProcessor</span>;</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">而我们整个任务的存储结构</span><span class="md-pair-s" spellcheck="false"><code>tasks</code></span><span class="md-plain">这个map中的元素，就是来自于我们上面类似</span><span class="md-pair-s" spellcheck="false"><code>ServiceChangedEvent</code></span><span class="md-plain">的各种事件处理代码中，会通过</span><span class="md-pair-s" spellcheck="false"><code>addTask()</code></span><span class="md-plain">方法放入到</span><span class="md-pair-s" spellcheck="false"><code>tasks</code></span><span class="md-plain">中从而完成任务的放入</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">addTask</span>(<span class="cm-variable-3">Object</span> <span class="cm-variable">key</span>, <span class="cm-variable">AbstractDelayTask</span> <span class="cm-variable">newTask</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">lock</span>.<span class="cm-variable">lock</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">try</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 往任务池添加任务</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">AbstractDelayTask</span> <span class="cm-variable">existTask</span> <span class="cm-operator">=</span> <span class="cm-variable">tasks</span>.<span class="cm-variable">get</span>(<span class="cm-variable">key</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">!=</span> <span class="cm-variable">existTask</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">newTask</span>.<span class="cm-variable">merge</span>(<span class="cm-variable">existTask</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">tasks</span>.<span class="cm-variable">put</span>(<span class="cm-variable">key</span>, <span class="cm-variable">newTask</span>);</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">finally</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">lock</span>.<span class="cm-variable">unlock</span>();</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span></pre>
<h2 class="md-end-block md-heading"><span class="md-plain">再看ServiceChangedEvent</span></h2>
<p class="md-end-block md-p"><span class="md-plain">OK，分析完了这个延迟执行引擎，我们继续回到</span><span class="md-pair-s" spellcheck="false"><code>ServiceChangedEvent</code></span><span class="md-plain">的处理逻辑中来</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">onEvent</span>(<span class="cm-variable">Event</span> <span class="cm-variable">event</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">upgradeJudgement</span>.<span class="cm-variable">isUseGrpcFeatures</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">event</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">ServiceEvent</span>.<span class="cm-variable">ServiceChangedEvent</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 如果服务变动，会通知该 service 所有的订阅者，更新本地缓存</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">ServiceEvent</span>.<span class="cm-variable">ServiceChangedEvent</span> <span class="cm-variable">serviceChangedEvent</span> <span class="cm-operator">=</span> (<span class="cm-variable">ServiceEvent</span>.<span class="cm-variable">ServiceChangedEvent</span>) <span class="cm-variable">event</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Service</span> <span class="cm-variable">service</span> <span class="cm-operator">=</span> <span class="cm-variable">serviceChangedEvent</span>.<span class="cm-variable">getService</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">delayTaskEngine</span>.<span class="cm-variable">addTask</span>(<span class="cm-variable">service</span>, <span class="cm-keyword">new</span> <span class="cm-variable">PushDelayTask</span>(<span class="cm-variable">service</span>, <span class="cm-variable">PushConfig</span>.<span class="cm-variable">getInstance</span>().<span class="cm-variable">getPushTaskDelay</span>()));</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">else</span> <span class="cm-keyword">if</span> (<span class="cm-variable">event</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">ServiceEvent</span>.<span class="cm-variable">ServiceSubscribedEvent</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 如果服务被一个客户端订阅，则只推送该客户端</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">ServiceEvent</span>.<span class="cm-variable">ServiceSubscribedEvent</span> <span class="cm-variable">subscribedEvent</span> <span class="cm-operator">=</span> (<span class="cm-variable">ServiceEvent</span>.<span class="cm-variable">ServiceSubscribedEvent</span>) <span class="cm-variable">event</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Service</span> <span class="cm-variable">service</span> <span class="cm-operator">=</span> <span class="cm-variable">subscribedEvent</span>.<span class="cm-variable">getService</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">delayTaskEngine</span>.<span class="cm-variable">addTask</span>(<span class="cm-variable">service</span>, <span class="cm-keyword">new</span> <span class="cm-variable">PushDelayTask</span>(<span class="cm-variable">service</span>, <span class="cm-variable">PushConfig</span>.<span class="cm-variable">getInstance</span>().<span class="cm-variable">getPushTaskDelay</span>(),</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">subscribedEvent</span>.<span class="cm-variable">getClientId</span>()));</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">我们知道了，</span><strong><span class="md-plain">这个</span><span class="md-pair-s" spellcheck="false"><code>入参event</code></span><span class="md-plain">会被封装为一个</span><span class="md-pair-s" spellcheck="false"><code>Service</code></span><span class="md-plain">对象，并且作为</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTask</code></span><span class="md-plain">最终被</span><span class="md-pair-s" spellcheck="false"><code>delayTaskEngine</code></span><span class="md-plain">中的</span><span class="md-pair-s" spellcheck="false"><code>defaultTaskProcessor</code></span><span class="md-plain">的</span><span class="md-pair-s" spellcheck="false"><code>process()</code></span><span class="md-plain">方法处理</span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" spellcheck="false"><span role="presentation">因为没有触发addProcessor()方法，所以不会针对这个event设置特定的processor，所以这里肯定是直接使用defaultTaskProcessor</span></pre>
<p class="md-end-block md-p"><span class="md-plain">因为</span><span class="md-pair-s" spellcheck="false"><code>delayTaskEngine</code></span><span class="md-plain">中的</span><span class="md-pair-s" spellcheck="false"><code>defaultTaskProcessor</code></span><span class="md-plain">的类型是</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTaskProcessor</code></span><span class="md-plain">，所以我们直接来看这个处理器的</span><span class="md-pair-s" spellcheck="false"><code>process()</code></span><span class="md-plain">方法</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">process</span>(<span class="cm-variable">NacosTask</span> <span class="cm-variable">task</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 任务类型转换</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">PushDelayTask</span> <span class="cm-variable">pushDelayTask</span> <span class="cm-operator">=</span> (<span class="cm-variable">PushDelayTask</span>) <span class="cm-variable">task</span>;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Service</span> <span class="cm-variable">service</span> <span class="cm-operator">=</span> <span class="cm-variable">pushDelayTask</span>.<span class="cm-variable">getService</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 提交 PushExecuteTask 线程任务</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">NamingExecuteTaskDispatcher</span>.<span class="cm-variable">getInstance</span>()</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  .<span class="cm-variable">dispatchAndExecuteTask</span>(<span class="cm-variable">service</span>, <span class="cm-keyword">new</span> <span class="cm-variable">PushExecuteTask</span>(<span class="cm-variable">service</span>, <span class="cm-variable">executeEngine</span>, <span class="cm-variable">pushDelayTask</span>));</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到，</span><strong><span class="md-plain">这个</span><span class="md-pair-s" spellcheck="false"><code>最初的入参event转换成了PushDelayTask后又被转化为了PushExecuteTask</code></span><span class="md-plain">，这个就是我们熟知的任务类型了，那么接下来我们可以直接去看他对应的</span><span class="md-pair-s" spellcheck="false"><code>run()</code></span><span class="md-plain">方法</span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">run</span>() {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">try</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 从注册表获取当前 service 最新的实例列表数据</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">PushDataWrapper</span> <span class="cm-variable">wrapper</span> <span class="cm-operator">=</span> <span class="cm-variable">generatePushData</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">ClientManager</span> <span class="cm-variable">clientManager</span> <span class="cm-operator">=</span> <span class="cm-variable">delayTaskEngine</span>.<span class="cm-variable">getClientManager</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 获取客户端id，也就是 rpc 客户端连接 id</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">for</span> (<span class="cm-variable-3">String</span> <span class="cm-variable">each</span> : <span class="cm-variable">getTargetClientIds</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 获取客户端，通知服务变动</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Client</span> <span class="cm-variable">client</span> <span class="cm-operator">=</span> <span class="cm-variable">clientManager</span>.<span class="cm-variable">getClient</span>(<span class="cm-variable">each</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">client</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// means this client has disconnect</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">continue</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 获取客户端的订阅者</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Subscriber</span> <span class="cm-variable">subscriber</span> <span class="cm-operator">=</span> <span class="cm-variable">client</span>.<span class="cm-variable">getSubscriber</span>(<span class="cm-variable">service</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 回调客户端</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">delayTaskEngine</span>.<span class="cm-variable">getPushExecutor</span>().<span class="cm-variable">doPushWithCallback</span>(<span class="cm-variable">each</span>, <span class="cm-variable">subscriber</span>, <span class="cm-variable">wrapper</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">new</span> <span class="cm-variable">NamingPushCallback</span>(<span class="cm-variable">each</span>, <span class="cm-variable">subscriber</span>, <span class="cm-variable">wrapper</span>.<span class="cm-variable">getOriginalData</span>(), <span class="cm-variable">delayTask</span>.<span class="cm-variable">isPushToAll</span>()));</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">catch</span> (<span class="cm-variable">Exception</span> <span class="cm-variable">e</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">PUSH</span>.<span class="cm-variable">error</span>(<span class="cm-string">"Push task for service"</span> <span class="cm-operator">+</span> <span class="cm-variable">service</span>.<span class="cm-variable">getGroupedServiceName</span>() <span class="cm-operator">+</span> <span class="cm-string">" execute failed "</span>, <span class="cm-variable">e</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">delayTaskEngine</span>.<span class="cm-variable">addTask</span>(<span class="cm-variable">service</span>, <span class="cm-keyword">new</span> <span class="cm-variable">PushDelayTask</span>(<span class="cm-variable">service</span>, <span class="cm-number">1000L</span>));</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span class="cm-comment">// 通过Service的信息(命名空间，group，cluster集群信息等等)和元数据（是否临时实例，保护阈值，路由选择器等等）得到PushDataWrapper</span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable">PushDataWrapper</span> <span class="cm-def">generatePushData</span>() {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">ServiceInfo</span> <span class="cm-variable">serviceInfo</span> <span class="cm-operator">=</span> <span class="cm-variable">delayTaskEngine</span>.<span class="cm-variable">getServiceStorage</span>().<span class="cm-variable">getPushData</span>(<span class="cm-variable">service</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">ServiceMetadata</span> <span class="cm-variable">serviceMetadata</span> <span class="cm-operator">=</span> <span class="cm-variable">delayTaskEngine</span>.<span class="cm-variable">getMetadataManager</span>().<span class="cm-variable">getServiceMetadata</span>(<span class="cm-variable">service</span>).<span class="cm-variable">orElse</span>(<span class="cm-atom">null</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">PushDataWrapper</span>(<span class="cm-variable">serviceMetadata</span>, <span class="cm-variable">serviceInfo</span>);</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable">Collection</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-def">getTargetClientIds</span>() {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 通过 pushToAll 这个参数来控制是否推送给全部的 client</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 如果为 true 推送 service 全部的 client，如果为 false，需要指定 targetClients 参数，只推送该参数里面的 client</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">delayTask</span>.<span class="cm-variable">isPushToAll</span>() <span class="cm-operator">?</span> <span class="cm-variable">delayTaskEngine</span>.<span class="cm-variable">getIndexesManager</span>().<span class="cm-variable">getAllClientsSubscribeService</span>(<span class="cm-variable">service</span>)</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  : <span class="cm-variable">delayTask</span>.<span class="cm-variable">getTargetClients</span>();</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">Collection</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-def">getAllClientsSubscribeService</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 从订阅表获取 clientId</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">subscriberIndexes</span>.<span class="cm-variable">containsKey</span>(<span class="cm-variable">service</span>) <span class="cm-operator">?</span> <span class="cm-variable">subscriberIndexes</span>.<span class="cm-variable">get</span>(<span class="cm-variable">service</span>) : <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashSet</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">这里补充一下，对于</span><span class="md-pair-s" spellcheck="false"><code>isPushToAll()</code></span><span class="md-plain">底层就是判断成员</span><span class="md-pair-s" spellcheck="false"><code>pushToAll为true还是false</code></span><span class="md-plain">，这个成员的数值取决于任务类型，我们知道在最外层的时候，会通过</span><span class="md-pair-s" spellcheck="false"><code>switch-case</code></span><span class="md-plain">区分</span><span class="md-pair-s" spellcheck="false"><code>event</code></span><span class="md-plain">的类型从而包装成不同类型的</span><span class="md-pair-s" spellcheck="false"><code>task</code></span><span class="md-plain">，虽然都是包装成为</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTask</code></span><span class="md-plain">,但是用到的构造方法不同，所以在初始化的时候这个</span><span class="md-pair-s" spellcheck="false"><code>pushToAll</code></span><span class="md-plain">的值就不一样</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable">Set</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-variable">targetClients</span>;</span><br><span role="presentation">​</span><br><span class="cm-comment">//  服务变动事件，所使用的构造方法,可以看到pushToAll设置为true，就是要通知给所有的订阅了该服务的客户端</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">PushDelayTask</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable-3">long</span> <span class="cm-variable">delay</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">this</span>.<span class="cm-variable">service</span> <span class="cm-operator">=</span> <span class="cm-variable">service</span>;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">pushToAll</span> <span class="cm-operator">=</span> <span class="cm-atom">true</span>;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">targetClients</span> <span class="cm-operator">=</span> <span class="cm-atom">null</span>;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">setTaskInterval</span>(<span class="cm-variable">delay</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">setLastProcessTime</span>(<span class="cm-variable">System</span>.<span class="cm-variable">currentTimeMillis</span>());</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span class="cm-comment">// 服务订阅事件使用的构造方法</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">PushDelayTask</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable-3">long</span> <span class="cm-variable">delay</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">targetClient</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">this</span>.<span class="cm-variable">service</span> <span class="cm-operator">=</span> <span class="cm-variable">service</span>;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">this</span>.<span class="cm-variable">pushToAll</span> <span class="cm-operator">=</span> <span class="cm-atom">false</span>;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">this</span>.<span class="cm-variable">targetClients</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">HashSet</span><span class="cm-operator">&lt;&gt;</span>(<span class="cm-number">1</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 把 clientId 添加到 targetClients 当中，这个 clientId 就是发起服务订阅的客户端ID</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">this</span>.<span class="cm-variable">targetClients</span>.<span class="cm-variable">add</span>(<span class="cm-variable">targetClient</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">setTaskInterval</span>(<span class="cm-variable">delay</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">setLastProcessTime</span>(<span class="cm-variable">System</span>.<span class="cm-variable">currentTimeMillis</span>());</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">我们回到</span><span class="md-pair-s" spellcheck="false"><code>run</code></span><span class="md-plain">中的逻辑，在最后通过</span><span class="md-pair-s" spellcheck="false"><code>doPushWithCallback</code></span><span class="md-plain">这个方式发送消息给对应的client，这个方法是来自于顶层接口，有三个实现类，因为我们之前聊到过Nacos2.X都是用的Rpc的形式了，所以直接去看Rpc版的实现</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251226163801378.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251226163801378.png" alt="image-20251226163801378"></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">doPushWithCallback</span>(<span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span>, <span class="cm-variable">Subscriber</span> <span class="cm-variable">subscriber</span>, <span class="cm-variable">PushDataWrapper</span> <span class="cm-variable">data</span>, <span class="cm-variable">PushCallBack</span> <span class="cm-variable">callBack</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 这里构建的是 NotifySubscriberRequest 类型请求参数</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">pushService</span>.<span class="cm-variable">pushWithCallback</span>(<span class="cm-variable">clientId</span>, <span class="cm-variable">NotifySubscriberRequest</span>.<span class="cm-variable">buildNotifySubscriberRequest</span>(<span class="cm-variable">getServiceInfo</span>(<span class="cm-variable">data</span>, <span class="cm-variable">subscriber</span>)),</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">callBack</span>, <span class="cm-variable">GlobalExecutor</span>.<span class="cm-variable">getCallbackExecutor</span>());</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到，这里发送的请求是</span><span class="md-pair-s" spellcheck="false"><code>NotifySubscriberRequest</code></span><span class="md-plain">，那么接下来我们可以去客户端查找这个请求的处理器</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-keyword">class</span> <span class="cm-def">NamingPushRequestHandler</span> <span class="cm-keyword">implements</span> <span class="cm-variable">ServerRequestHandler</span> {</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">private</span> <span class="cm-keyword">final</span> <span class="cm-variable">ServiceInfoHolder</span> <span class="cm-variable">serviceInfoHolder</span>;</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">public</span> <span class="cm-variable">NamingPushRequestHandler</span>(<span class="cm-variable">ServiceInfoHolder</span> <span class="cm-variable">serviceInfoHolder</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">this</span>.<span class="cm-variable">serviceInfoHolder</span> <span class="cm-operator">=</span> <span class="cm-variable">serviceInfoHolder</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-meta">@Override</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">public</span> <span class="cm-variable">Response</span> <span class="cm-variable">requestReply</span>(<span class="cm-variable">Request</span> <span class="cm-variable">request</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">request</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">NotifySubscriberRequest</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 类型转换</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">NotifySubscriberRequest</span> <span class="cm-variable">notifyResponse</span> <span class="cm-operator">=</span> (<span class="cm-variable">NotifySubscriberRequest</span>) <span class="cm-variable">request</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 更新本地缓存数据</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">serviceInfoHolder</span>.<span class="cm-variable">processServiceInfo</span>(<span class="cm-variable">notifyResponse</span>.<span class="cm-variable">getServiceInfo</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">NotifySubscriberResponse</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">null</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>serviceInfoHolder.processServiceInfo</code></span><span class="md-plain"> 更新本地缓存的方法，我们在上一篇文章也见过了，也就是更新 </span><span class="md-pair-s" spellcheck="false"><code>serviceInfoMap</code></span><span class="md-plain"> 中的数据即可。</span></p>
<h1 class="md-end-block md-heading"><span class="md-plain">总结</span></h1>
<ol class="ol-list">
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-plain">事件触发</span></strong><span class="md-plain">：服务端处理完服务注册等操作后，会发布</span><span class="md-pair-s" spellcheck="false"><code>ServiceChangedEvent</code></span><span class="md-plain">（服务变更事件）或</span><span class="md-pair-s" spellcheck="false"><code>ServiceSubscribedEvent</code></span><span class="md-plain">（服务订阅事件）。</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-plain">任务封装</span></strong><span class="md-plain">：事件处理器将事件封装为</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTask</code></span><span class="md-plain">，通过</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTaskExecuteEngine</code></span><span class="md-plain">（延迟任务执行引擎）加入任务队列。其中，服务变更事件对应的任务会设置</span><span class="md-pair-s" spellcheck="false"><code>pushToAll=true</code></span><span class="md-plain">（通知所有订阅客户端），服务订阅事件则</span><span class="md-pair-s" spellcheck="false"><code>pushToAll=false</code></span><span class="md-plain">且后续操作指定目标客户端 ID。</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-plain">延迟任务执行</span></strong><span class="md-plain">：</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTaskExecuteEngine</code></span><span class="md-plain">基于父类</span><span class="md-pair-s" spellcheck="false"><code>NacosDelayTaskExecuteEngine</code></span><span class="md-plain">的线程池，通过</span><span class="md-pair-s" spellcheck="false"><code>processTasks()</code></span><span class="md-plain">方法轮询处理任务，调用默认处理器</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTaskProcessor</code></span><span class="md-plain">的</span><span class="md-pair-s" spellcheck="false"><code>process()</code></span><span class="md-plain">方法。</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-plain">推送任务执行</span></strong><span class="md-plain">：</span><span class="md-pair-s" spellcheck="false"><code>PushDelayTask</code></span><span class="md-plain">转换为</span><span class="md-pair-s" spellcheck="false"><code>PushExecuteTask</code></span><span class="md-plain">，该任务会获取服务最新实例数据（</span><span class="md-pair-s" spellcheck="false"><code>ServiceInfo</code></span><span class="md-plain">）和订阅客户端列表，通过 gRPC 向目标客户端发送</span><span class="md-pair-s" spellcheck="false"><code>NotifySubscriberRequest</code></span><span class="md-plain">请求。</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-plain">客户端处理</span></strong><span class="md-plain">：客户端通过</span><span class="md-pair-s" spellcheck="false"><code>NamingPushRequestHandler</code></span><span class="md-plain">接收请求，调用</span><span class="md-pair-s" spellcheck="false"><code>serviceInfoHolder.processServiceInfo()</code></span><span class="md-plain">更新本地缓存，完成服务变更的同步。</span></p>
</li>
</ol>
<p class="md-end-block md-p md-focus"><span class="md-plain md-expand">整个流程通过事件驱动、延迟任务调度和 gRPC 通信，实现了服务端与客户端的高效数据同步。</span></p>]]></description>
    <pubDate>Fri, 26 Dec 2025 16:46:29 +0800</pubDate>
    <dc:creator>ZealSinger</dc:creator>
    <guid>http://8.154.40.120:24180/?post=51</guid>
</item>
<item>
    <title>Nacos源码学习计划-Day21-Nacos2.x-从服务调用链路上来加深对于内存注册表的理解</title>
    <link>http://8.154.40.120:24180/?post=50</link>
    <description><![CDATA[<p class="md-end-block md-p md-focus"><span class="md-plain md-expand">这一章，主要是从服务调用的链路角度，</span><strong><span class="md-plain">来帮我们对于Nacos2.X版本中的</span><span class="md-pair-s" spellcheck="false"><code>内存注册表结构</code></span><span class="md-plain">的理解</span></strong><span class="md-plain">，在服务调用的链路中，客户端肯定会需要发起从服务端查询对应的服务实例，服务端也会处理这个请求从而查找内存注册表从而进行响应，这个过程中自然就会需要我们去了解内存注册表的结构</span></p>
<h1 class="md-end-block md-heading"><span class="md-plain">客户端服务查询逻辑</span></h1>
<p class="md-end-block md-p"><span class="md-plain">这里我们之前其在Nacos1.4.X版本的分析中有说过的，当时我们对于Nacos2.X版本中的实现也是有一定的提及的，所以我们不会很详细的从头到尾讲，大家可以先去回顾一下</span></p>
<p class="md-end-block md-p"><span class="md-meta-i-c  md-link"><a href="/?post=30" target="_blank" rel="noopener"><span class="md-plain">Nacos源码学习计划-Day05-服务调用时的调用链路（如何获取服务信息） - ZealSingerの博客啦~</span></a></span></p>
<p class="md-end-block md-p"><span class="md-plain">之前有提到，Nacos2.X版本中就是通过实现Spring Cloud服务发现接口，依赖于</span><span class="md-pair-s" spellcheck="false"><code>NacosServiceDiscovery</code></span><span class="md-plain">来和Nacos服务端进行交互，在</span><span class="md-pair-s" spellcheck="false"><code>NacosServiceDiscovery</code></span><span class="md-plain">中查询服务列表的操作其实就是调用的</span><span class="md-pair-s" spellcheck="false"><code>namingService().selectInstances()</code></span><span class="md-plain">，我们可以进去看看他的逻辑</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251219205150332.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251219205150332.png" alt="image-20251219205150332"></span></p>
<p class="md-end-block md-p"><span class="md-plain">这里需要注意，不要看到</span><span class="md-pair-s" spellcheck="false"><code>return语句中的selectInstances</code></span><span class="md-plain">去了，这个里面是对于</span><span class="md-pair-s" spellcheck="false"><code>serviceInfo</code></span><span class="md-plain">对象中的</span><span class="md-pair-s" spellcheck="false"><code>hosts</code></span><span class="md-plain">的健康状态的判断，用于去除不健康的数据，也就是说我们服务端直接返回的是</span><span class="md-pair-s" spellcheck="false"><code>serviceInfo</code></span><span class="md-plain">，所以我们要看这个对象的获取逻辑，也就是如下这行逻辑</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="Java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable">Instance</span><span class="cm-operator">&gt;</span> <span class="cm-def">selectInstances</span>(<span class="cm-variable-3">String</span> <span class="cm-variable">serviceName</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">groupName</span>, <span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-variable">clusters</span>, <span class="cm-variable-3">boolean</span> <span class="cm-variable">healthy</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">boolean</span> <span class="cm-variable">subscribe</span>) <span class="cm-keyword">throws</span> <span class="cm-variable">NacosException</span> {</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">ServiceInfo</span> <span class="cm-variable">serviceInfo</span>;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">clusterString</span> <span class="cm-operator">=</span> <span class="cm-variable">StringUtils</span>.<span class="cm-variable">join</span>(<span class="cm-variable">clusters</span>, <span class="cm-string">","</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 判断是否需要 订阅，默认为 true</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">subscribe</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 查询 Nacos 本地缓存数据</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">serviceInfo</span> <span class="cm-operator">=</span> <span class="cm-variable">serviceInfoHolder</span>.<span class="cm-variable">getServiceInfo</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusterString</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 如果本地缓存数据为空，则通过 client 对象请求服务端获取数据，这里是调用的订阅方法</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">serviceInfo</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">serviceInfo</span> <span class="cm-operator">=</span> <span class="cm-variable">clientProxy</span>.<span class="cm-variable">subscribe</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusterString</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">else</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">serviceInfo</span> <span class="cm-operator">=</span> <span class="cm-variable">clientProxy</span>.<span class="cm-variable">queryInstancesOfService</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusterString</span>, <span class="cm-number">0</span>, <span class="cm-atom">false</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 返回数据</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">selectInstances</span>(<span class="cm-variable">serviceInfo</span>, <span class="cm-variable">healthy</span>);</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><strong><span class="md-plain">这个</span><span class="md-pair-s" spellcheck="false"><code>subscribe</code></span><span class="md-plain">大家先知道他是客户端是否采用</span><span class="md-pair-s" spellcheck="false"><code>订阅模式</code></span><span class="md-plain">，默认情况下就是</span><span class="md-pair-s" spellcheck="false"><code>true即开启订阅模式</code></span><span class="md-plain">，该配置影响的也就是客户端查询服务实例的方式和时机，区别的体现也是就是我们上述代码中</span></strong></p>
<h2 class="md-end-block md-heading"><span class="md-plain">订阅模式下</span></h2>
<p class="md-end-block md-p"><span class="md-plain">订阅模式下，我们可以很清楚的看到，先执行的</span><span class="md-pair-s" spellcheck="false"><code>serviceInfoHolder.getServiceInfo</code></span><span class="md-plain">先查询缓存，其底层就是在客户端内存Map中进行</span><span class="md-pair-s" spellcheck="false"><code>get()操作</code></span><span class="md-plain">，然后</span><strong><span class="md-plain">再执行的</span><span class="md-pair-s" spellcheck="false"><code>clientProxy.subscribe</code></span><span class="md-plain">就是查询服务端且进行订阅，我可以来看一下这个过程的逻辑，这个subscribe是需要寻找对应的实现类的，我们可以直接查看clientProxy数据类型，就知道要看哪个了</span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">ServiceInfo</span> <span class="cm-def">subscribe</span>(<span class="cm-variable-3">String</span> <span class="cm-variable">serviceName</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">groupName</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">clusters</span>) <span class="cm-keyword">throws</span> <span class="cm-variable">NacosException</span> {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">NAMING_LOGGER</span>.<span class="cm-variable">info</span>(<span class="cm-string">"[SUBSCRIBE-SERVICE] service:{}, group:{}, clusters:{} "</span>, <span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusters</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">serviceNameWithGroup</span> <span class="cm-operator">=</span> <span class="cm-variable">NamingUtils</span>.<span class="cm-variable">getGroupedName</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">serviceKey</span> <span class="cm-operator">=</span> <span class="cm-variable">ServiceInfo</span>.<span class="cm-variable">getKey</span>(<span class="cm-variable">serviceNameWithGroup</span>, <span class="cm-variable">clusters</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 开启实例查询定时任务</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">serviceInfoUpdateService</span>.<span class="cm-variable">scheduleUpdateIfAbsent</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusters</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 会再一次查询缓存数据</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">ServiceInfo</span> <span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-variable">serviceInfoHolder</span>.<span class="cm-variable">getServiceInfoMap</span>().<span class="cm-variable">get</span>(<span class="cm-variable">serviceKey</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">result</span> <span class="cm-operator">||</span> <span class="cm-operator">!</span><span class="cm-variable">isSubscribed</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusters</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 如果还是为空，则会使用 grpc 来请求服务端</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-variable">grpcClientProxy</span>.<span class="cm-variable">subscribe</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusters</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 更新本地缓存</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">serviceInfoHolder</span>.<span class="cm-variable">processServiceInfo</span>(<span class="cm-variable">result</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">result</span>;</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>scheduleUpdateIfAbsent</code></span><span class="md-plain">开启了一个 </span><span class="md-pair-s" spellcheck="false"><code>实例查询定时任务</code></span><span class="md-plain">，在这个定时任务中会去更新本地缓存的数据，我们来看一下里面的逻辑</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">final</span> <span class="cm-variable">Map</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span>, <span class="cm-variable">ScheduledFuture</span><span class="cm-operator">&lt;?&gt;&gt;</span> <span class="cm-variable">futureMap</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">HashMap</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span>, <span class="cm-variable">ScheduledFuture</span><span class="cm-operator">&lt;?&gt;&gt;</span>();</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">scheduleUpdateIfAbsent</span>(<span class="cm-variable-3">String</span> <span class="cm-variable">serviceName</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">groupName</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">clusters</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 生成一个 serverKey</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">serviceKey</span> <span class="cm-operator">=</span> <span class="cm-variable">ServiceInfo</span>.<span class="cm-variable">getKey</span>(<span class="cm-variable">NamingUtils</span>.<span class="cm-variable">getGroupedName</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>), <span class="cm-variable">clusters</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 判断当前 serviceKey 是否有开启定时任务，如果有就不开启了</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">futureMap</span>.<span class="cm-variable">get</span>(<span class="cm-variable">serviceKey</span>) <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 加了一把同步锁，以免并发冲突</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">synchronized</span> (<span class="cm-variable">futureMap</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 加锁之后又再一次进行判断，双重检测</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">futureMap</span>.<span class="cm-variable">get</span>(<span class="cm-variable">serviceKey</span>) <span class="cm-operator">!=</span> <span class="cm-atom">null</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 最后开 UpdateTask 定时任务</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">ScheduledFuture</span><span class="cm-operator">&lt;?&gt;</span> <span class="cm-variable">future</span> <span class="cm-operator">=</span> <span class="cm-variable">addTask</span>(<span class="cm-keyword">new</span> <span class="cm-variable">UpdateTask</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusters</span>));</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">futureMap</span>.<span class="cm-variable">put</span>(<span class="cm-variable">serviceKey</span>, <span class="cm-variable">future</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">每次查询一个新的 service 都会开启这么一个定时任务，来为本地数据进行更新操作。我们再来看看这个 </span><span class="md-pair-s" spellcheck="false"><code>UpdateTask</code></span><span class="md-plain"> 做了什么事情，代码如下：</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">run</span>() {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">long</span> <span class="cm-variable">delayTime</span> <span class="cm-operator">=</span> <span class="cm-variable">DEFAULT_DELAY</span>;</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">try</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 如果没有订阅且map中不包含任务 则取消该任务</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">changeNotifier</span>.<span class="cm-variable">isSubscribed</span>(<span class="cm-variable">groupName</span>, <span class="cm-variable">serviceName</span>, <span class="cm-variable">clusters</span>) <span class="cm-operator">&amp;&amp;</span> <span class="cm-operator">!</span><span class="cm-variable">futureMap</span>.<span class="cm-variable">containsKey</span>(</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">serviceKey</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">NAMING_LOGGER</span>.<span class="cm-variable">info</span>(<span class="cm-string">"update task is stopped, service:{}, clusters:{}"</span>, <span class="cm-variable">groupedServiceName</span>, <span class="cm-variable">clusters</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">isCancel</span> <span class="cm-operator">=</span> <span class="cm-atom">true</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 从本地缓存中获取一次，如果本地缓存中为空</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">ServiceInfo</span> <span class="cm-variable">serviceObj</span> <span class="cm-operator">=</span> <span class="cm-variable">serviceInfoHolder</span>.<span class="cm-variable">getServiceInfoMap</span>().<span class="cm-variable">get</span>(<span class="cm-variable">serviceKey</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">serviceObj</span> <span class="cm-operator">==</span> <span class="cm-atom">null</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 为空就会通过 gRPC 去查询服务端的数据</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">serviceObj</span> <span class="cm-operator">=</span> <span class="cm-variable">namingClientProxy</span>.<span class="cm-variable">queryInstancesOfService</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusters</span>, <span class="cm-number">0</span>, <span class="cm-atom">false</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 更新本地缓存</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">serviceInfoHolder</span>.<span class="cm-variable">processServiceInfo</span>(<span class="cm-variable">serviceObj</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 更新获取时间</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">lastRefTime</span> <span class="cm-operator">=</span> <span class="cm-variable">serviceObj</span>.<span class="cm-variable">getLastRefTime</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 如果本地缓存不为空，会判断该本地缓存最后一次刷新的时间，是否小于等于 最后一次数据刷新时间</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">serviceObj</span>.<span class="cm-variable">getLastRefTime</span>() <span class="cm-operator">&lt;=</span> <span class="cm-variable">lastRefTime</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 小于等于的话，会重新请求服务端数据，然后更新本地缓存</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 需要注意queryInstancesOfService方法的后面两个参数 0是updPort 这个是；历史兼容问题，Nacos1.X版本中会通过upd协议进行服务端对客户端的主动推送，这个参数用于告诉服务端，客户端在哪个端口进行监听接受</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 但是Nacos2.X版本中已经全面采用gRPC了，所以这个参数其实是没有意义，所以直接传入0即可</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 最后一个boolea类型的参数，代表healthOnly，即是否只返回健康实例，如果传入false则代表无论健康还是不健康的实例都会被传回</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">serviceObj</span> <span class="cm-operator">=</span> <span class="cm-variable">namingClientProxy</span>.<span class="cm-variable">queryInstancesOfService</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusters</span>, <span class="cm-number">0</span>, <span class="cm-atom">false</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">serviceInfoHolder</span>.<span class="cm-variable">processServiceInfo</span>(<span class="cm-variable">serviceObj</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; </span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">lastRefTime</span> <span class="cm-operator">=</span> <span class="cm-variable">serviceObj</span>.<span class="cm-variable">getLastRefTime</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 如果为empty 则代表请求失败了 则会累计请求失败次数</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 因为我们在查询的时候设置了healthOnly参数为false，所以如果返回为空则说明从服务端的角度而言单体或者集群中满足条件的健康实例和非健康实例，这个对于客户端缓存而言的效果就是没有，所以empty可以直接和请求fail进行绑定</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">CollectionUtils</span>.<span class="cm-variable">isEmpty</span>(<span class="cm-variable">serviceObj</span>.<span class="cm-variable">getHosts</span>())) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 累计请求失败次数 实质上就是failCount这个变量递增</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">incFailCount</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 计算下一次定时任务执行的时间，这里的结果是 6s</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">delayTime</span> <span class="cm-operator">=</span> <span class="cm-variable">serviceObj</span>.<span class="cm-variable">getCacheMillis</span>() <span class="cm-operator">*</span> <span class="cm-variable">DEFAULT_UPDATE_CACHE_TIME_MULTIPLE</span>;</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 当请求成功之后，会重置错误次数为 0 </span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">resetFailCount</span>();</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">catch</span> (<span class="cm-variable">Throwable</span> <span class="cm-variable">e</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 记录请求失败的次数</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">incFailCount</span>();</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">NAMING_LOGGER</span>.<span class="cm-variable">warn</span>(<span class="cm-string">"[NA] failed to update serviceName: {}"</span>, <span class="cm-variable">groupedServiceName</span>, <span class="cm-variable">e</span>);</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">finally</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">isCancel</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 这里就根据请求失败的次数，来动态调整定时任务的执行时间，防止频繁地重试</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">executor</span>.<span class="cm-variable">schedule</span>(<span class="cm-keyword">this</span>, <span class="cm-variable">Math</span>.<span class="cm-variable">min</span>(<span class="cm-variable">delayTime</span> <span class="cm-operator">&lt;&lt;</span> <span class="cm-variable">failCount</span>, <span class="cm-variable">DEFAULT_DELAY</span> <span class="cm-operator">*</span> <span class="cm-number">60</span>),</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">TimeUnit</span>.<span class="cm-variable">MILLISECONDS</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">定时任务开启之后，我们会通过</span><span class="md-pair-s" spellcheck="false"><code>subscribe</code></span><span class="md-plain">方法对服务端发起订阅查询这个RPC请求，其逻辑如下,可以发现就是发起了一个SubscribeServiceRequest的请求</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-comment">/**</span><br><span role="presentation"> <span class="cm-comment">* Execute subscribe operation.</span></span><br><span role="presentation"> <span class="cm-comment">*</span></span><br><span role="presentation"> <span class="cm-comment">* @param serviceName service name</span></span><br><span role="presentation"> <span class="cm-comment">* @param groupName &nbsp; group name</span></span><br><span role="presentation"> <span class="cm-comment">* @param clusters &nbsp;  clusters, current only support subscribe all clusters, maybe deprecated</span></span><br><span role="presentation"> <span class="cm-comment">* @return current service info of subscribe service</span></span><br><span role="presentation"> <span class="cm-comment">* @throws NacosException nacos exception</span></span><br><span role="presentation"> <span class="cm-comment">*/</span></span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">ServiceInfo</span> <span class="cm-def">doSubscribe</span>(<span class="cm-variable-3">String</span> <span class="cm-variable">serviceName</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">groupName</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">clusters</span>) <span class="cm-keyword">throws</span> <span class="cm-variable">NacosException</span> {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">SubscribeServiceRequest</span> <span class="cm-variable">request</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">SubscribeServiceRequest</span>(<span class="cm-variable">namespaceId</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">serviceName</span>, <span class="cm-variable">clusters</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-atom">true</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">SubscribeServiceResponse</span> <span class="cm-variable">response</span> <span class="cm-operator">=</span> <span class="cm-variable">requestToServer</span>(<span class="cm-variable">request</span>, <span class="cm-variable">SubscribeServiceResponse</span>.<span class="cm-keyword">class</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">redoService</span>.<span class="cm-variable">subscriberRegistered</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusters</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">response</span>.<span class="cm-variable">getServiceInfo</span>();</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p">&nbsp;</p>
<h2 class="md-end-block md-heading"><span class="md-plain">非订阅模式下</span></h2>
<p class="md-end-block md-p"><span class="md-plain">至于非订阅模式下，代码逻辑就很简单了</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-variable">serviceInfo</span> <span class="cm-operator">=</span> <span class="cm-variable">clientProxy</span>.<span class="cm-variable">queryInstancesOfService</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">clusterString</span>, <span class="cm-number">0</span>, <span class="cm-atom">false</span>);</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到，这个逻辑其实也是订阅模式下</span><span class="md-pair-s" spellcheck="false"><code>UpdateTask</code></span><span class="md-plain">的</span><span class="md-pair-s" spellcheck="false"><code>run</code></span><span class="md-plain">逻辑中</span><span class="md-pair-s" spellcheck="false"><code>RPC查询服务端</code></span><span class="md-plain">的方法，其实就是调用了同一个方法进行RPC查询，所以这里我们不需要多解释。</span></p>
<figure class="md-table-fig table-figure">
<table class="md-table">
<thead>
<tr class="md-end-block">
<th><span class="md-plain">方法</span></th>
<th><span class="md-plain">设计意图</span></th>
</tr>
</thead>
<tbody>
<tr class="md-end-block">
<td><span class="md-pair-s" spellcheck="false"><code>subscribe</code></span></td>
<td><span class="md-plain">以「一次订阅请求」换「后续无感知的推送更新」，减少客户端与服务端的交互次数，提升性能（订阅模式的核心价值）</span></td>
</tr>
<tr class="md-end-block">
<td><span class="md-pair-s" spellcheck="false"><code>queryInstancesOfService</code></span></td>
<td><span class="md-plain">满足「单次实时查询」场景（如手动触发的实例校验、非长连接场景），牺牲性能换实时性，同时通过降级缓存保证容错</span></td>
</tr>
</tbody>
</table>
</figure>
<h1 class="md-end-block md-heading"><span class="md-plain">服务端处理订阅查询请求</span></h1>
<p class="md-end-block md-p"><span class="md-plain">我们在分析订阅模式下的请求逻辑的时候，看到了实际上就是发出了一个</span><span class="md-pair-s" spellcheck="false"><code>SubscribeServiceRequest</code></span><span class="md-plain">的请求到服务端，老规矩，我们利用Idea的搜索功能到服务端查看一下服务端是如何处理的</span></p>
<p class="md-end-block md-p"><span class="md-plain">很轻松我们就能找到</span><span class="md-pair-s" spellcheck="false"><code>SubscribeServiceRequestHandler</code></span><span class="md-plain">这个处理器，其逻辑为</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-meta">@Secured</span>(<span class="cm-variable">action</span> <span class="cm-operator">=</span> <span class="cm-variable">ActionTypes</span>.<span class="cm-variable">READ</span>)</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">SubscribeServiceResponse</span> <span class="cm-def">handle</span>(<span class="cm-variable">SubscribeServiceRequest</span> <span class="cm-variable">request</span>, <span class="cm-variable">RequestMeta</span> <span class="cm-variable">meta</span>) <span class="cm-keyword">throws</span> <span class="cm-variable">NacosException</span> {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 从request中获取相关目标服务的数据</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">namespaceId</span> <span class="cm-operator">=</span> <span class="cm-variable">request</span>.<span class="cm-variable">getNamespace</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">serviceName</span> <span class="cm-operator">=</span> <span class="cm-variable">request</span>.<span class="cm-variable">getServiceName</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">groupName</span> <span class="cm-operator">=</span> <span class="cm-variable">request</span>.<span class="cm-variable">getGroupName</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">app</span> <span class="cm-operator">=</span> <span class="cm-variable">request</span>.<span class="cm-variable">getHeader</span>(<span class="cm-string">"app"</span>, <span class="cm-string">"unknown"</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">groupedServiceName</span> <span class="cm-operator">=</span> <span class="cm-variable">NamingUtils</span>.<span class="cm-variable">getGroupedName</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 利用参数创建一个临时service 即被调用的那个服务的Service</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Service</span> <span class="cm-variable">service</span> <span class="cm-operator">=</span> <span class="cm-variable">Service</span>.<span class="cm-variable">newService</span>(<span class="cm-variable">namespaceId</span>, <span class="cm-variable">groupName</span>, <span class="cm-variable">serviceName</span>, <span class="cm-atom">true</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 利用请求元数据 创建请求订阅的对象 即请求发起者</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Subscriber</span> <span class="cm-variable">subscriber</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">Subscriber</span>(<span class="cm-variable">meta</span>.<span class="cm-variable">getClientIp</span>(), <span class="cm-variable">meta</span>.<span class="cm-variable">getClientVersion</span>(), <span class="cm-variable">app</span>, <span class="cm-variable">meta</span>.<span class="cm-variable">getClientIp</span>(),</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">namespaceId</span>, <span class="cm-variable">groupedServiceName</span>, <span class="cm-number">0</span>, <span class="cm-variable">request</span>.<span class="cm-variable">getClusters</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 获取健康的实例</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">ServiceInfo</span> <span class="cm-variable">serviceInfo</span> <span class="cm-operator">=</span> <span class="cm-variable">ServiceUtil</span>.<span class="cm-variable">selectInstancesWithHealthyProtection</span>(<span class="cm-variable">serviceStorage</span>.<span class="cm-variable">getData</span>(<span class="cm-variable">service</span>),</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">metadataManager</span>.<span class="cm-variable">getServiceMetadata</span>(<span class="cm-variable">service</span>).<span class="cm-variable">orElse</span>(<span class="cm-atom">null</span>), <span class="cm-variable">subscriber</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">request</span>.<span class="cm-variable">isSubscribe</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 如果是订阅模式 在这里进行订阅关系的绑定 当订阅关系绑定成功后 会触发服务端推送服务数据</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">clientOperationService</span>.<span class="cm-variable">subscribeService</span>(<span class="cm-variable">service</span>, <span class="cm-variable">subscriber</span>, <span class="cm-variable">meta</span>.<span class="cm-variable">getConnectionId</span>());</span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">else</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">clientOperationService</span>.<span class="cm-variable">unsubscribeService</span>(<span class="cm-variable">service</span>, <span class="cm-variable">subscriber</span>, <span class="cm-variable">meta</span>.<span class="cm-variable">getConnectionId</span>());</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">SubscribeServiceResponse</span>(<span class="cm-variable">ResponseCode</span>.<span class="cm-variable">SUCCESS</span>.<span class="cm-variable">getCode</span>(), <span class="cm-string">"success"</span>, <span class="cm-variable">serviceInfo</span>);</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">主体逻辑的分析就是如上，我们现在来看看里面的几个方法</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">selectInstancesWithHealthyProtection</span></h2>
<p class="md-end-block md-p"><span class="md-plain">这个方法的直接逻辑是获取有健康保证的实例，其方法定义如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-comment">/**</span><br><span role="presentation"> <span class="cm-comment">* Select instance of service info with healthy protection.</span></span><br><span role="presentation"> <span class="cm-comment">* @param serviceInfo &nbsp; &nbsp; original service info</span></span><br><span role="presentation"> <span class="cm-comment">* @param serviceMetadata service meta info</span></span><br><span role="presentation"> <span class="cm-comment">* @param subscriber subscriber</span></span><br><span role="presentation"> <span class="cm-comment">* @return new service info</span></span><br><span role="presentation"> <span class="cm-comment">*/</span></span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-keyword">static</span> <span class="cm-variable">ServiceInfo</span> <span class="cm-def">selectInstancesWithHealthyProtection</span>(<span class="cm-variable">ServiceInfo</span> <span class="cm-variable">serviceInfo</span>, <span class="cm-variable">ServiceMetadata</span> <span class="cm-variable">serviceMetadata</span>, <span class="cm-variable">Subscriber</span> <span class="cm-variable">subscriber</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">selectInstancesWithHealthyProtection</span>(<span class="cm-variable">serviceInfo</span>, <span class="cm-variable">serviceMetadata</span>, <span class="cm-variable">subscriber</span>.<span class="cm-variable">getCluster</span>(), <span class="cm-atom">false</span>, <span class="cm-atom">false</span>, <span class="cm-variable">subscriber</span>.<span class="cm-variable">getIp</span>());</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">该方法的第一个参数是</span><span class="md-pair-s" spellcheck="false"><code>ServiceInfo</code></span><span class="md-plain">对象，这个参数的获取是通过</span><span class="md-pair-s" spellcheck="false"><code>serviceStorage.getData(service)</code></span><span class="md-plain">获取到的，我们来看一下他的逻辑</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentMap</span><span class="cm-operator">&lt;</span><span class="cm-variable">Service</span>, <span class="cm-variable">ServiceInfo</span><span class="cm-operator">&gt;</span> <span class="cm-variable">serviceDataIndexes</span>;</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">ServiceInfo</span> <span class="cm-def">getData</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 判断缓存中是否有数据，如果有直接取缓存数据，缓存没有就调用 getPushData(service) </span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">serviceDataIndexes</span>.<span class="cm-variable">containsKey</span>(<span class="cm-variable">service</span>) <span class="cm-operator">?</span> <span class="cm-variable">serviceDataIndexes</span>.<span class="cm-variable">get</span>(<span class="cm-variable">service</span>) : <span class="cm-variable">getPushData</span>(<span class="cm-variable">service</span>);</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">我们第一次查，肯定是没有缓存的，也就是肯定走后面的</span><span class="md-pair-s" spellcheck="false"><code>getPushData()</code></span><span class="md-plain">方法</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">ServiceInfo</span> <span class="cm-def">getPushData</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// emptyServiceInfo的作用是将Service对象转化为ServiceInfo对象，注意这个result目前其hosts是空的</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">ServiceInfo</span> <span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-variable">emptyServiceInfo</span>(<span class="cm-variable">service</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">ServiceManager</span>.<span class="cm-variable">getInstance</span>().<span class="cm-variable">containSingleton</span>(<span class="cm-variable">service</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">result</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// getAllInstancesFromIndex方法赋值hosts</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">result</span>.<span class="cm-variable">setHosts</span>(<span class="cm-variable">getAllInstancesFromIndex</span>(<span class="cm-variable">service</span>));</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">serviceDataIndexes</span>.<span class="cm-variable">put</span>(<span class="cm-variable">service</span>, <span class="cm-variable">result</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">result</span>;</span><br><span role="presentation">}</span><br><span role="presentation"> &nbsp; </span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable">Instance</span><span class="cm-operator">&gt;</span> <span class="cm-def">getAllInstancesFromIndex</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Set</span><span class="cm-operator">&lt;</span><span class="cm-variable">Instance</span><span class="cm-operator">&gt;</span> <span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">HashSet</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Set</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-variable">clusters</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">HashSet</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">for</span> (<span class="cm-variable-3">String</span> <span class="cm-variable">each</span> : <span class="cm-variable">serviceIndexesManager</span>.<span class="cm-variable">getAllClientsRegisteredService</span>(<span class="cm-variable">service</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Optional</span><span class="cm-operator">&lt;</span><span class="cm-variable">InstancePublishInfo</span><span class="cm-operator">&gt;</span> <span class="cm-variable">instancePublishInfo</span> <span class="cm-operator">=</span> <span class="cm-variable">getInstanceInfo</span>(<span class="cm-variable">each</span>, <span class="cm-variable">service</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">instancePublishInfo</span>.<span class="cm-variable">isPresent</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Instance</span> <span class="cm-variable">instance</span> <span class="cm-operator">=</span> <span class="cm-variable">parseInstance</span>(<span class="cm-variable">service</span>, <span class="cm-variable">instancePublishInfo</span>.<span class="cm-variable">get</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">result</span>.<span class="cm-variable">add</span>(<span class="cm-variable">instance</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">clusters</span>.<span class="cm-variable">add</span>(<span class="cm-variable">instance</span>.<span class="cm-variable">getClusterName</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// cache clusters of this service</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">serviceClusterIndex</span>.<span class="cm-variable">put</span>(<span class="cm-variable">service</span>, <span class="cm-variable">clusters</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">LinkedList</span><span class="cm-operator">&lt;&gt;</span>(<span class="cm-variable">result</span>);</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">其中</span><span class="md-pair-s" spellcheck="false"><code>getAllInstancesFromIndex</code></span><span class="md-plain">方法很关键，分析如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable">List</span><span class="cm-operator">&lt;</span><span class="cm-variable">Instance</span><span class="cm-operator">&gt;</span> <span class="cm-def">getAllInstancesFromIndex</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 这里的 service 参数对应我们需要查询的服务 stock-service</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Set</span><span class="cm-operator">&lt;</span><span class="cm-variable">Instance</span><span class="cm-operator">&gt;</span> <span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">HashSet</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Set</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-variable">clusters</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">HashSet</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation"> &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// each = clientId，通过 service 获取对应 service 全部的 ClientId</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">for</span> (<span class="cm-variable-3">String</span> <span class="cm-variable">each</span> : <span class="cm-variable">serviceIndexesManager</span>.<span class="cm-variable">getAllClientsRegisteredService</span>(<span class="cm-variable">service</span>)) {</span><br><span role="presentation"> &nbsp; </span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 通过 clientId 查找对应的 Instance 信息</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Optional</span><span class="cm-operator">&lt;</span><span class="cm-variable">InstancePublishInfo</span><span class="cm-operator">&gt;</span> <span class="cm-variable">instancePublishInfo</span> <span class="cm-operator">=</span> <span class="cm-variable">getInstanceInfo</span>(<span class="cm-variable">each</span>, <span class="cm-variable">service</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">instancePublishInfo</span>.<span class="cm-variable">isPresent</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 对象转换</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Instance</span> <span class="cm-variable">instance</span> <span class="cm-operator">=</span> <span class="cm-variable">parseInstance</span>(<span class="cm-variable">service</span>, <span class="cm-variable">instancePublishInfo</span>.<span class="cm-variable">get</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">result</span>.<span class="cm-variable">add</span>(<span class="cm-variable">instance</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">clusters</span>.<span class="cm-variable">add</span>(<span class="cm-variable">instance</span>.<span class="cm-variable">getClusterName</span>());</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  }</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 缓存该 service 对应有哪些 集群</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">serviceClusterIndex</span>.<span class="cm-variable">put</span>(<span class="cm-variable">service</span>, <span class="cm-variable">clusters</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">LinkedList</span><span class="cm-operator">&lt;&gt;</span>(<span class="cm-variable">result</span>);</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><strong><span class="md-pair-s" spellcheck="false"><code>getAllClientsRegisteredService</code></span><span class="md-plain">的底层其实就是我们上一节分析过的内容，从</span><span class="md-pair-s" spellcheck="false"><code>publisherIndexes</code></span><span class="md-plain">这个Map中获取对应</span><span class="md-pair-s" spellcheck="false"><code>clientId集合</code></span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-comment">// 这个map保存的是&lt;Service,Set&lt;clientId&gt;&gt;的对照关系</span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentMap</span><span class="cm-operator">&lt;</span><span class="cm-variable">Service</span>, <span class="cm-variable">Set</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;&gt;</span> <span class="cm-variable">publisherIndexes</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentMap</span><span class="cm-operator">&lt;</span><span class="cm-variable">Service</span>, <span class="cm-variable">Set</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;&gt;</span> <span class="cm-variable">subscriberIndexes</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">Collection</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;</span> <span class="cm-def">getAllClientsRegisteredService</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>) {</span><br><span role="presentation">    <span class="cm-keyword">return</span> <span class="cm-variable">publisherIndexes</span>.<span class="cm-variable">containsKey</span>(<span class="cm-variable">service</span>) <span class="cm-operator">?</span> <span class="cm-variable">publisherIndexes</span>.<span class="cm-variable">get</span>(<span class="cm-variable">service</span>) : <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashSet</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">接下来的逻辑也很明显了，</span><strong><span class="md-plain">拿到对应的</span><span class="md-pair-s" spellcheck="false"><code>clientId</code></span><span class="md-plain">之后就可以查找client从而获得对应的实例信息（之前有提到过这两者之间的关系），也就是</span><span class="md-pair-s" spellcheck="false"><code>getInstanceInfo</code></span><span class="md-plain">的逻辑</span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable">Optional</span><span class="cm-operator">&lt;</span><span class="cm-variable">InstancePublishInfo</span><span class="cm-operator">&gt;</span> <span class="cm-def">getInstanceInfo</span>(<span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span>, <span class="cm-variable">Service</span> <span class="cm-variable">service</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 通过 clientId 获取每一个对应的 client 连接对象</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Client</span> <span class="cm-variable">client</span> <span class="cm-operator">=</span> <span class="cm-variable">clientManager</span>.<span class="cm-variable">getClient</span>(<span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">client</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">Optional</span>.<span class="cm-variable">empty</span>();</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 在 client 对象中，获取实例信息，getInstancePublishInfo的底层就是从publishers这个Map中获取，这个Map中维护了Service和Instance的对照关系</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">Optional</span>.<span class="cm-variable">ofNullable</span>(<span class="cm-variable">client</span>.<span class="cm-variable">getInstancePublishInfo</span>(<span class="cm-variable">service</span>));</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">到这里，我们整个查找的过程差不多接近尾声了，这个通过Service查找到对应的Instance的流程和我们上一节提到的内存表的设计关系是完全一致的</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">绑定订阅关系</span></h2>
<p class="md-end-block md-p"><span class="md-plain">查找到有健康保障的实例时候，就需要绑定订阅关系了</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">if</span> (<span class="cm-variable">request</span>.<span class="cm-variable">isSubscribe</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 添加订阅者，如果被订阅的服务有变动，需要通知订阅者</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">clientOperationService</span>.<span class="cm-variable">subscribeService</span>(<span class="cm-variable">service</span>, <span class="cm-variable">subscriber</span>, <span class="cm-variable">meta</span>.<span class="cm-variable">getConnectionId</span>());</span><br><span role="presentation">} <span class="cm-keyword">else</span> {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">clientOperationService</span>.<span class="cm-variable">unsubscribeService</span>(<span class="cm-variable">service</span>, <span class="cm-variable">subscriber</span>, <span class="cm-variable">meta</span>.<span class="cm-variable">getConnectionId</span>());</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>subscribeService</code></span><span class="md-plain">的代码逻辑如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-comment">/**</span><br><span role="presentation"> <span class="cm-comment">* 订阅者列表</span></span><br><span role="presentation"> <span class="cm-comment">*/</span></span><br><span role="presentation"><span class="cm-keyword">protected</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;</span><span class="cm-variable">Service</span>, <span class="cm-variable">Subscriber</span><span class="cm-operator">&gt;</span> <span class="cm-variable">subscribers</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;&gt;</span>(<span class="cm-number">16</span>, <span class="cm-number">0.75f</span>, <span class="cm-number">1</span>);</span><br><span role="presentation">​</span><br><span role="presentation">​</span><br><span class="cm-comment">/**</span><br><span role="presentation"> <span class="cm-comment">* 添加订阅者</span></span><br><span role="presentation"> <span class="cm-comment">* @param service &nbsp;  service 被查询的对象</span></span><br><span role="presentation"> <span class="cm-comment">* @param subscriber subscribe 订阅者 对应请求调用者</span></span><br><span role="presentation"> <span class="cm-comment">* @param clientId &nbsp; id of client &nbsp; 维护请求调用者和Nacos之间的关系</span></span><br><span role="presentation"> <span class="cm-comment">*/</span></span><br><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">subscribeService</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable">Subscriber</span> <span class="cm-variable">subscriber</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span>) {</span><br><span role="presentation">​</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 被调用者对象/服务对象</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Service</span> <span class="cm-variable">singleton</span> <span class="cm-operator">=</span> <span class="cm-variable">ServiceManager</span>.<span class="cm-variable">getInstance</span>().<span class="cm-variable">getSingletonIfExist</span>(<span class="cm-variable">service</span>).<span class="cm-variable">orElse</span>(<span class="cm-variable">service</span>);</span><br><span role="presentation">​</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 订阅者/发其请求者</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Client</span> <span class="cm-variable">client</span> <span class="cm-operator">=</span> <span class="cm-variable">clientManager</span>.<span class="cm-variable">getClient</span>(<span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">clientIsLegal</span>(<span class="cm-variable">client</span>, <span class="cm-variable">clientId</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">//  订阅者/发其请求者 对应的 Client 对象中，添加订阅者订阅者信息</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">client</span>.<span class="cm-variable">addServiceSubscriber</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">subscriber</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">client</span>.<span class="cm-variable">setLastUpdatedTime</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 发布客户端订阅事件</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">ClientOperationEvent</span>.<span class="cm-variable">ClientSubscribeServiceEvent</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">clientId</span>));</span><br><span role="presentation">}</span><br><span role="presentation">​</span><br><span role="presentation">​</span><br><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">addServiceSubscriber</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable">Subscriber</span> <span class="cm-variable">subscriber</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 添加 服务者-订阅者 之间的映射关系内存缓存map</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">subscribers</span>.<span class="cm-variable">put</span>(<span class="cm-variable">service</span>, <span class="cm-variable">subscriber</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">MetricsMonitor</span>.<span class="cm-variable">incrementSubscribeCount</span>();</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到如上代码的作用，</span><strong><span class="md-plain">主要就是发布了通知事件且在 订阅者的client对象中添加了</span><span class="md-pair-s" spellcheck="false"><code>key=服务对象；value=订阅者</code></span><span class="md-plain">的缓存内容</span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" spellcheck="false"><span role="presentation">这里需要注意一下ConcurrentHashMap&lt;Service, Subscriber&gt; subscribers 他是属于client层面的，也就是每个client中的一份，而不是staic或者说不是全局的</span><br><span role="presentation">​</span><br><span role="presentation">怎么理解呢，就是如果大家先不这么理解，可能会有UU认为就是，这个map是一对一的关系，表示的是服务者-订阅者，那么是不是代表一个服务只能被一个订阅者订阅呢？然后我们来分析这个问题</span><br><span role="presentation">首先从业务层面而言，这个理解肯定不对，我们有两个客户端clientA和clientB，然后我们有个远端服务ServiceC，A和B进行服务调用的时候肯定都会需要查询服务C，按照默认设置都是订阅模式，那么肯定需要A和B同时都能动态感知ServiceC的变化，也就是需要A和B都订阅C，事实上我们Nacos中自然是能这么使用的。所以从业务角度而言，肯定需要支持被多个订阅者订阅、</span><br><span role="presentation">​</span><br><span role="presentation">然后从代码层面来看，就需要结合我们所说的，这个Map是每个client层面的，也就是说ServiceC-clientB这个属于clientB中的map，ServiceC-clientA这个属于clientA中的map，ServiceC-clientA标识cleintA订阅了ServiceC，如果我们还有别的微服务，A同样需要监听，也就会出现ServiceC1-clientA，ServiceC2-clientA，ServiceC3-clientA...</span><br><span role="presentation">​</span><br><span role="presentation">那么我们所说的，一个ServiceC对于所有的订阅他的client是如何存储的呢？订阅功能的需求而言，肯定会需要统一维护，当出现变更的时候才能快速的进行通知，这个其实就是我们之前的ConcurrentMap&lt;Service, Set&lt;String&gt;&gt; subscriberIndexes这种类型的map，保存的是&lt;Service,Set&lt;clientId&gt;&gt;的对照关系</span></pre>
<h2 class="md-end-block md-heading"><span class="md-plain">ClientSubscribeServiceEvent</span></h2>
<p class="md-end-block md-p"><span class="md-plain">在上述逻辑的最后呢，发布了</span><span class="md-pair-s" spellcheck="false"><code>ClientSubscribeServiceEvent</code></span><span class="md-plain">这个事件，我们来分析一下这个地方，同样的，通过Idea的全局搜索，我们可以找到处理这个事件的地方，其实也是我们一开始进行RPC注册时候处理注册事件的地方，我们先来稍微的看一下</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251220203053250.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251220203053250.png" alt="image-20251220203053250"></span></p>
<p class="md-end-block md-p"><strong><span class="md-pair-s" spellcheck="false"><code>addSubscriberIndexes</code></span><span class="md-plain">具体逻辑如下</span></strong><span class="md-plain">，如果是第一次订阅的客户端，</span><strong><span class="md-plain">会发布</span><span class="md-pair-s" spellcheck="false"><code>ServiceSubscribedEvent</code></span><span class="md-plain">服务订阅事件，这个我们放在下一个章节来讲解</span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable-3">void</span> <span class="cm-def">addSubscriberIndexes</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">subscriberIndexes</span>.<span class="cm-variable">computeIfAbsent</span>(<span class="cm-variable">service</span>, (<span class="cm-variable">key</span>) <span class="cm-operator">-&gt;</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashSet</span><span class="cm-operator">&lt;&gt;</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// Fix #5404,只有第一次放入的时候需要通知</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">subscriberIndexes</span>.<span class="cm-variable">get</span>(<span class="cm-variable">service</span>).<span class="cm-variable">add</span>(<span class="cm-variable">clientId</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 发布订阅事件</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">ServiceEvent</span>.<span class="cm-variable">ServiceSubscribedEvent</span>(<span class="cm-variable">service</span>, <span class="cm-variable">clientId</span>));</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span></pre>]]></description>
    <pubDate>Sat, 20 Dec 2025 20:34:27 +0800</pubDate>
    <dc:creator>ZealSinger</dc:creator>
    <guid>http://8.154.40.120:24180/?post=50</guid>
</item>
<item>
    <title>Nacos源码学习计划-补档-Nacos服务订阅链路分析纠错</title>
    <link>http://8.154.40.120:24180/?post=49</link>
    <description><![CDATA[<h3 class="header-iWP5WJ auto-hide-last-sibling-br">对于之前在《<a href="/?post=30" target="_blank" rel="noopener">Nacos源码学习计划-Day05-服务调用时的调用链路（如何获取服务信息）</a>》一文中，在开始介绍Nacos高版本中通过SCLB来实现客户端实例发现，在一开始做高版本和低版本之间的区别总结，提到如下内容</h3>
<p>- 旧版本中，优先查本地缓存，然后再Nacos服务端进行辅助和更新，是以本地缓存为主，Nacos服务端为辅，这个点其实很容易看出来会有实时性的问题<br>- 新版本中，获取实例都是直接从服务端拉取，但是本地缓存依旧存在，成功拉取后会更新本地缓存，只有当远端拉取失败的时候，才会用缓存数据进行兜底（类似于Leaf的那种buffer机制，为了减少对服务端的绝对依赖，当Nacos服务挂了）<br>- 从上述变化中可以看到，就是对于实时性和性能之间的优先级的权衡变化</p>
<h3 class="header-iWP5WJ auto-hide-last-sibling-br">上述对比不准确且有错误，没有详细的区分订阅模式和非订阅模式，高低版本中缓存的使用其实是类似的，但是其真正的区别在于底层的本质</h3>
<p>&nbsp;</p>
<h3 class="header-iWP5WJ auto-hide-last-sibling-br">一、核心前提：版本底层架构差异（根源）</h3>
<div class="auto-hide-last-sibling-br mdbox-table-root table-container-cYf_5N" data-scroll-inline-overflow="true" data-scroll-inline-at-start="true" data-scroll-inline-start-overflow="false" data-scroll-inline-at-end="false" data-scroll-inline-end-overflow="true">
<div class="table-scroll-container-Gyf4hQ mdbox-table-scroll-container">
<table>
<thead>
<tr>
<th>版本</th>
<th>通信协议</th>
<th>核心更新机制</th>
<th>缓存核心载体</th>
</tr>
</thead>
<tbody>
<tr>
<td>Nacos 1.4</td>
<td>HTTP 短连接</td>
<td>客户端主动<strong>定时轮询</strong>（6 秒）</td>
<td><code>hostReactor</code>&nbsp;下的&nbsp;<code>serviceInfoMap</code></td>
</tr>
<tr>
<td>Nacos 2.X</td>
<td>gRPC 长连接（主）+ HTTP（备）</td>
<td>服务端主动<strong>增量推送</strong></td>
<td><code>serviceInfoHolder</code>&nbsp;本地缓存</td>
</tr>
</tbody>
</table>
</div>
</div>
<h3 class="header-iWP5WJ auto-hide-last-sibling-br">二、Nacos 1.4 订阅 / 非订阅模式汇总</h3>
<h4 class="header-iWP5WJ auto-hide-last-sibling-br">1. 订阅模式（subscribe=true，默认）</h4>
<div class="auto-hide-last-sibling-br mdbox-table-root table-container-cYf_5N" data-scroll-inline-overflow="false" data-scroll-inline-at-start="true" data-scroll-inline-start-overflow="false" data-scroll-inline-at-end="true" data-scroll-inline-end-overflow="false">
<div class="table-scroll-container-Gyf4hQ mdbox-table-scroll-container">
<table>
<thead>
<tr>
<th>维度</th>
<th>核心细节</th>
</tr>
</thead>
<tbody>
<tr>
<td>核心方法</td>
<td>- 入口：<code>selectInstances</code>&nbsp;&rarr;&nbsp;<code>hostReactor.getServiceInfo</code>
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
- 兜底查服务端：<code>updateServiceNow</code>（HTTP 查服务端全量数据）
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
- 缓存更新：<code>scheduleUpdateIfAbsent</code>（启动 6 秒定时轮询）</td>
</tr>
<tr>
<td>缓存逻辑</td>
<td>① 优先查本地缓存（<code>serviceInfoMap</code>）；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
② 缓存未命中时，同步调用<code>updateServiceNow</code>查服务端并填充缓存；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
③ 最终必然返回缓存数据；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
④ 故障时降级读<code>failover</code>本地故障文件；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
⑤ 无 &ldquo;服务端查询失败用缓存&rdquo; 逻辑（缓存未命中时查服务端失败直接抛异常）</td>
</tr>
<tr>
<td>底层本质</td>
<td>HTTP 短连接下的「缓存优先 + 客户端定时轮询保鲜」，缓存是<strong>主数据来源</strong>，服务端查询仅为缓存初始化兜底</td>
</tr>
</tbody>
</table>
</div>
</div>
<h4 class="header-iWP5WJ auto-hide-last-sibling-br">2. 非订阅模式（subscribe=false）</h4>
<div class="auto-hide-last-sibling-br mdbox-table-root table-container-cYf_5N" data-scroll-inline-overflow="false" data-scroll-inline-at-start="true" data-scroll-inline-start-overflow="false" data-scroll-inline-at-end="true" data-scroll-inline-end-overflow="false">
<div class="table-scroll-container-Gyf4hQ mdbox-table-scroll-container">
<table>
<thead>
<tr>
<th>维度</th>
<th>核心细节</th>
</tr>
</thead>
<tbody>
<tr>
<td>核心方法</td>
<td>- 入口：<code>selectInstances</code>&nbsp;&rarr;&nbsp;<code>hostReactor.getServiceInfoDirectlyFromServer</code>
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
- 底层：<code>namingProxy.queryInstancesOfService</code>（HTTP 查服务端）</td>
</tr>
<tr>
<td>缓存逻辑</td>
<td>① 完全忽略本地缓存，直接 HTTP 查服务端；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
② 查询结果<strong>顺带存入缓存</strong>（但不启动定时轮询）；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
③ 无降级逻辑：服务端查询失败直接抛异常，不会读取缓存；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
④ 下次非订阅查询仍直接查服务端，缓存无实际作用</td>
</tr>
<tr>
<td>底层本质</td>
<td>单次 HTTP 短连接查询，追求 &ldquo;单次实时性&rdquo;，缓存仅为 &ldquo;临时存储&rdquo;，无持续更新</td>
</tr>
</tbody>
</table>
</div>
</div>
<h3 class="header-iWP5WJ auto-hide-last-sibling-br">三、Nacos 2.X 订阅 / 非订阅模式汇总</h3>
<h4 class="header-iWP5WJ auto-hide-last-sibling-br">1. 订阅模式（subscribe=true，默认）</h4>
<div class="auto-hide-last-sibling-br mdbox-table-root table-container-cYf_5N" data-scroll-inline-overflow="false" data-scroll-inline-at-start="true" data-scroll-inline-start-overflow="false" data-scroll-inline-at-end="true" data-scroll-inline-end-overflow="false">
<div class="table-scroll-container-Gyf4hQ mdbox-table-scroll-container">
<table>
<thead>
<tr>
<th>维度</th>
<th>核心细节</th>
</tr>
</thead>
<tbody>
<tr>
<td>核心方法</td>
<td>- 入口：<code>selectInstances</code>&nbsp;&rarr;&nbsp;<code>serviceInfoHolder.getServiceInfo</code>
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
- 缓存未命中：<code>clientProxy.subscribe</code>（gRPC 初始化订阅）
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
- 增量更新：服务端 gRPC 推送（无需客户端轮询）</td>
</tr>
<tr>
<td>缓存逻辑</td>
<td>① 优先查本地缓存（<code>serviceInfoHolder</code>）；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
② 缓存未命中时，调用<code>subscribe</code>（建立 gRPC 长连接 + 拉取服务端全量数据填充缓存）；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
③ 后续服务端实例变更主动推送增量数据更新缓存；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
④ 高版本增加 &ldquo;缓存时效性校验&rdquo;：异步更新过期缓存，仍优先返回缓存；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
⑤ 最终必然返回缓存数据</td>
</tr>
<tr>
<td>底层本质</td>
<td>gRPC 长连接下的「缓存优先 + 服务端主动推送」，缓存是<strong>主数据来源</strong>，全量拉取仅初始化，增量推送保证实时性</td>
</tr>
</tbody>
</table>
</div>
</div>
<h4 class="header-iWP5WJ auto-hide-last-sibling-br">2. 非订阅模式（subscribe=false）</h4>
<div class="auto-hide-last-sibling-br mdbox-table-root table-container-cYf_5N" data-scroll-inline-overflow="false" data-scroll-inline-at-start="true" data-scroll-inline-start-overflow="false" data-scroll-inline-at-end="true" data-scroll-inline-end-overflow="false">
<div class="table-scroll-container-Gyf4hQ mdbox-table-scroll-container">
<table>
<thead>
<tr>
<th>维度</th>
<th>核心细节</th>
</tr>
</thead>
<tbody>
<tr>
<td>核心方法</td>
<td>- 入口：<code>selectInstances</code>&nbsp;&rarr;&nbsp;<code>clientProxy.queryInstancesOfService</code>
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
- 底层：优先 gRPC、备 HTTP 查服务端</td>
</tr>
<tr>
<td>缓存逻辑</td>
<td>① 直接查服务端最新数据（忽略缓存）；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
② 查询成功后，更新本地缓存；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
③ 服务端查询失败时，<strong>降级读取缓存</strong>（容错设计）；
<div class="container-Uxvbjy md-box-line-break wrapper-GYqxgQ undefined">&nbsp;</div>
④ 无定时 / 推送更新缓存</td>
</tr>
<tr>
<td>底层本质</td>
<td>单次 gRPC/HTTP 查询，追求 &ldquo;单次实时性 + 容错&rdquo;，缓存作为<strong>降级兜底</strong></td>
</tr>
</tbody>
</table>
</div>
</div>
<h3 class="header-iWP5WJ auto-hide-last-sibling-br">四、核心差异对比表（快速查阅）</h3>
<div class="auto-hide-last-sibling-br mdbox-table-root table-container-cYf_5N" data-scroll-inline-overflow="true" data-scroll-inline-at-start="true" data-scroll-inline-start-overflow="false" data-scroll-inline-at-end="false" data-scroll-inline-end-overflow="true">
<div class="table-scroll-container-Gyf4hQ mdbox-table-scroll-container">
<table>
<thead>
<tr>
<th>版本</th>
<th>模式</th>
<th>核心方法</th>
<th>缓存核心逻辑</th>
<th>底层本质</th>
</tr>
</thead>
<tbody>
<tr>
<td>1.4</td>
<td>订阅</td>
<td>hostReactor.getServiceInfo</td>
<td>缓存优先，未命中查服务端，定时轮询更新</td>
<td>HTTP 短连接 + 客户端定时轮询</td>
</tr>
<tr>
<td>1.4</td>
<td>非订阅</td>
<td>hostReactor.getServiceInfoDirectlyFromServer</td>
<td>直接查服务端，缓存仅临时存储，无降级</td>
<td>单次 HTTP 查询，无容错</td>
</tr>
<tr>
<td>2.X</td>
<td>订阅</td>
<td>serviceInfoHolder.getServiceInfo + clientProxy.subscribe</td>
<td>缓存优先，未命中初始化 gRPC 订阅，服务端推送更新</td>
<td>gRPC 长连接 + 服务端主动推送</td>
</tr>
<tr>
<td>2.X</td>
<td>非订阅</td>
<td>clientProxy.queryInstancesOfService</td>
<td>直接查服务端，成功更缓存，失败用缓存</td>
<td>单次 gRPC/HTTP 查询，容错降级</td>
</tr>
</tbody>
</table>
</div>
</div>
<h3 class="header-iWP5WJ auto-hide-last-sibling-br">总结</h3>
<ol class="auto-hide-last-sibling-br">
<li><strong>订阅模式（默认）</strong>：1.4 和 2.X 均为「缓存优先」，核心差异是 &ldquo;缓存更新方式&rdquo;（1.4 客户端轮询 vs 2.X 服务端推送）；</li>
<li><strong>非订阅模式</strong>：核心差异是 &ldquo;容错逻辑&rdquo;（1.4 无降级，失败抛异常；2.X 失败降级用缓存）；</li>
<li>缓存角色：订阅模式下缓存是 &ldquo;主数据来源&rdquo;，非订阅模式下 1.4 缓存仅 &ldquo;临时存储&rdquo;，2.X 缓存是 &ldquo;降级兜底&rdquo;。</li>
</ol>]]></description>
    <pubDate>Fri, 19 Dec 2025 22:31:52 +0800</pubDate>
    <dc:creator>ZealSinger</dc:creator>
    <guid>http://8.154.40.120:24180/?post=49</guid>
</item>
<item>
    <title>Nacos源码学习计划-Day20-Nacos2.x-服务端处理客户端gRPC注册请求</title>
    <link>http://8.154.40.120:24180/?post=47</link>
    <description><![CDATA[<h1 class="md-end-block md-heading md-focus"><span class="md-plain md-expand">Nacos2.x服务端处理gRPC注册请求</span></h1>
<p class="md-end-block md-p"><span class="md-plain">上一章节我们分析了Nacos2.X的客户端中是如何利用gRPC发起注册请求的，那么接下来自然就是找服务端这边对于该注册请求的处理。</span></p>
<p class="md-end-block md-p"><span class="md-plain">我们可以看到，最终发出请求的方法</span><span class="md-pair-s" spellcheck="false"><code>doRegisterService</code></span><span class="md-plain">，其代码回顾一下如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">doRegisterService</span>(<span class="cm-variable-3">String</span> <span class="cm-variable">serviceName</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">groupName</span>, <span class="cm-variable">Instance</span> <span class="cm-variable">instance</span>) <span class="cm-keyword">throws</span> <span class="cm-variable">NacosException</span> {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; <span class="cm-variable">InstanceRequest</span> <span class="cm-variable">request</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">InstanceRequest</span>(<span class="cm-variable">namespaceId</span>, <span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">NamingRemoteConstants</span>.<span class="cm-variable">REGISTER_INSTANCE</span>, <span class="cm-variable">instance</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; <span class="cm-variable">requestToServer</span>(<span class="cm-variable">request</span>, <span class="cm-variable">Response</span>.<span class="cm-keyword">class</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; <span class="cm-variable">redoService</span>.<span class="cm-variable">instanceRegistered</span>(<span class="cm-variable">serviceName</span>, <span class="cm-variable">groupName</span>);</span><br><span role="presentation"> }</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到，这个请求体request的数据类型是</span><span class="md-pair-s" spellcheck="false"><code>InstanceRequest</code></span><span class="md-plain">，那么自然，服务端这边找到对应的处理入口，肯定也是需要处理同样的数据类型，利用Idea的全局搜索，我们很快找到了这个Request的对应的Handler处理器Bean</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251216211546699.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251216211546699.png" alt="image-20251216211546699"></span></p>
<p class="md-end-block md-p"><span class="md-plain">很明显，</span><strong><span class="md-plain">我们现在要看</span><span class="md-pair-s" spellcheck="false"><code>handle()</code></span><span class="md-plain">方法中的逻辑，可以看到，这个方法的逻辑首先根据request中的一些命名空间，groupName组名称和serviceName服务名称构建了一个Service对象，且通过这个条链路注册的实例都是默认ephemeral为true,即临时实例</span></strong></p>
<p class="md-end-block md-p"><span class="md-plain">然后就是就是根据</span><span class="md-pair-s" spellcheck="false"><code>request中的type字段</code></span><span class="md-plain">进行switch-case判断，我们回过头看一下，就知道我们这里应该进入到第一个case的逻辑中</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251216213557080.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251216213557080.png" alt="image-20251216213557080"></span></p>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>registerInstance()</code></span><span class="md-plain">的逻辑如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable">InstanceResponse</span> <span class="cm-def">registerInstance</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable">InstanceRequest</span> <span class="cm-variable">request</span>, <span class="cm-variable">RequestMeta</span> <span class="cm-variable">meta</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 调用了注册方法，在这里我们要注意这些参数：</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// service 就是我们刚刚在 swtich 之前创建的一个新的 service 对象，里面有命名空间、分组名、服务名</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// request.getInstance() 这个就是注册到对应客户端的Instance实例对象（微服务对象），里面肯定有 服务ip、端口等信息</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// meta.getConnectionId() 这个很关键，连接Id，meta里面是请求客户端的元数据，客户端的一些IP信息</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">clientOperationService</span>.<span class="cm-variable">registerInstance</span>(<span class="cm-variable">service</span>, <span class="cm-variable">request</span>.<span class="cm-variable">getInstance</span>(), <span class="cm-variable">meta</span>.<span class="cm-variable">getConnectionId</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-keyword">new</span> <span class="cm-variable">InstanceResponse</span>(<span class="cm-variable">NamingRemoteConstants</span>.<span class="cm-variable">REGISTER_INSTANCE</span>);</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到，实际上的逻辑还是</span><span class="md-pair-s" spellcheck="false"><code>clientOperationService.registerInstance()方法</code></span><span class="md-plain">，所以我们来分析这个方法</span></p>
<h2 class="md-end-block md-heading"><span class="md-plain">registerInstance方法分析</span></h2>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">void</span> <span class="cm-def">registerInstance</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable">Instance</span> <span class="cm-variable">instance</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Service</span> <span class="cm-variable">singleton</span> <span class="cm-operator">=</span> <span class="cm-variable">ServiceManager</span>.<span class="cm-variable">getInstance</span>().<span class="cm-variable">getSingleton</span>(<span class="cm-variable">service</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">singleton</span>.<span class="cm-variable">isEphemeral</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">throw</span> <span class="cm-keyword">new</span> <span class="cm-variable">NacosRuntimeException</span>(<span class="cm-variable">NacosException</span>.<span class="cm-variable">INVALID_PARAM</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable-3">String</span>.<span class="cm-variable">format</span>(<span class="cm-string">"Current service %s is persistent service, can't register ephemeral instance."</span>,</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">singleton</span>.<span class="cm-variable">getGroupedServiceName</span>()));</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Client</span> <span class="cm-variable">client</span> <span class="cm-operator">=</span> <span class="cm-variable">clientManager</span>.<span class="cm-variable">getClient</span>(<span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">clientIsLegal</span>(<span class="cm-variable">client</span>, <span class="cm-variable">clientId</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">InstancePublishInfo</span> <span class="cm-variable">instanceInfo</span> <span class="cm-operator">=</span> <span class="cm-variable">getPublishInfo</span>(<span class="cm-variable">instance</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">client</span>.<span class="cm-variable">addServiceInstance</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">instanceInfo</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">client</span>.<span class="cm-variable">setLastUpdatedTime</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">ClientOperationEvent</span>.<span class="cm-variable">ClientRegisterServiceEvent</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">clientId</span>));</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  .<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">MetadataEvent</span>.<span class="cm-variable">InstanceMetadataEvent</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">instanceInfo</span>.<span class="cm-variable">getMetadataId</span>(), <span class="cm-atom">false</span>));</span><br><span role="presentation">}</span></pre>
<h3 class="md-end-block md-heading"><span class="md-plain">getSingleton()方法</span></h3>
<p class="md-end-block md-p"><span class="md-plain">可以看到第一行，就是通过</span><span class="md-pair-s" spellcheck="false"><code>getSingleton()</code></span><span class="md-plain">方法获取到Service实例</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-variable">Service</span> <span class="cm-variable">singleton</span> <span class="cm-operator">=</span> <span class="cm-variable">ServiceManager</span>.<span class="cm-variable">getInstance</span>().<span class="cm-variable">getSingleton</span>(<span class="cm-variable">service</span>);</span></pre>
<p class="md-end-block md-p"><span class="md-plain">其方法实现如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;</span><span class="cm-variable">Service</span>, <span class="cm-variable">Service</span><span class="cm-operator">&gt;</span> <span class="cm-variable">singletonRepository</span>;</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span>, <span class="cm-variable">Set</span><span class="cm-operator">&lt;</span><span class="cm-variable">Service</span><span class="cm-operator">&gt;&gt;</span> <span class="cm-variable">namespaceSingletonMaps</span>;</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">Service</span> <span class="cm-def">getSingleton</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 添加一个 service，如果存在就不添加</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">singletonRepository</span>.<span class="cm-variable">putIfAbsent</span>(<span class="cm-variable">service</span>, <span class="cm-variable">service</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 然后从这个 Map 中把 Service 取出来</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Service</span> <span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-variable">singletonRepository</span>.<span class="cm-variable">get</span>(<span class="cm-variable">service</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 以 命名空间 为 key，把相同命名空间的 service 加入到 namespaceSingletonMaps 当中,这个map中保存的也是同一个命名空间下的所有的Service</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">namespaceSingletonMaps</span>.<span class="cm-variable">computeIfAbsent</span>(<span class="cm-variable">result</span>.<span class="cm-variable">getNamespace</span>(), (<span class="cm-variable">namespace</span>) <span class="cm-operator">-&gt;</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashSet</span><span class="cm-operator">&lt;&gt;</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">namespaceSingletonMaps</span>.<span class="cm-variable">get</span>(<span class="cm-variable">result</span>.<span class="cm-variable">getNamespace</span>()).<span class="cm-variable">add</span>(<span class="cm-variable">result</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">result</span>;</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到，在Nacos 2.x版本中，</span><strong><span class="md-plain">把 service 和命名空间拆分成了两个 Map，回想一下在Nacos 1.4.X版本中，是不是进行的Map的嵌套，Service是嵌套在Group所在的Map中的，这里也是Nacos 2.X和1.4之间的内部注册表的结构上的一个明显的差异</span></strong></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20250909233156423.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20250909233156423.png" alt="image-20250909233156423"></span></p>
<div class="md-hr md-end-block" tabindex="-1"><hr></div>
<p class="md-end-block md-p"><span class="md-plain">获取到对应的Service之后，判断这个Service是否是一个持久化的实例，</span><strong><span class="md-plain">如果是持久化的实例，则会抛出异常从而不准注册</span></strong></p>
<p class="md-end-block md-p"><span class="md-plain">这个其实在我们第一篇章说到Nacos2.x的变化的时候也提到过,Nacos 2.X版本不会允许同一个服务同时存在持久化实例和非持久化实例</span></p>
<blockquote>
<p class="md-end-block md-p"><span class="md-plain">Nacos在1.x版本中允许一个服务同时存在持久化和非持久化实例，持久化属性只是作为实例的一个元数据进行存储和识别，这就导致了实际情况下运维人员很苦恼且从系统架构层面看来存在矛盾。所以在Nacos 2.x中简化了Nacos的服务数据模型，</span><strong><span class="md-plain">是否持久化的数据抽象至服务级别且不再允许一个服务同时存在持久化和非持久化实例，实例的是否持久化属性配置继承服务的是否持久化属性配置</span></strong></p>
</blockquote>
<p class="md-end-block md-p"><span class="md-plain">所以这里的判断就是，如果该服务已经注册了一个持久化实例，本请求链路是注册的非持久化实例，自然是不允许同时注册的，所以抛出异常。</span></p>
<div class="md-hr md-end-block" tabindex="-1"><hr></div>
<h3 class="md-end-block md-heading"><span class="md-plain">getClient()方法</span></h3>
<p class="md-end-block md-p"><span class="md-plain">分支逻辑判断完成之后获取</span><span class="md-pair-s" spellcheck="false"><code>Client对象</code></span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-variable">Client</span> <span class="cm-variable">client</span> <span class="cm-operator">=</span> <span class="cm-variable">clientManager</span>.<span class="cm-variable">getClient</span>(<span class="cm-variable">clientId</span>);</span></pre>
<p class="md-end-block md-p"><span class="md-plain">方法逻辑如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentMap</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span>, <span class="cm-variable">IpPortBasedClient</span><span class="cm-operator">&gt;</span> <span class="cm-variable">clients</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation">​</span><br><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable">Client</span> <span class="cm-def">getClient</span>(<span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">clients</span>.<span class="cm-variable">get</span>(<span class="cm-variable">clientId</span>);</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">这个地方稍微需要一点Netty的知识，所以我们简单概况一下，当我们Nacos底层的gRPC实际上也是Netty，当客户端和服务端之间使用Netty建立连接的时候，服务端内部会创建一个socketChannel对象用于管理这条连接，每和一个独特的客户端的连接都会对应唯一一个socketChannel，在 socketChannel 对象的基础之上，Nacos 又封装了一层 Client 对象，并且会生成一个 connectionId 把它们关联起来</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251216230146399.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251216230146399.png" alt="image-20251216230146399"></span></p>
<p class="md-end-block md-p"><span class="md-plain">所以这一步就是利用这个id进行获取对应的客户端</span></p>
<div class="md-hr md-end-block" tabindex="-1"><hr></div>
<p class="md-end-block md-p"><span class="md-plain">然后接下来的逻辑如下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">clientIsLegal</span>(<span class="cm-variable">client</span>, <span class="cm-variable">clientId</span>)) {</span><br><span role="presentation">    <span class="cm-keyword">return</span>;</span><br><span role="presentation">}</span><br><span role="presentation"><span class="cm-variable">InstancePublishInfo</span> <span class="cm-variable">instanceInfo</span> <span class="cm-operator">=</span> <span class="cm-variable">getPublishInfo</span>(<span class="cm-variable">instance</span>);</span></pre>
<p class="md-end-block md-p"><span class="md-pair-s" spellcheck="false"><code>clientIsLegal</code></span><span class="md-plain">方法的逻辑如下，其实也没什么很好说的，就是一些对于client的判空和是否持久化的判断</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">clientIsLegal</span>(<span class="cm-variable">Client</span> <span class="cm-variable">client</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">client</span> <span class="cm-operator">==</span> <span class="cm-atom">null</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">SRV_LOG</span>.<span class="cm-variable">warn</span>(<span class="cm-string">"Client connection {} already disconnect"</span>, <span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">false</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">client</span>.<span class="cm-variable">isEphemeral</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">SRV_LOG</span>.<span class="cm-variable">warn</span>(<span class="cm-string">"Client connection {} type is not ephemeral"</span>, <span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">false</span>;</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><strong><span class="md-pair-s" spellcheck="false"><code>getPublishInfo(instance)</code></span><span class="md-plain">方法的逻辑，也没什么很好说的，不要看代码好像还不少，其实就是将入参Instance中的对象，经过一些判断和处理，封装到了</span><span class="md-pair-s" spellcheck="false"><code>InstancePublishInfo</code></span><span class="md-plain">对象中</span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">default</span> <span class="cm-variable">InstancePublishInfo</span> <span class="cm-def">getPublishInfo</span>(<span class="cm-variable">Instance</span> <span class="cm-variable">instance</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">InstancePublishInfo</span> <span class="cm-variable">result</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">InstancePublishInfo</span>(<span class="cm-variable">instance</span>.<span class="cm-variable">getIp</span>(), <span class="cm-variable">instance</span>.<span class="cm-variable">getPort</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Map</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span>, <span class="cm-variable-3">Object</span><span class="cm-operator">&gt;</span> <span class="cm-variable">extendDatum</span> <span class="cm-operator">=</span> <span class="cm-variable">result</span>.<span class="cm-variable">getExtendDatum</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">!=</span> <span class="cm-variable">instance</span>.<span class="cm-variable">getMetadata</span>() <span class="cm-operator">&amp;&amp;</span> <span class="cm-operator">!</span><span class="cm-variable">instance</span>.<span class="cm-variable">getMetadata</span>().<span class="cm-variable">isEmpty</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">extendDatum</span>.<span class="cm-variable">putAll</span>(<span class="cm-variable">instance</span>.<span class="cm-variable">getMetadata</span>());</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">StringUtils</span>.<span class="cm-variable">isNotEmpty</span>(<span class="cm-variable">instance</span>.<span class="cm-variable">getInstanceId</span>())) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">extendDatum</span>.<span class="cm-variable">put</span>(<span class="cm-variable">Constants</span>.<span class="cm-variable">CUSTOM_INSTANCE_ID</span>, <span class="cm-variable">instance</span>.<span class="cm-variable">getInstanceId</span>());</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">Constants</span>.<span class="cm-variable">DEFAULT_INSTANCE_WEIGHT</span> <span class="cm-operator">!=</span> <span class="cm-variable">instance</span>.<span class="cm-variable">getWeight</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">extendDatum</span>.<span class="cm-variable">put</span>(<span class="cm-variable">Constants</span>.<span class="cm-variable">PUBLISH_INSTANCE_WEIGHT</span>, <span class="cm-variable">instance</span>.<span class="cm-variable">getWeight</span>());</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-operator">!</span><span class="cm-variable">instance</span>.<span class="cm-variable">isEnabled</span>()) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">extendDatum</span>.<span class="cm-variable">put</span>(<span class="cm-variable">Constants</span>.<span class="cm-variable">PUBLISH_INSTANCE_ENABLE</span>, <span class="cm-variable">instance</span>.<span class="cm-variable">isEnabled</span>());</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">clusterName</span> <span class="cm-operator">=</span> <span class="cm-variable">StringUtils</span>.<span class="cm-variable">isBlank</span>(<span class="cm-variable">instance</span>.<span class="cm-variable">getClusterName</span>()) <span class="cm-operator">?</span> <span class="cm-variable">UtilsAndCommons</span>.<span class="cm-variable">DEFAULT_CLUSTER_NAME</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  : <span class="cm-variable">instance</span>.<span class="cm-variable">getClusterName</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">result</span>.<span class="cm-variable">setHealthy</span>(<span class="cm-variable">instance</span>.<span class="cm-variable">isHealthy</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">result</span>.<span class="cm-variable">setCluster</span>(<span class="cm-variable">clusterName</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-variable">result</span>;</span><br><span role="presentation">}</span></pre>
<div class="md-hr md-end-block" tabindex="-1"><hr></div>
<h3 class="md-end-block md-heading"><span class="md-plain">addServiceInstance()方法</span></h3>
<p class="md-end-block md-p"><span class="md-plain">接下来就是</span><span class="md-pair-s" spellcheck="false"><code>addServiceInstance(singleton, instanceInfo);</code></span><span class="md-plain">方法，这行的逻辑比较重要，可以来分析一下</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-variable">client</span>.<span class="cm-variable">addServiceInstance</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">instanceInfo</span>);</span></pre>
<p class="md-end-block md-p"><span class="md-plain">同样的，这里是调用的client对象中的方法，这里得看不同的实现类从而有不同的逻辑，好在的是</span><span class="md-pair-s" spellcheck="false"><code>addServiceInstance</code></span><span class="md-plain">只有两个对应的实现方法</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251217202253858.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251217202253858.png" alt="image-20251217202253858"></span></p>
<p class="md-end-block md-p"><span class="md-plain">我们先来看看</span><span class="md-pair-s" spellcheck="false"><code>AbstractClient</code></span><span class="md-plain">中的实现</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">protected</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;</span><span class="cm-variable">Service</span>, <span class="cm-variable">InstancePublishInfo</span><span class="cm-operator">&gt;</span> <span class="cm-variable">publishers</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;&gt;</span>(<span class="cm-number">16</span>, <span class="cm-number">0.75f</span>, <span class="cm-number">1</span>);</span><br><span role="presentation">​</span><br><span class="cm-meta">@Override</span><br><span role="presentation"><span class="cm-keyword">public</span> <span class="cm-variable-3">boolean</span> <span class="cm-def">addServiceInstance</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable">InstancePublishInfo</span> <span class="cm-variable">instancePublishInfo</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 将其放入到一个内部的Maap中，根据put的返回值判断是否第一次放入该Service，如果为null则说明第一次放入</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-atom">null</span> <span class="cm-operator">==</span> <span class="cm-variable">publishers</span>.<span class="cm-variable">put</span>(<span class="cm-variable">service</span>, <span class="cm-variable">instancePublishInfo</span>)) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-comment">// 第一次放入的就需要内部计数器递增一下进行记录，这个方法底层就是个原子Int增加</span></span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">MetricsMonitor</span>.<span class="cm-variable">incrementInstanceCount</span>();</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 发布事件 事件类型ClientChangedEvent 这个和集群同步相关 暂且先不进行详细解释</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">ClientEvent</span>.<span class="cm-variable">ClientChangedEvent</span>(<span class="cm-keyword">this</span>));</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Loggers</span>.<span class="cm-variable">SRV_LOG</span>.<span class="cm-variable">info</span>(<span class="cm-string">"Client change for service {}, {}"</span>, <span class="cm-variable">service</span>, <span class="cm-variable">getClientId</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">return</span> <span class="cm-atom">true</span>;</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">以至于另外一个实现类</span><span class="md-pair-s" spellcheck="false"><code>IpPortBaseClient</code></span><span class="md-plain">中的</span><span class="md-pair-s" spellcheck="false"><code>addServiceInstance</code></span><span class="md-plain">，其实也是直接用的父类中实现，没有什么需要额外分析的</span></p>
<div class="md-hr md-end-block" tabindex="-1"><hr></div>
<p class="md-end-block md-p"><span class="md-plain">最后几行代码，可以很直观的知道，其实也是和事件发布有关，我们接下来就是分析这些发布的事件</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-variable">client</span>.<span class="cm-variable">setLastUpdatedTime</span>(); &nbsp;<span class="cm-comment">//更新时间</span></span><br><span class="cm-comment">// 发布ClientRegisterServiceEvent事件</span><br><span role="presentation"><span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">ClientOperationEvent</span>.<span class="cm-variable">ClientRegisterServiceEvent</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">clientId</span>));</span><br><span role="presentation">​</span><br><span class="cm-comment">// 发布InstanceMetadataEvent事件</span><br><span class="cm-variable">NotifyCenter</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp;  .<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">MetadataEvent</span>.<span class="cm-variable">InstanceMetadataEvent</span>(<span class="cm-variable">singleton</span>, <span class="cm-variable">instanceInfo</span>.<span class="cm-variable">getMetadataId</span>(), <span class="cm-atom">false</span>));</span><br><span role="presentation">​</span></pre>
<h2 class="md-end-block md-heading"><span class="md-plain">ClientRegisterServiceEvent事件</span></h2>
<p class="md-end-block md-p"><span class="md-plain">既然我们是和服务注册相关，</span><strong><span class="md-plain">自然首先看</span><span class="md-pair-s" spellcheck="false"><code>ClientRegisterServiceEvent事件</code></span></strong><span class="md-plain">，（我们这里不会介绍InstanceMetadataEvent事件的处理，感兴趣的可以自己点进去看看，和实例的元数据保存有关的，会记录元数据的过期与否，底层也就是对于一个Set进行操作。没过期则加入反之则remove）分析发布事件的相关逻辑，其实就是看两个点</span></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-plain">哪里接受和处理这个事件</span></strong></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-plain">这个事件的逻辑是啥</span></strong></p>
</li>
</ul>
<p class="md-end-block md-p"><span class="md-plain">那我们先找到在哪里接受和处理这个事件类型，依旧可以通过Idea的全局搜索进行查找</span></p>
<p class="md-end-block md-p"><span class="md-plain">可以看到，很多都是在</span><span class="md-pair-s" spellcheck="false"><code>new创建对象的时候</code></span><span class="md-plain">涉及到，这些地方肯定不是；除此之外就只有如下图所示的地方，出现了</span><span class="md-pair-s" spellcheck="false"><code>event instanceof</code></span><span class="md-plain">，这个地方很像对于接收到的event事件进行类型判断然后分类型进行处理，所以我们可以去看看这里的逻辑</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251217205003012.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251217205003012.png" alt="image-20251217205003012"></span></p>
<p class="md-end-block md-p"><span class="md-plain">可以从这里的代码结构看出来，就是一种类似事件中心的处理结构，Event都进入到这个方法，然后根据不同的事件类型进行处理</span></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable-3">void</span> <span class="cm-def">handleClientOperation</span>(<span class="cm-variable">ClientOperationEvent</span> <span class="cm-variable">event</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">Service</span> <span class="cm-variable">service</span> <span class="cm-operator">=</span> <span class="cm-variable">event</span>.<span class="cm-variable">getService</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span> <span class="cm-operator">=</span> <span class="cm-variable">event</span>.<span class="cm-variable">getClientId</span>();</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 处理客户端注册事件</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-keyword">if</span> (<span class="cm-variable">event</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">ClientOperationEvent</span>.<span class="cm-variable">ClientRegisterServiceEvent</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">addPublisherIndexes</span>(<span class="cm-variable">service</span>, <span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 处理客户端注销事件</span></span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">else</span> <span class="cm-keyword">if</span> (<span class="cm-variable">event</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">ClientOperationEvent</span>.<span class="cm-variable">ClientDeregisterServiceEvent</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">removePublisherIndexes</span>(<span class="cm-variable">service</span>, <span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 处理客户端订阅事件</span></span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">else</span> <span class="cm-keyword">if</span> (<span class="cm-variable">event</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">ClientOperationEvent</span>.<span class="cm-variable">ClientSubscribeServiceEvent</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">addSubscriberIndexes</span>(<span class="cm-variable">service</span>, <span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 处理客户端取消订阅事件</span></span><br><span role="presentation"> &nbsp;  } <span class="cm-keyword">else</span> <span class="cm-keyword">if</span> (<span class="cm-variable">event</span> <span class="cm-keyword">instanceof</span> <span class="cm-variable">ClientOperationEvent</span>.<span class="cm-variable">ClientUnsubscribeServiceEvent</span>) {</span><br><span role="presentation"> &nbsp; &nbsp; &nbsp; &nbsp;<span class="cm-variable">removeSubscriberIndexes</span>(<span class="cm-variable">service</span>, <span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp;  }</span><br><span role="presentation">}</span><br><span role="presentation">​</span></pre>
<p class="md-end-block md-p"><span class="md-plain">可以看到，</span><strong><span class="md-plain">其逻辑下调用的是</span><span class="md-pair-s" spellcheck="false"><code>addPublisherIndexes()</code></span><span class="md-plain">方法，而</span><span class="md-pair-s" spellcheck="false"><code>addPublisherIndexes()</code></span><span class="md-plain">方法</span></strong></p>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" spellcheck="false"><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-keyword">final</span> <span class="cm-variable">ConcurrentMap</span><span class="cm-operator">&lt;</span><span class="cm-variable">Service</span>, <span class="cm-variable">Set</span><span class="cm-operator">&lt;</span><span class="cm-variable-3">String</span><span class="cm-operator">&gt;&gt;</span> <span class="cm-variable">publisherIndexes</span> <span class="cm-operator">=</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashMap</span><span class="cm-operator">&lt;&gt;</span>();</span><br><span role="presentation">​</span><br><span role="presentation"><span class="cm-keyword">private</span> <span class="cm-variable-3">void</span> <span class="cm-def">addPublisherIndexes</span>(<span class="cm-variable">Service</span> <span class="cm-variable">service</span>, <span class="cm-variable-3">String</span> <span class="cm-variable">clientId</span>) {</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 这里可以看到 publisherIndexes 存储了 &lt;Service,Set&lt;clientId&gt;&gt;的对应结构，通过clientId自然能找到client，client中对应的有Instance的信息</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">publisherIndexes</span>.<span class="cm-variable">computeIfAbsent</span>(<span class="cm-variable">service</span>, (<span class="cm-variable">key</span>) <span class="cm-operator">-&gt;</span> <span class="cm-keyword">new</span> <span class="cm-variable">ConcurrentHashSet</span><span class="cm-operator">&lt;&gt;</span>());</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">publisherIndexes</span>.<span class="cm-variable">get</span>(<span class="cm-variable">service</span>).<span class="cm-variable">add</span>(<span class="cm-variable">clientId</span>);</span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-comment">// 发布ServiceChangedEvent  服务改变事件</span></span><br><span role="presentation"> &nbsp; &nbsp;<span class="cm-variable">NotifyCenter</span>.<span class="cm-variable">publishEvent</span>(<span class="cm-keyword">new</span> <span class="cm-variable">ServiceEvent</span>.<span class="cm-variable">ServiceChangedEvent</span>(<span class="cm-variable">service</span>, <span class="cm-atom">true</span>));</span><br><span role="presentation">}</span></pre>
<p class="md-end-block md-p"><span class="md-plain">继续往下看</span><strong><span class="md-plain">就是</span><span class="md-pair-s" spellcheck="false"><code>publishEvent</code></span><span class="md-plain">的逻辑</span></strong><span class="md-plain">，但是我们通过事件的类型就可以判断，下面的内容属于服务改变才会触发的事件，而我们当前要追踪的是服务注册事件，自然而言下面的其实不属于我们今天的主线任务</span></p>
<p class="md-end-block md-p">&nbsp;</p>
<h1 class="md-end-block md-heading"><span class="md-plain">总结</span></h1>
<p class="md-end-block md-p"><span class="md-plain">到这里，我们今天的分析其实就结束了，可能大家会比较恍惚啊，可能只记得出现了很多个Map，但是没能理清楚各种Map之间的关系，因为Nacos2.x版本中的设计，不是简单的Map嵌套了，而是采用扁平化的设计，减少层级开销，我们再来总结一下这个流程中涉及到的map以及之间的关系</span></p>
<ol class="ol-list">
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-pair-s" spellcheck="false"><code>singletonRepository</code></span></strong></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">类型：</span><span class="md-pair-s" spellcheck="false"><code>ConcurrentHashMap&lt;Service, Service&gt;</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">作用：确保每个服务（Service）在全局唯一。</span></p>
<p class="md-end-block md-p"><span class="md-plain">当注册请求到来时，通过</span><span class="md-pair-s" spellcheck="false"><code>getSingleton(service)</code></span><span class="md-plain">方法，先尝试将服务对象放入该 Map（putIfAbsent），若已存在则直接返回现有实例，避免重复创建。</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">意义：Service 的唯一性是 Nacos2.x 的重要设计（例如不允许同一服务同时存在持久化和非持久化实例），此 Map 是这一规则的底层保障。</span></p>
</li>
</ul>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-pair-s" spellcheck="false"><code>namespaceSingletonMaps</code></span></strong></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">类型：</span><span class="md-pair-s" spellcheck="false"><code>ConcurrentHashMap&lt;String, Set&lt;Service&gt;&gt;</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">作用：按命名空间（namespace）对服务进行分组管理。</span></p>
<p class="md-end-block md-p"><span class="md-plain">Key 是命名空间 ID，Value 是该命名空间下所有 Service 的集合（ConcurrentHashSet）。每次获取或创建 Service 时，会将其加入对应命名空间的集合中。</span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">意义：实现服务的命名空间隔离，方便按命名空间快速查询所有服务（例如跨命名空间的服务不可见）。</span></p>
</li>
</ul>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-pair-s" spellcheck="false"><code>clients</code></span><span class="md-plain">（ClientManager 中）</span></strong></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">类型：</span><span class="md-pair-s" spellcheck="false"><code>ConcurrentMap&lt;String, IpPortBasedClient&gt;</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">作用：管理客户端连接与 Client 对象的映射。</span></p>
<p class="md-end-block md-p"><span class="md-plain">Key 是客户端连接 ID（connectionId，由 gRPC/Netty 连接生成），Value 是封装了客户端信息的IpPortBasedClient对象。每个客户端连接对应唯一 Client 对象。</span></p>
</li>
</ul>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-pair-s" spellcheck="false"><code>publishers</code></span><span class="md-plain">（Client 对象中）</span></strong></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">类型：</span><span class="md-pair-s" spellcheck="false"><code>ConcurrentHashMap&lt;Service, InstancePublishInfo&gt;</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">作用：记录当前客户端发布的所有实例信息。</span></p>
<p class="md-end-block md-p"><span class="md-plain">Key 是服务（Service），Value 是该服务下的实例详情（InstancePublishInfo，包含 IP、端口、元数据等）。当客户端注册实例时，会将实例信息存入此 Map。</span></p>
</li>
</ul>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><strong><span class="md-pair-s" spellcheck="false"><code>publisherIndexes</code></span></strong></p>
<ul class="ul-list" data-mark="-">
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">类型：</span><span class="md-pair-s" spellcheck="false"><code>ConcurrentMap&lt;Service, Set&lt;String&gt;&gt;</code></span></p>
</li>
<li class="md-list-item">
<p class="md-end-block md-p"><span class="md-plain">作用：反向索引服务与客户端的关联。</span></p>
<p class="md-end-block md-p"><span class="md-plain">Key 是服务（Service），Value 是发布该服务的所有客户端 ID（clientId）的集合。通过该结构可以快速找到某个服务对应的所有客户端，进而通过客户端 ID 从clients中获取实例信息。</span></p>
</li>
</ul>
</li>
</ol>
<p class="md-end-block md-p"><span class="md-plain">整体结构可以归纳为如下流程关系</span></p>
<p class="md-end-block md-p"><span class="md-image md-img-loaded" data-src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251218222223391.png"><img src="https://zealsinger-book-bucket.oss-cn-hangzhou.aliyuncs.com/img/image-20251218222223391.png" alt="image-20251218222223391"></span></p>
<p class="md-end-block md-p md-focus"><span class="md-plain md-expand">如果还有不明白的地方的话，后面分析实例查找啊，变更啊时候，我们会继续需要了解这个内存结构的</span></p>]]></description>
    <pubDate>Thu, 18 Dec 2025 22:23:12 +0800</pubDate>
    <dc:creator>ZealSinger</dc:creator>
    <guid>http://8.154.40.120:24180/?post=47</guid>
</item></channel>
</rss>