Sentinel源码解析二(Slot总览)
写在前面
本文继续来分析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种不同维度的流量统计
- 资源在上下文维度(DefaultNode)的统计
- clusterNode 维度的统计
- Origin 来源维度的统计
- 入口全局流量的统计
关于流量的统计原理的本文就不深入分析了,接下来的文章中会单独分析
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主要根据预设的资源的统计信息,按照固定的次序,依次生效。如果一个资源对应两条或者多条流控规则,则会根据如下次序依次检验,直到全部通过或者有一个规则生效为止:
并且同样也会根据三种不同的维度来进行限流:
- 资源在上下文维度(DefaultNode)的统计
- clusterNode 维度的统计
- Origin 来源维度的统计
关于流控规则源码的深入分析就不在本篇文章赘述了
DegradeSlot
这个slot主要针对资源的平均响应时间(RT)以及异常比率,来决定资源是否在接下来的时间被自动熔断掉。
总结
- 相同的资源({@link ResourceWrapper#equals(Object)})将全局共享相同的{@link ProcessorSlotChain},无论在哪个上下文中
- 流控有多个维度,分别包括:1.不同上下文中的资源 2.相同资源 3.入口流量 3.相同的来源
Sentinel系列
Sentinel源码解析二(Slot总览)的更多相关文章
- Sentinel源码解析一(流程总览)
引言 Sentinel作为ali开源的一款轻量级流控框架,主要以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度来帮助用户保护服务的稳定性.相比于Hystrix,Sentinel的设计更加简 ...
- Sentinel源码解析四(流控策略和流控效果)
引言 在分析Sentinel的上一篇文章中,我们知道了它是基于滑动窗口做的流量统计,那么在当我们能够根据流量统计算法拿到流量的实时数据后,下一步要做的事情自然就是基于这些数据做流控.在介绍Sentin ...
- Mybatis源码解析(二) —— 加载 Configuration
Mybatis源码解析(二) -- 加载 Configuration 正如上文所看到的 Configuration 对象保存了所有Mybatis的配置信息,也就是说mybatis-config. ...
- RxJava2源码解析(二)
title: RxJava2源码解析(二) categories: 源码解析 tags: 源码解析 rxJava2 前言 本篇主要解析RxJava的线程切换的原理实现 subscribeOn 首先, ...
- element-ui 源码解析 二
Carousel 走马灯源码解析 1. 基本原理:页面切换 页面切换使用的是 transform 2D 转换和 transition 过渡 可以看出是采用内联样式来实现的 举个栗子 <div : ...
- iOS即时通讯之CocoaAsyncSocket源码解析二
原文 前言 本文承接上文:iOS即时通讯之CocoaAsyncSocket源码解析一 上文我们提到了GCDAsyncSocket的初始化,以及最终connect之前的准备工作,包括一些错误检查:本机地 ...
- jQuery 源码解析二:jQuery.fn.extend=jQuery.extend 方法探究
终于动笔开始 jQuery 源码解析第二篇,写文章还真是有难度,要把自已懂的表述清楚,要让别人听懂真的不是一见易事. 在 jQuery 源码解析一:jQuery 类库整体架构设计解析 一文,大致描述了 ...
- Common.Logging源码解析二
Common.Logging源码解析一分析了LogManager主入口的整个逻辑,其中第二步生成日志实例工厂类接口分析的很模糊,本随笔将会详细讲解整个日志实例工厂类接口的生成过程! (1).关于如何生 ...
- erlang下lists模块sort(排序)方法源码解析(二)
上接erlang下lists模块sort(排序)方法源码解析(一),到目前为止,list列表已经被分割成N个列表,而且每个列表的元素是有序的(从大到小) 下面我们重点来看看mergel和rmergel ...
随机推荐
- Java 排序算法-冒泡排序及其优化
Java 排序算法-冒泡排序及其优化 什么是冒泡排序 基本写法 优化后写法 终极版本 源码及测试 什么是冒泡排序 这里引用一下百度百科上的定义: 冒泡排序(Bubble Sort),是一种计算机科学领 ...
- Linux中find常见用法示例 ·find path -option [ -print ] [ -exec -ok command ] {} \;
find命令的参数: pathname: find命令所查找的目录路径.例如用.来表示当前目录,用/来表示系统根目录.-print: find命令将匹配的文件输出到标准输出.-exec: find命令 ...
- 【编程之美】超时重传,滑动窗口,可靠性传输原理C语言实现
版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://www.cnblogs.com/lihuidashen/p/128003 ...
- 2019-2020-1 20199325《Linux内核原理与分析》第十一周作业
实验简介: Set-UID 是 Unix 系统中的一个重要的安全机制.当一个 Set-UID 程序运行的时候,它被假设为具有拥有者的权限.例如,如果程序的拥有者是root,那么任何人运行这个程序时都会 ...
- JVM原理与深度调优(一)
什么是jvm jvm是java虚拟机 运行在用户态.通过应用程序实现java代码跨平台.与平台无关.实际上是"一次编译,到处执行" 1.从微观来说编译出来的是字节码!去到哪个平台都 ...
- 日日算法:Kruskal算法
介绍 克鲁斯卡尔(Kruskal)算法是用来求出连通图中最小生成树的算法. 连通图:指无向图中任意两点都能相通的图. 最小生成树:指联通图的所有生成树中边权重的总和最小的树(即,找出一个树,让其联通所 ...
- Linux监听磁盘使用情况
前阵子服务器磁盘写满了,导致项目出了很多奇怪的问题,比如文件上传不了(这个很好理解),还有登录时验证码无法加载(现在依旧不知道原因,项目的验证码图片是只在内存中生成的BufferedImage对象,不 ...
- BurpSuite 扩展开发[1]-API与HelloWold
园长 · 2014/11/20 15:08 0x00 简介 BurpSuite神器这些年非常的受大家欢迎,在国庆期间解了下Burp相关开发并写了这篇笔记.希望和大家分享一下JavaSwing和Burp ...
- IIS6服务器的请求流程(图文&源码)
1.IIS 7开发与管理完全参考手册 http://book.51cto.com/art/200908/146040.htm 2.Web服务IIS 6 https://technet.micro ...
- 题解 AT4867 【[ABC155D] Pairs】
题目 两次二分 首先对ans进行二分,在\([-10^{18},10^{18}]\)之间 考虑怎么check 对于每个ans,枚举每个\(a_i\),二分查找有几个\(a_j\),使得\(a_i\ti ...