sentinel的见解
resourceName),每次资源调用都会创建一个 Entry 对象。Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 SphU API 显式创建。Entry 创建的时候,同时也会创建一系列功能插槽(slot chain),这些插槽有不同的职责,例如:NodeSelectorSlot负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;ClusterBuilderSlot则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;StatisticSlot则用于记录、统计不同纬度的 runtime 指标监控信息;FlowSlot则用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制;(流控规则)AuthoritySlot则根据配置的黑白名单和调用来源信息,来做黑白名单控制;(授权规则)DegradeSlot则通过统计信息以及预设的规则,来做熔断降级;(降级规则)SystemSlot则通过系统的状态,例如 load1 等,来控制总的入口流量;(系统规则)
Sentinel 核心类解析
ProcessorSlotChain
- Sentinel 的核心骨架,将不同的 Slot 按照顺序串在一起(责任链模式),从而将不同的功能(限流、降级、系统保护)组合在一起。slot chain 其实可以分为两部分:统计数据构建部分(statistic)和判断部分(rule checking)。核心结构:
Context
- Context 代表调用链路上下文,贯穿一次调用链路中的所有
Entry。Context 名称即为调用链路入口名称。 - Context 维持的方式:
enter- 每一次资源调用都会创建一个
Entry。Entry包含了资源名、curNode(当前统计节点)、originNode(来源统计节点)等信息。 CtEntry为普通的Entry,在调用SphU.entry(xxx)的时候创建。特性:Linked entry within current context(内部维护着parent和child)- 需要注意的一点:CtEntry 构造函数中会做调用链的变换,即将当前 Entry 接到传入 Context 的调用链路上(
setUpEntryFor)。 - 资源调用结束时需要
entry.exit()。exit 操作会过一遍 slot chain exit,恢复调用栈,exit context 然后清空 entry 中的 context 防止重复调用。
- 每一次资源调用都会创建一个
- Context 代表调用链路上下文,贯穿一次调用链路中的所有
Node
EntranceNode:入口节点,特殊的链路节点,对应某个 Context 入口的所有调用数据。DefaultNode:链路节点,用于统计调用链路上某个资源的数据,维持树状结构。ClusterNode:簇点,用于统计每个资源全局的数据(不区分调用链路),以及存放该资源的按来源区分的调用数据StatisticNode:最为基础的统计节点,包含秒级和分钟级两个滑动窗口结构
业务1: controller中的资源@PostMapping("/order/query") 访问了service中的资源/goods
- 不同的入口有不同的
EntranceNode - 不同的链路有不同/goods资源都有单独的一个
ClusterNode,统计所有链路
SPI 扩展
Sentinel 提供多样化的 SPI 接口用于提供扩展的能力。开发者可以在用同一个sentinel-core的基础上自行扩展接口实现,从而可以方便地根据业务需求给 Sentinel 添加自定义的逻辑。目前 Sentinel 提供如下的扩展点:
- 初始化过程扩展:提供
InitFuncSPI接口,可以添加自定义的一些初始化逻辑,如动态规则源注册等。- Slot/Slot Chain 扩展:用于给 Sentinel 功能链添加自定义的功能并自由编排。
- 指标统计扩展(StatisticSlot Callback):用于扩展 StatisticSlot 指标统计相关的逻辑。
- Transport 扩展:提供 CommandHandler、CommandCenter 等接口,用于对心跳发送、监控 API Server 进行扩展。
- 集群流控扩展:可以方便地定制 token client/server 自定义实现,可参考对应文档
- 日志扩展:用于自定义 record log Logger,可用于对接 slf4j 等标准日志实现。
Sentinel 工作原理
1、@SentinelResource基于Aspect的AOP实现
spring.factories加载SentinelAutoConfiguration.java-->SentinelResourceAspect.java-->@Aspect定义标记-->定义切入点@Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")-->增强拦截@Around("sentinelResourceAnnotationPointcut()")
- entryWithPriority()接口,entry统一会进入此接口,ThreadLocal保护线程之中,一个线程使用一个context,
-->SphU.entry()-->Env.sph.entryWithType()-->CtSph#entryWithPriority()
private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)
throws BlockException {
// ThreadLocal<Context> contextHolder,使用ThreadLocal线程保护获取上下文
Context context = ContextUtil.getContext();
// 默认获取名称为sentinel_default_context的上下文
if (context == null) {
context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
}
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
Entry e = new CtEntry(resourceWrapper, chain, context);
try {
chain.entry(context, resourceWrapper, null, count, prioritized, args);
} catch (BlockException e1) {
e.exit(count, args);
throw e1;
} catch (Throwable e1) {
// This should not happen, unless there are errors existing in Sentinel internal.
RecordLog.info("Sentinel unexpected exception", e1);
}
return e;
}
- 创建context上下文,ContextUtil.trueEnter()接口的实现
- 里面使用到Lock锁和两次get()操作实现双重校验锁 DCL,保证原子性和性能问题
protected static Context trueEnter(String name, String origin) {
Context context = contextHolder.get();
if (context == null) {
Map<String, DefaultNode> localCacheNameMap = contextNameNodeMap;
// Lock锁和两次get()操作实现双重校验锁 DCL,保证原子性和性能问题
DefaultNode node = localCacheNameMap.get(name);
if (node == null) {
if (localCacheNameMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
setNullContext();
return NULL_CONTEXT;
} else {
try {
LOCK.lock();
node = contextNameNodeMap.get(name);
if (node == null) {
if (contextNameNodeMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
setNullContext();
return NULL_CONTEXT;
} else {
node = new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null);
// Add entrance node.
Constants.ROOT.addChild(node);
// 里面使用到CopyOnWrite技术,首先将旧的contextNameNodeMap拷贝一份,然后更新拷贝的map,再用更新后的实例列表来覆盖旧的实例列表。防止高并发读脏数据。
Map<String, DefaultNode> newMap = new HashMap<>(contextNameNodeMap.size() + 1);
newMap.putAll(contextNameNodeMap);
newMap.put(name, node);
contextNameNodeMap = newMap;
}
}
} finally {
LOCK.unlock();
}
}
}
context = new Context(node, name);
context.setOrigin(origin);
contextHolder.set(context);
} return context;
}
2、接口基于AbstractSentinellnterceptor拦截器实现
- 1.获取resourceName (controller方法的RegestMapping)
- 2.获取contextName,默认是sentinel-springweb-context
- 3.获取origin,基于自定义的RequestOriginParser
- 4初始化Context ContextUtil.enter(contextilame, oring)4.1.创建EntranceNode(contextName)4.2创建Context,放入ThreadLocal
- 5.标记资源,创建Entry,Entry e = SphU.entry(resourceName)
- 执行ProcesserSlotChain
Sentinel 的SlotChain
NodeSelectorSlot、ClusterBuilderSlot、StatisticSlotParamFlowSlot、FlowSlot、AuthoritySlot 、DegradeSlot 、SystemSlotNodeSelectorSlot
ClusterBuilderSlot
StatisticSlot
负责统计实时调用数据,包括运行信息 (访问次数、线程数)、来源信息等是实现限流的关键,其中基于滑动时间窗口算法维护了计数器,统计进入某个资源的请求次数
ParamFlowSlot
SystemSlot
AuthoritySlot
FlowSlot
- 三种流控模式:直接模式、关联模式、链路模式
- 三种流控效果:快速失败、warm up、排队等待
DegradeSlot
限流算法
- 计数器算法,又包括窗口计数器算法、滑动窗口计数器算法
- 令牌桶算法 (Token Bucket)
- 漏桶算法(Leaky Bucket)
滑动窗口计数器算法
- 固定窗口算法缺点:对于在两个窗口中间临界点的流量突刺,不能统计起来
- 滑动窗口算法优点:每次根据当前时间获取时间窗口期,可以统计流量突刺缺点:统计量大
令牌桶算法
- 以固定的速率生成令牌,存入令牌桶中,如果令牌桶满了以后,多余令牌丢弃
- 请求进入后,必须先尝试从桶中获取令牌,获取到令牌后才可以被处理
- 如果令牌桶中没有令牌,则请求等待或丢弃
漏桶算法
- 将每个请求视作作”水滴”放入”漏桶”进行存储
- ”漏桶”以固定速率向外"漏”出请求来执行,如果”漏桶”空了则停止”漏水”
@Override
public boolean canPass(Node node, int acquireCount, boolean prioritized) {
// Pass when acquire count is less or equal than 0.
if (acquireCount <= 0) {
return true;
}
// Reject when count is less or equal than 0.
// Otherwise,the costTime will be max of long and waitTime will overflow in some cases.
if (count <= 0) {
return false;
}
long currentTime = TimeUtil.currentTimeMillis();
// Calculate the interval between every two requests.
long costTime = Math.round(1.0 * (acquireCount) / count * 1000);
// Expected pass time of this request.
long expectedTime = costTime + latestPassedTime.get();
if (expectedTime <= currentTime) {
// Contention may exist here, but it's okay.
latestPassedTime.set(currentTime);
return true;
} else {
// Calculate the time to wait.
long waitTime = costTime + latestPassedTime.get() - TimeUtil.currentTimeMillis();
if (waitTime > maxQueueingTimeMs) {
return false;
} else {
long oldTime = latestPassedTime.addAndGet(costTime);
try {
waitTime = oldTime - TimeUtil.currentTimeMillis();
if (waitTime > maxQueueingTimeMs) {
latestPassedTime.addAndGet(-costTime);
return false;
}
// in race condition waitTime may <= 0
if (waitTime > 0) {
Thread.sleep(waitTime);
}
return true;
} catch (InterruptedException e) {
}
}
}
return false;
}
限流算法对比
sentinel的见解的更多相关文章
- Redis集群~windows下搭建Sentinel环境及它对主从模式的实际意义
回到目录 关于redis-sentinel出现的原因 Redis集群的主从模式有个最大的弊端,就是当主master挂了之前,它的slave从服务器无法提升为主,而在redis-sentinel出现之后 ...
- Redis Sentinel集群配置中的一些细节
今天在配置Redis集群,用作Tomcat集群的缓存共享.关于Redis集群的配置网上有很多文章,这里只是记录一下我在配置过程中遇到的一些小的细节问题. 1. 关于Protected Mode的问题 ...
- redis 集群热备自动切换sentinel配置实战
---恢复内容开始--- Redis SentinelSentinel(哨兵)是用于监控redis集群中Master状态的工具,其已经被集成在redis2.4+的版本中一.Sentinel作用:1): ...
- Redis Sentinel 高可用实现说明
背景: 前面介绍了Redis 复制.Sentinel的搭建和原理说明,通过这篇文章大致能了解Sentinel的原理和实现方法以及相关的搭建.这篇文章就针对Redis Sentinel的搭建做 ...
- Redis 复制、Sentinel的搭建和原理说明
背景: Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端) ...
- Redis Sentinel机制与用法说明【转】
本文来自:https://segmentfault.com/a/1190000002680804 概述 Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Ma ...
- 24.Redis2.8主从集群sentinel
0.集群架构(此处只说两种;本文2种,避免sentinel成为单节点) 第一种: 第二种: 1.下载redis2.8.x版本,2.8.x都是稳定版 redis-2.8.24.tar.gz 2.解压,安 ...
- 基于sentinel 的redis集群环境搭建
环境信息,三台机器,一台master,两台slave,每台机器上启动一个sentinel master 192.168.1.106 slave1 192.168.1.102 slave2 192.16 ...
- [OC笔记] Category分类之见解
用过别的语言做过开发的同学都知道,如果你想扩充一个类,就应该去继承这个类.但是OC里面有更好的方法,那就是分类. 那什么是分类呢?就是在不改变原先类,我们可以在其中添加咱们自定义的方法,这样和同事合作 ...
- redis sentinel基本命令与参数
1.redis基本命令1)获取sentinel的状态(1)info查看sentinel的状态(2)sentinel masters 获取sentinel中监控的所有master的节点(3)sentin ...
随机推荐
- flask session 伪造
flask session 伪造 一.session的作用 由于http协议是一个无状态的协议,也就是说同一个用户第一次请求和第二次请求是完全没有关系的,但是现在的网站基本上有登录使用的功能,这就要求 ...
- SqlSugar子查询
1.基础教程 1.1 API目录 *****只查一列***** //First: SqlFunc.Subqueryable<School>().Where(s => s.Id == ...
- 应对数据爆炸时代,揭秘向量数据库如何成为AI开发者的新宠,各数据库差异对比
应对数据爆炸时代,揭秘向量数据库如何成为AI开发者的新宠,各数据库差异对比 随着大模型的爆火,向量数据库也越发成为开发者关注的焦点.为了方便大家更好地了解向量数据库,我们特地推出了<Hello, ...
- PaddleNLP通用信息抽取技术UIE【一】产业应用实例:信息抽取{实体关系抽取、中文分词、精准实体标。情感分析等}、文本纠错、问答系统、闲聊机器人、定制训练
相关文章: 1.快递单中抽取关键信息[一]----基于BiGRU+CR+预训练的词向量优化 2.快递单信息抽取[二]基于ERNIE1.0至ErnieGram + CRF预训练模型 3.快递单信息抽取[ ...
- 4.4 Windows驱动开发:内核监控进程与线程创建
当你需要在Windows操作系统中监控进程的启动和退出时,可以使用PsSetCreateProcessNotifyRoutineEx函数来创建一个MyCreateProcessNotifyEx回调函数 ...
- Django后台输出原生SQL语句
如果需要打印orm翻译后的原生sql语句,只需要在setting最后加上下面代码就行. 1 LOGGING = { 2 'version': 1, 3 'disable_existing_logger ...
- WPF性能优化:形状(Shape)、几何图形(Geometry)和图画(Drawing)的使用
在用户界面技术中,绘图是一个绕不开的话题.WPF提供了多种可根据应用程序要求进行优化的2D图形和图像的处理功能,包括画刷(Brush).形状(Shape).几何图形(Geometry).图画(Draw ...
- 由反射引出的Java动态代理与静态代理
写在开头 在<深入剖析Java中的反射,由浅入深,层层剥离!>这篇文章中我们讲反射时,曾提到过Java的动态代理中使用了反射技术,那么好,今天我们要就着反射的索引,来学习一下Java中的代 ...
- SESSION会话机制解析
Windows Session(会话)的概念 会话 session 是由代表单个用户登录会话的所有进程和系统对象组成的.其中的对象包括所有的窗口,桌面和windows stations.桌面是特定se ...
- TfrxReport.Clear。尽量慎用。
for MyXuHaoKey in MyXuHaoJianRongSanJieKouDataDicApi.KeySortList do begin //标记下打印编号,吸入淘打的客户 MyTradeA ...