写在前面

本文继续来分析Sentinel的源码,上篇文章对Sentinel的调用过程做了深入分析,主要涉及到了两个概念:插槽链和Node节点。那么接下来我们就根据插槽链的调用关系来依次分析每个插槽(slot)的源码。

默认插槽链的调用顺序,以及每种类型Node节点的关系都在上面文章开头分析过 Sentinel源码解析一

NodeSelectorSlot

/**
* 相同的资源但是Context不同,分别新建 DefaultNode,并以ContextName为key
*/
private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10); public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)
throws Throwable { // 根据ContextName尝试获取DefaultNode
DefaultNode node = map.get(context.getName());
if (node == null) {
synchronized (this) {
node = map.get(context.getName());
if (node == null) {
// 初始化DefaultNode,每个Context对应一个
node = new DefaultNode(resourceWrapper, null);
HashMap<String, DefaultNode> cacheMap = new HashMap<String, DefaultNode>(map.size());
cacheMap.putAll(map);
cacheMap.put(context.getName(), node);
map = cacheMap;
}
// 构建 Node tree
((DefaultNode)context.getLastNode()).addChild(node);
}
} context.setCurNode(node);
// 唤醒执行下一个插槽
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

NodeSelectorSlot顾名思义是用来构建Node的。

我们可以看到NodeSelectorSlot对于不同的上下文都会生成一个DefaultNode。这里还有一个要注意的点:相同的资源({@link ResourceWrapper#equals(Object)})将全局共享相同的{@link ProcessorSlotChain},无论在哪个上下文中,因此不同的上下文可以进入到同一个对象的NodeSelectorSlot.entry方法中,那么这里要怎么区分不同的上下文所创建的资源Node呢?显然可以使用上下文名称作为映射键以区分相同的资源Node。

然后这里要考虑另一个问题。一个资源有可能创建多个DefaultNode(有多个上下文时),那么我们应该如何快速的获取总的统计数据呢?

答案就在下一个Slot(ClusterBuilderSlot)中被解决了。

ClusterBuilderSlot

上面有提到一个问题,我们要如何统计不同上下文相同资源的总量数据。ClusterBuilderSlot给了很好的解决方案:具有相同资源名称的共享一个ClusterNode

// 相同的资源共享一个 ClusterNode
private static volatile Map<ResourceWrapper, ClusterNode> clusterNodeMap = new HashMap<>(); private static final Object lock = new Object(); private volatile ClusterNode clusterNode = null; @Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args)
throws Throwable {
// 判断本资源是否已经初始化过clusterNode
if (clusterNode == null) {
synchronized (lock) {
if (clusterNode == null) {
// 没有初始化则初始化clusterNode
clusterNode = new ClusterNode(resourceWrapper.getName(), resourceWrapper.getResourceType());
HashMap<ResourceWrapper, ClusterNode> newMap = new HashMap<>(Math.max(clusterNodeMap.size(), 16));
newMap.putAll(clusterNodeMap);
newMap.put(node.getId(), clusterNode); clusterNodeMap = newMap;
}
}
}
// 给相同资源的DefaultNode设置一样的ClusterNode
node.setClusterNode(clusterNode); /*
* 如果有来源则新建一个来源Node
*/
if (!"".equals(context.getOrigin())) {
Node originNode = node.getClusterNode().getOrCreateOriginNode(context.getOrigin());
context.getCurEntry().setOriginNode(originNode);
} fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

上面的代码其实就是做了一件事情,为资源创建CluserNode。这里我又要提一嘴 相同的资源({@link ResourceWrapper#equals(Object)})将全局共享相同的{@link ProcessorSlotChain},无论在哪个上下文中。也就是说,能进入到同一个ClusterBuilderSlot对象的entry方法的请求都是来自同一个资源的,所以这些相同资源需要初始化一个统一的CluserNode用来做流量的汇总统计。

LogSlot

代码比较简单,逻辑就是打印异常日志,就不分析了

StatisticSlot

StatisticSlot 是 Sentinel 的核心功能插槽之一,用于统计实时的调用数据。

public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
try {
// 先进行后续的check,包括规则的check,黑白名单check
fireEntry(context, resourceWrapper, node, count, prioritized, args); // 统计默认qps 线程数
node.increaseThreadNum();
node.addPassRequest(count); if (context.getCurEntry().getOriginNode() != null) {
// 根据来源统计qps 线程数
context.getCurEntry().getOriginNode().increaseThreadNum();
context.getCurEntry().getOriginNode().addPassRequest(count);
} if (resourceWrapper.getEntryType() == EntryType.IN) {
// 统计入口 qps 线程数
Constants.ENTRY_NODE.increaseThreadNum();
Constants.ENTRY_NODE.addPassRequest(count);
} .... 省略其他代码
}
}

StatisticSlot主要做了4种不同维度的流量统计

  1. 资源在上下文维度(DefaultNode)的统计
  2. clusterNode 维度的统计
  3. Origin 来源维度的统计
  4. 入口全局流量的统计

关于流量的统计原理的本文就不深入分析了,接下来的文章中会单独分析

SystemSlot

SystemSlot比较简单,其实就是根据StatisticSlot所统计的全局入口流量进行限流。

AuthoritySlot

public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args)
throws Throwable {
checkBlackWhiteAuthority(resourceWrapper, context);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
} void checkBlackWhiteAuthority(ResourceWrapper resource, Context context) throws AuthorityException {
Map<String, Set<AuthorityRule>> authorityRules = AuthorityRuleManager.getAuthorityRules(); if (authorityRules == null) {
return;
}
// 根据资源获取黑白名单规则
Set<AuthorityRule> rules = authorityRules.get(resource.getName());
if (rules == null) {
return;
}
// 对规则进行校验,只要有一条不通过 就抛异常
for (AuthorityRule rule : rules) {
if (!AuthorityRuleChecker.passCheck(rule, context)) {
throw new AuthorityException(context.getOrigin(), rule);
}
}
}

AuthoritySlot会对资源的黑白名单做检查,并且只要有一条不通过就抛异常。

FlowSlot

@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
checkFlow(resourceWrapper, context, node, count, prioritized); fireEntry(context, resourceWrapper, node, count, prioritized, args);
} void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)
throws BlockException {
// 检查限流规则
checker.checkFlow(ruleProvider, resource, context, node, count, prioritized);
}

这个slot主要根据预设的资源的统计信息,按照固定的次序,依次生效。如果一个资源对应两条或者多条流控规则,则会根据如下次序依次检验,直到全部通过或者有一个规则生效为止:

并且同样也会根据三种不同的维度来进行限流:

  1. 资源在上下文维度(DefaultNode)的统计
  2. clusterNode 维度的统计
  3. Origin 来源维度的统计

关于流控规则源码的深入分析就不在本篇文章赘述了

DegradeSlot

这个slot主要针对资源的平均响应时间(RT)以及异常比率,来决定资源是否在接下来的时间被自动熔断掉。

总结

  1. 相同的资源({@link ResourceWrapper#equals(Object)})将全局共享相同的{@link ProcessorSlotChain},无论在哪个上下文中
  2. 流控有多个维度,分别包括:1.不同上下文中的资源 2.相同资源 3.入口流量 3.相同的来源

Sentinel系列

Sentinel源码解析二(Slot总览)的更多相关文章

  1. Sentinel源码解析一(流程总览)

    引言 Sentinel作为ali开源的一款轻量级流控框架,主要以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度来帮助用户保护服务的稳定性.相比于Hystrix,Sentinel的设计更加简 ...

  2. Sentinel源码解析四(流控策略和流控效果)

    引言 在分析Sentinel的上一篇文章中,我们知道了它是基于滑动窗口做的流量统计,那么在当我们能够根据流量统计算法拿到流量的实时数据后,下一步要做的事情自然就是基于这些数据做流控.在介绍Sentin ...

  3. Mybatis源码解析(二) —— 加载 Configuration

    Mybatis源码解析(二) -- 加载 Configuration    正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...

  4. RxJava2源码解析(二)

    title: RxJava2源码解析(二) categories: 源码解析 tags: 源码解析 rxJava2 前言 本篇主要解析RxJava的线程切换的原理实现 subscribeOn 首先, ...

  5. element-ui 源码解析 二

    Carousel 走马灯源码解析 1. 基本原理:页面切换 页面切换使用的是 transform 2D 转换和 transition 过渡 可以看出是采用内联样式来实现的 举个栗子 <div : ...

  6. iOS即时通讯之CocoaAsyncSocket源码解析二

    原文 前言 本文承接上文:iOS即时通讯之CocoaAsyncSocket源码解析一 上文我们提到了GCDAsyncSocket的初始化,以及最终connect之前的准备工作,包括一些错误检查:本机地 ...

  7. jQuery 源码解析二:jQuery.fn.extend=jQuery.extend 方法探究

    终于动笔开始 jQuery 源码解析第二篇,写文章还真是有难度,要把自已懂的表述清楚,要让别人听懂真的不是一见易事. 在 jQuery 源码解析一:jQuery 类库整体架构设计解析 一文,大致描述了 ...

  8. Common.Logging源码解析二

    Common.Logging源码解析一分析了LogManager主入口的整个逻辑,其中第二步生成日志实例工厂类接口分析的很模糊,本随笔将会详细讲解整个日志实例工厂类接口的生成过程! (1).关于如何生 ...

  9. erlang下lists模块sort(排序)方法源码解析(二)

    上接erlang下lists模块sort(排序)方法源码解析(一),到目前为止,list列表已经被分割成N个列表,而且每个列表的元素是有序的(从大到小) 下面我们重点来看看mergel和rmergel ...

随机推荐

  1. Java中Double保留小数位

    1.能四舍五入 double d = 114.145; d = (double) Math.round(d * 100) / 100; System.out.println(d); 2. BigDec ...

  2. ISO及安全业务,机制

    ISO 应用层 为应用软件提供接口,使应用程序能够使用网络服务. 各种应用程序协议如HTTP(Web),Telnet(远程控制),FTP(文本传输) 表示层 数据的交换格式.数据加密解密.数据的压缩解 ...

  3. swoole学习--登录模块

    使用swoole+thinkphp6.0+redis 结合开发的登录模块,做完之后有几点感悟: 1.不要相信任务数据,包括请求的外部接口,特别是超时者部分,尽可能的交给task完成. 2.原来可以在入 ...

  4. Taro 2.2 全面插件化,支持拓展和定制个性化功能

    自 2.2 开始,Taro 引入了插件化机制,允许开发者通过编写插件的方式来为 Taro 拓展更多功能或者为自身业务定制个性化功能,欢迎大家进行尝试,共同讨论~ 当前版本 2.2.1 官方插件 Tar ...

  5. Netty(五):ServerBootstrap启动流程

    这篇文章主要是对ServerBootstrap启动流程做一个梳理,方便我们串联起各个类,同时也对主要的一些类有个大概的印象,方便之后逐个类的深入学习. 本篇文章不在具体贴出代码,而是对整个启动流程画了 ...

  6. web前端项目中遇到的一些问题总结(08.23更新)

    个人网站 https://iiter.cn 程序员导航站 开业啦,欢迎各位观众姥爷赏脸参观,如有意见或建议希望能够不吝赐教! 写一些最近工作中Vue项目中遇到的问题. 巴啦啦小魔仙,污卡拉,全身变,小 ...

  7. CF思维联系–CodeForces - 222 C Reducing Fractions(数学+有技巧的枚举)

    ACM思维题训练集合 To confuse the opponents, the Galactic Empire represents fractions in an unusual format. ...

  8. [CodeForces-259C] Little Elephant and Bits

    C. Little Elephant and Bits time limit per test 2 seconds memory limit per test 256 megabytes input ...

  9. bzoj4173 数学

    bzoj4173 数学 欧拉\(\varphi\)函数,变形还是很巧妙的 求: \[\varphi(n)\cdot\varphi(m)\cdot\sum_{n\bmod k+m\bmod k\ge k ...

  10. 别了,JavaScript;你好,Blazor

    Web开发与JavaScript开发向来是同义词.直到WebAssembly的横空出世,WebAssembly (Wasm)是一种在浏览器中可以执行的二进制指令. WebAssembly 的 官方工具 ...