This commit is contained in:
周伟
2022-05-11 19:04:14 +08:00
parent 9440ac7291
commit d9c5ffd627
826 changed files with 0 additions and 481675 deletions

View File

@@ -25,13 +25,7 @@
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
@@ -55,289 +49,148 @@
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/深入理解 Sentinel/01 开篇词:一次服务雪崩问题排查经历.md.html">01 开篇词:一次服务雪崩问题排查经历.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/02 为什么需要服务降级以及常见的几种降级方式.md.html">02 为什么需要服务降级以及常见的几种降级方式.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/03 为什么选择 SentinelSentinel 与 Hystrix 的对比.md.html">03 为什么选择 SentinelSentinel 与 Hystrix 的对比.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/04 Sentinel 基于滑动窗口的实时指标数据统计.md.html">04 Sentinel 基于滑动窗口的实时指标数据统计.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/05 Sentinel 的一些概念与核心类介绍.md.html">05 Sentinel 的一些概念与核心类介绍.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/06 Sentinel 中的责任链模式与 Sentinel 的整体工作流程.md.html">06 Sentinel 中的责任链模式与 Sentinel 的整体工作流程.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/07 Java SPI 及 SPI 在 Sentinel 中的应用.md.html">07 Java SPI 及 SPI 在 Sentinel 中的应用.md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/深入理解 Sentinel/08 资源指标数据统计的实现全解析(上).md.html">08 资源指标数据统计的实现全解析(上).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/09 资源指标数据统计的实现全解析(下).md.html">09 资源指标数据统计的实现全解析(下).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/10 限流降级与流量效果控制器(上).md.html">10 限流降级与流量效果控制器(上).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/11 限流降级与流量效果控制器(中).md.html">11 限流降级与流量效果控制器(中).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/12 限流降级与流量效果控制器(下).md.html">12 限流降级与流量效果控制器(下).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/13 熔断降级与系统自适应限流.md.html">13 熔断降级与系统自适应限流.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/14 黑白名单限流与热点参数限流.md.html">14 黑白名单限流与热点参数限流.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/15 自定义 ProcessorSlot 实现开关降级.md.html">15 自定义 ProcessorSlot 实现开关降级.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/16 Sentinel 动态数据源:规则动态配置.md.html">16 Sentinel 动态数据源:规则动态配置.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/17 Sentinel 主流框架适配.md.html">17 Sentinel 主流框架适配.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/18 Sentinel 集群限流的实现(上).md.html">18 Sentinel 集群限流的实现(上).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/19 Sentinel 集群限流的实现(下).md.html">19 Sentinel 集群限流的实现(下).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/20 结束语Sentinel 对应用的性能影响如何?.md.html">20 结束语Sentinel 对应用的性能影响如何?.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/21 番外篇Sentinel 1.8.0 熔断降级新特性解读.md.html">21 番外篇Sentinel 1.8.0 熔断降级新特性解读.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
@@ -347,9 +200,6 @@
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
@@ -357,9 +207,6 @@
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
@@ -389,9 +236,6 @@
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
@@ -415,13 +259,7 @@ function hide_canvas() {
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
@@ -519,9 +357,6 @@ function hide_canvas() {
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
// 出口方法什么也不做
@Override
@@ -533,9 +368,6 @@ function hide_canvas() {
}
}
</code></pre>
<p>如源码所示map 字段是一个非静态字段,意味着每个 NodeSelectorSlot 都有一个 map。由于一个资源对应一个 ProcessorSlotChain而一个 ProcessorSlotChain 只创建一个 NodeSelectorSlot并且 map 缓存 DefaultNode 使用的 key 并非资源 ID而是 Context.name所以 map 的作用是缓存针对同一资源为不同调用链路入口创建的 DefaultNode。</p>
@@ -549,9 +381,6 @@ function hide_canvas() {
<p>NodeSelectorSlot#entry 方法最难以理解的就是实现绑定调用树这行代码:</p>
<pre><code class="language-java">((DefaultNode) context.getLastNode()).addChild(node);
</code></pre>
<p>这行代码分两种情况分析更容易理解,我们就以 Sentinel 提供的 Demo 为例进行分析。</p>
@@ -563,9 +392,6 @@ function hide_canvas() {
<pre><code class="language-java">@RestController
public class WebMvcTestController {
@GetMapping(&quot;/hello&quot;)
public String apiHello() throws BlockException {
@@ -577,9 +403,6 @@ public class WebMvcTestController {
}
}
</code></pre>
<p>我们不需要添加任何规则,只是为了调试 Sentinel 的源码。将 demo 启动起来后,在浏览器访问“/hello”接口在 NodeSelectorSlot#entry 方法的绑定调用树这一行代码下断点,观察此时 Context 的字段信息。正常情况下我们可以看到如下图所示的结果。</p>
@@ -603,9 +426,6 @@ public class WebMvcTestController {
/
DefaultNode resource name: GET:/hello
</code></pre>
<p>如果我们现在再访问 Demo 的其他接口,例如访问“/err”接口那么生成的调用树就会变成如下</p>
@@ -619,9 +439,6 @@ DefaultNode resource name: GET:/hello
/ \
DefaultNode resource name: GET:/hello DefaultNode resource name: GET:/err
</code></pre>
<p>Context.entranceNode 将会存储 Web 项目的所有资源(接口)的 DefaultNode。</p>
@@ -637,9 +454,6 @@ DefaultNode resource name: GET:/hello DefaultNode resource name: GE
<pre><code class="language-java">@RestController
public class WebMvcTestController {
@GetMapping(&quot;/hello&quot;)
public String apiHello() throws BlockException {
@@ -685,9 +499,6 @@ public class WebMvcTestController {
}
}
</code></pre>
<p>我们可将 doBusiness 方法看成是远程调用例如调用第三方的接口接口名称为“http://wujiuye.com/hello2”使用 POST 方式调用那么我们可以使用“POST:http://wujiuye.com/hello2”作为资源名称并将流量类型设置为 OUT 类型。上下文名称取名为&quot;my_context&quot;</p>
@@ -713,9 +524,6 @@ public class WebMvcTestController {
/
DefaultNode (POST:/hello2)
</code></pre>
<p>此时,当前调用链路上也已经存在两个 CtEntry这两个 CtEntry 构造一个双向链表,如下图所示。</p>
@@ -729,9 +537,6 @@ public class WebMvcTestController {
<pre><code class="language-java">// 替换 Context.curNode 为当前 DefaultNode
context.setCurNode(node);
</code></pre>
<p>替换 Context.curNode 为当前资源 DefaultNode 这行代码就是将当前创建的 DefaultNode 赋值给当前 CtEntry.curNode。对着上图理解就是将资源“GET:/hello”的 DefaultNode 赋值给第一个 CtEntry.curNode将资源“POST:http://wujiuye.com/hello2”的 DefaultNode 赋值给第二个 CtEntry.curNode。</p>
@@ -755,9 +560,6 @@ context.setCurNode(node);
}
}
</code></pre>
<p>这个例子虽然简单,但也足以说明 Sentinel 构造 CtEntry 双向链表的目的。</p>
@@ -781,15 +583,9 @@ context.setCurNode(node);
private static volatile Map&lt;ResourceWrapper, ClusterNode&gt; clusterNodeMap = new HashMap&lt;&gt;();
private static final Object lock = new Object();
// 非静态,一个资源对应一个 ProcessorSlotChain所以一个资源共用一个 ClusterNode
private volatile ClusterNode clusterNode = null;
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
@@ -841,9 +637,6 @@ context.setCurNode(node);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
@@ -853,9 +646,6 @@ context.setCurNode(node);
}
}
</code></pre>
<p>ClusterBuilderSlot 使用一个 Map 缓存资源的 ClusterNode并且用一个非静态的字段维护当前资源的 ClusterNode。因为一个资源只会创建一个 ProcessorSlotChain意味着 ClusterBuilderSlot 也只会创建一个,那么让 ClusterBuilderSlot 持有该资源的 ClusterNode 就可以省去每次都从 Map 中获取的步骤,这当然也是 Sentinel 为性能做出的努力。</p>
@@ -875,9 +665,6 @@ context.setCurNode(node);
private Map&lt;String, StatisticNode&gt; originCountMap = new HashMap&lt;&gt;();
}
</code></pre>
<p>如果上游服务在调用当前服务的接口传递 origin 字段过来,例如可在 http 请求头添加“S-user”参数或者 Dubbo rpc 调用在请求参数列表加上“application”参数那么 ClusterBuilderSlot 就会为 ClusterNode 创建一个 StatisticNode用来统计当前资源被远程服务调用的指标数据。</p>
@@ -925,9 +712,6 @@ context.setCurNode(node);
return statisticNode;
}
</code></pre>
<p>为了便于使用ClusterBuilderSlot 会将调用来源origin的 StatisticNode 赋值给 Context.curEntry.originNode后续的 ProcessorSlot 可调用 Context#getCurEntry#getOriginNode 方法获取该 StatisticNode。这里我们可以得出一个结论如果我们自定义的 ProcessorSlot 需要用到调用来源的 StatisticNode那么在构建 ProcessorSlotChain 时,我们必须要将这个自定义 ProcessorSlot 放在 ClusterBuilderSlot 之后。</p>
@@ -951,9 +735,6 @@ context.setCurNode(node);
</div>
</div>
</div>
</div>
@@ -961,9 +742,6 @@ context.setCurNode(node);
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
@@ -979,17 +757,11 @@ context.setCurNode(node);
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
@@ -1015,9 +787,6 @@ context.setCurNode(node);
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
@@ -1029,9 +798,6 @@ context.setCurNode(node);
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
@@ -1049,12 +815,6 @@ context.setCurNode(node);
return "";
}
</script>
</html>