关于sentinel:基于Sentinel自研组件的系统限流降级负载保护最佳实践探索-京东云技术团队

作者:京东物流 杨建民 一、Sentinel简介Sentinel 以流量为切入点,从流量管制、熔断降级、零碎负载爱护等多个维度爱护服务的稳定性。 Sentinel 具备以下特色: 丰盛的利用场景:秒杀(即突发流量管制在零碎容量能够接受的范畴)、音讯削峰填谷、集群流量管制、实时熔断上游不可用利用等。齐备的实时监控:Sentinel 同时提供实时的监控性能。您能够在控制台中看到接入利用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行状况。宽泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您只须要引入相应的依赖并进行简略的配置即可疾速地接入 Sentinel。同时 Sentinel 提供 Java/Go/C++ 等多语言的原生实现。欠缺的 SPI 扩大机制:Sentinel 提供简略易用、欠缺的 SPI 扩大接口。您能够通过实现扩大接口来疾速地定制逻辑。例如定制规定治理、适配动静数据源等 无关Sentinel的具体介绍以及和Hystrix的区别能够自行网上检索,举荐一篇文章:https://mp.weixin.qq.com/s/Q7Xv8cypQFrrOQhbd9BOXw 本次次要应用了Sentinel的降级、限流、零碎负载爱护性能 二、Sentinel关键技术源码解析 无论是限流、降级、负载等管制伎俩,大抵流程如下: •StatisticSlot 则用于记录、统计不同维度的 runtime 指标监控信息 •责任链顺次触发后续 slot 的 entry 办法,如 SystemSlot、FlowSlot、DegradeSlot 等的规定校验; •当后续的 slot 通过,没有抛出 BlockException 异样,阐明该资源被胜利调用,则减少执行线程数和通过的申请数等信息。 对于数据统计,次要会牵扯到 ArrayMetric、BucketLeapArray、MetricBucket、WindowWrap 等类。 我的项目构造 以下次要剖析core包里的内容 2.1注解入口 2.1.1 Entry、Context、NodeSphU门面类的办法出参都是Entry,Entry能够了解为每次进入资源的一个凭证,如果调用SphO.entry()或者SphU.entry()能获取Entry对象,代表获取了凭证,没有被限流,否则抛出一个BlockException。 Entry中持有本次对资源调用的相干信息: •createTime:创立该Entry的工夫戳。 •curNode:Entry以后是在哪个节点。 •orginNode:Entry的调用源节点。 •resourceWrapper:Entry关联的资源信息。 Entry是一个抽象类,CtEntry是Entry的实现,CtEntry持有Context和调用链的信息 Context的源码正文如下, This class holds metadata of current invocationNode的源码正文 ...

May 16, 2023 · 18 min · jiezi

关于sentinel:不改一行源码实现-sentineldashboard-所有配置支持-apollo-持久化

sentinel-dashboard apollo 定制版sentinel-dashboard-apollo 是从官网 Sentinel fork 的 dashboard 定制版,反对所有配置长久化到 apollo。 github:https://github.com/fengjx/Sen...gitee:https://gitee.com/fengjx/Sent...应用文档:http://blog.fengjx.com/sentin...sentinel-dashboard 为什么须要定制Sentinel 是阿里巴巴开源的流量治理组件。性能十分齐全,包含了:申请过滤、降级、限流、流量监控等性能。如果对 sentinel 还不是很理解能够查看官网文档:https://sentinelguard.io/zh-c... 尽管 sentinel 的设计十分优良,基本上满足了流量治理的所有需要,然而 sentinel-dashboard(治理后盾)的配置都是存储在内存,在服务重启后就会失落。所以 sentinel 目前是不具备在生产环境上应用的。即便 sentinel 客户端是反对了从 apollo、consul、etcd、eureka、nacos、redis、spring-cloud-config、zookeeper 读取配置,然而如果不应用 dashboard,间接手动批改配置的话,官网也没有提供具体的参数配置文档,想晓得哪些参数可配置,须要本人查看源码,应用上十分不敌对。 而这个问题早在 2020 年就有人提出来了(github issue) dashboard 配置长久化性能,然而官网至今(2022-07)仍然没有实现这个性能。 https://github.com/alibaba/Se... https://github.com/alibaba/Se... 值得一提的是,阿里云的商业版 sentinel-dashboard 是有这个性能的。并且在 test 代码中能够看到有对应长久化实现的。所以这很显著官网并不想在开源版实现这个性能,须要咱们本人去实现。这其中的原由曾经非常明显了。 计划选型目前曾经实现的组件中,sentinel 客户端曾经反对: apolloconsuletcdeurekanacosredisspring-cloud-configzookeeper 以最小化改变准则,咱们能够从下面其中一个作为长久化存储计划,否则就须要本人再开发一套客户端同步数据组件。 这里我抉择 apollo,理由是:apollo 作为配置核心,有丰盛的配置性能,与其余计划如 nacos 都要欠缺和稳固许多。而其余如 redis、zookeeper 在数据排查方面都不是太不便。 源码剖析sentinel-dashboard 的源码构造非常简单。后端应用 spring-boot,前端应用 angular1。 咱们关上浏览器抓包工具,在界面上操作增删改查对应配置,就能够晓得对应的接口是多少,而后通过接口门路找到对应的 Controller,持续往下跟踪就能够晓得残缺的解决流程了。 例如:新增网关流控规定的接口是 /gateway/flow/new.json 通过剖析不难发现,不论是什么配置,对应增删改查的接口门路都是相似的。 sentinel 规定总共有 7 中类型,都实现了 RuleEntity 接口 ...

August 3, 2022 · 2 min · jiezi

关于sentinel:Spring-Cloud-AlibabaSentinel流量控制框架

Sentinel简介Sentinel被称为分布式系统的流量防守兵,是阿里开源流控框架,从服务限流、降级、熔断等多个维度爱护服务,Sentinel提供了简洁易用的控制台,能够看到接入利用的秒级数据,并且能够在控制台设置一些规定爱护利用,它比Hystrix反对的范围广,如Spring Cloud、Dubbo、gRPC都能够整合。资源是Sentinel最要害的概念,遵循Sentinel API的开发标准定义资源,就能将利用爱护起来。而规定能够通过控制面板配置,也能够和资源联结起来,规定能够在控制台批改并且即时失效。名词解释:限流:不能让流量一窝蜂的进来,否则可能会冲垮零碎,须要限载流量,个别采纳排队的形式有序进行 对应生存中的小例子:比方在一个景区,个别是不会让所有人在同一时间进去的,会限度人数,排队进入,让景区内人数在一个可控范畴,因为景区的基础设施服务不了那么多人。 降级:即便在零碎出故障的状况下,也要尽可能的提供服务,在可用和不可用之间找一个平衡点,比方返回敌对提醒等。 例如当初稍有规模的电商零碎,为了给用户提供个性化服务,个别都有举荐零碎,假如当初举荐零碎宕机了,不应该在举荐商品一栏不给用户展现商品,反而能够升高一点要求,保障给用户看到的是敌对界面,给用户返回一些筹备好的静态数据。 熔断:间接回绝拜访,而后返回敌对提醒,个别是依据申请失败率或申请响应工夫做熔断。 熔断好比家里的保险盒,当线路过热时,就会跳闸,免得烧坏电路。 Sentinel和同类产品比照Sentinel、Hystrix、Resilience4j的异同根底个性SentinelHystrixResilience4j限流QPS、线程数、调用关系无限的反对Rate LImiter注解的反对反对反对反对动静规定配置反对多种数据源反对多种数据源无限反对实时统计信息滑动窗口滑动窗口Ring Bit Buffer熔断降级策略均匀响应工夫、异样比例、异样数异样比例均匀响应工夫、异样比例控制台可配置各种规定,接口调用的秒级信息,机器发现等简略监控不提供控制台,可对接其它监控平台流量整形反对预热、排队模式不反对简略的Rate Limiter模式零碎自适应限流反对不反对不反对扩展性多个扩大点插件的模式接口的模式罕用适配框架Servlet、Spring Cloud、Dubbo、gRPC等Servlet、Spring Cloud、NetflixSpring Boot、Spring Cloud 下载和运行github地址github.com/alibaba/Sen…能够间接下载jar包运行jar包,也能够下载源码编译运行因为它是springboot我的项目,下载jar包后间接运行jar包即可java -jar sentinel-dashboard-1.8.3.jar默认端口8080,如果须要批改,能够减少-Dserver.port参数,启动命令批改为java -jar -Dserver.port=9000 sentinel-dashboard-1.8.3.jar ,即可将程序端口改为9000默认账号sentinel,默认明码sentinel,登录后页面是空白的,是因为sentinel采纳懒加载的形式,只有证正应用它,性能才会展现进去 我的项目集成Sentinel4.1 创立提供者服务pom.xml <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 热部署 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <!-- 衰弱监控 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- 服务注册/服务发现须要引入的 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- nacos配置核心依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> <!-- sentinel组件依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency></dependencies> ...

June 24, 2022 · 1 min · jiezi

关于sentinel:解决Sentinel-module-javabase-does-not-opens-javalang-问题

1 环境Sentinel 1.8.3OpenJDK 17.0.2Manjaro2 问题形容依据官网Github Wiki应用如下命令启动Sentinel: java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar报错截图如下: 摘要如下: java.lang.IllegalStateException: Cannot load configuration class: com.alibaba.csp.sentinel.dashboard.DashboardApplicationCaused by: org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InaccessibleObjectException-->Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @74fdb593Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @74fdb593能够看到异样来自于InaccessibleObjectException,报错信息为module java.base does not "opens java.lang" to unnamed module @74fdb593。 ...

March 14, 2022 · 1 min · jiezi

关于sentinel:sentinel与hystrix比较

表格来自 Sentinel Github 官网文档 Hystrix 最外围的一项性能就是资源隔离,反对线程池隔离和信号量隔离;Sentinel反对信号量隔离。Sentinel与Hystrix都反对基于失败比率的熔断降级,在调用超过指定的数量并且失败比率达到设定阀值时触发熔断,并在下个工夫窗口主动复原。Sentinel也反对按失败总数熔断降级,但按失败总数的熔断降级固定工夫窗口为1分钟,当1分钟内调用失败总数达到设定的阀值就会触发熔断。此外,Sentinel还反对基于均匀响应工夫的熔断降级,均匀响应工夫越长,阐明服务的性能在继续降落,在响应工夫继续飙高时主动熔断,能够避免调用慢造成级联阻塞。Sentinel 和旧版本 Hystrix 的实时指标数据统计实现都是基于滑动窗口,指标数据统计指的是统计每个资源的以后窗口工夫内的申请总数、解决胜利总数、失败总数、总耗时、均匀耗时、最大耗时、最小耗时、被降级总数等。Sentinel 提供数据源接口可实现动静加载规定配置,联合 loadRules API 可灵便的运行时批改规定配置,并且随时批改随时失效。Hystrix 的资源模型设计上采纳了命令模式,在创立 Command 时就须要指定隔离策略是线程池隔离还是信号量隔离,一但指定了隔离策略,运行期间便不能批改隔离策略,而在 Sentinel 中资源定义和规定配置是拆散的,因而在配置的灵活性上 Sentinel 更具备劣势。Sentinel 支持系统自适应限流,Hystrix 所不反对的。当零碎负载较高的时候,如果仍继续让申请进入,可能会导致系统解体,无奈响应。在集群环境下,负载平衡把本应这台机器承载的流量转发到其它的机器下来,如果这个时候其它的机器也处在一个边缘状态的时候,这个减少的流量就会导致这台机器解体,最初导致整个集群不可用。针对这个状况,Sentinel 提供了对应的爱护机制,让零碎的入口流量和零碎的负载达到一个均衡,保证系统在能力范畴之内解决最多的申请。在 QPS 过高的状况下,间接回绝超出限度的申请是最常见的实现形式,但有些场景咱们可能并不想间接拒绝请求,而是让申请排队期待解决,例如某一秒突增申请过多,但下一秒可能又没有申请或者申请很少的状况。Sentinel 的流量管制反对多种模式,例如间接回绝模式、慢启动预热模式、匀速器模式。而慢启动预热模式和匀速器模式也是 Hystrix 所不反对的。Sentinel 在框架的设计上应用了责任链模式和 SPI 机制提供扩大性能。应用SPI咱们能够自定义降级性能ProcessorSlot。Sentinel 还反对集群限流。除了轮询负载平衡算法外,其它的算法都会导致流量到集群的每个节点都不一样,有的多有的少。集群流控能够准确地管制整个集群的调用总量,联合单机限流兜底,能够更好地施展流量管制的成果。Sentinel集群分嵌入模式(Embedded)与独立模式(Alone)。 嵌入模式:集群限流服务端作为利用的内置服务同利用一起启动,与利用在同一个过程,可动静的筛选其中一个节点作为集群限流服务端集群限流服务端作为一个独立的利用部署黑白名单限流和热点参数限流也是 Sentinel 的一大特色,黑白名单限流,可依据申请起源判断起源是否在黑名单中,如果在黑名单中则拒绝请求,否则放行,联合 Sentinel 的灵便动静配置,黑白名单可用于顶峰期间对某些服务限流。

February 14, 2022 · 1 min · jiezi

关于sentinel:sentinel滑动窗口流量统计

StatisticSlot次要统计两类数据: 线程数申请数,也就是QPS对于线程数统计比较简单,是通过外部保护LongAdder进行的以后线程数的统计,每进入一个线程加1,线程执行完减1,从而失去线程数。对于QPS的统计则要简单点,其中用到了滑动窗口的原理。上面就来重点剖析下实现的详情。 BucketSentinel应用Bucket统计一个窗口工夫内的各项指标数据,这些指标数据包含申请总数、胜利总数、异样总数、总耗时、最小耗时、最大耗时等,而一个Bucket能够是记录1s内的数据,也能够是10ms内的数据,这个工夫长度称为窗口工夫。 public class MetricBucket { /** * 存储各事件的计数,比方异样总数、申请总数等 */ private final LongAdder[] counters; /** * 这段事件内的最小耗时 */ private volatile long minRt;}Bucket记录一段时间内的各项指标数据用的是一个LongAdder数组,LongAdder保障了数据批改的原子性,并且性能较AtomicInteger体现更好。数组的每个元素别离记录一个工夫窗口的申请总数、异样数、部耗时。Sentinel应用枚举类型MetricEvent的ordinal属性作为下标,当须要获取Bucket记录总的胜利申请数或异样总数、总的申请解决耗时,可依据事件类型(MetricEvent)从Bucket的LongAdder数组中获取对应的LongAdder,并调用sum办法获取: // 假如事件为 MetricEvent.SUCCESSpublic long get(MetricEvent event) { // MetricEvent.SUCCESS.ordinal()为 1 return counters[event.ordinal()].sum();}须要记录申请数时操作如下: // 假如事件为 MetricEvent.RTpublic void add(MetricEvent event, long n) { // MetricEvent.RT.ordinal()为 2 counters[event.ordinal()].add(n);}滑动窗口咱们心愿晓得某个接口的每秒解决胜利申请数(胜利QPS)、申请均匀耗时(avg rt),咱们只须要管制Bucket统计一秒钏的指标数据即可。Sentinel是如何实现的呢?它定义了一个Bucket数组,依据工夫戳来定位到数组的下标。假如咱们须要统计每1秒解决的申请数,且只须要保留最近一分钟的数据,那么Bucket数组的大小就能够设置为60,每个Bucket的windowLengthInMs(窗口工夫)大小就是1000ms。咱们不可能当然也不须要有限存储Bucket,如果说只须要保留一分钟的数据,那咱们就能够将Bucket的大小设置为60并循环应用,防止频繁创立Bucket。这种状况下如何定位Bucket呢?做法是将以后工夫戳去掉毫秒局部等到以后秒数,再将失去的秒数与数组长度取余,就能失去以后工夫窗口的Bucket在数组中的地位。例如给定工夫戳计算数组索引: private int calculateTimeIdx(long timeMillis) { /** * 假如以后工夫戳为 1577017699235 * windowLengthInMs 为 1000 毫秒(1 秒) * 则 * 将毫秒转为秒 => 1577017699 * 而后对数组长度取余=>映射到数组的索引 * 取余是为了循环利用数组 */ long timeId = timeMillis / windowLengthInMs; return (int) (timeId % array.length()); }因为数组是循环应用的,以后工夫戳与一分钟之前的工夫戳与后一分钟的工夫戳都会映射到数组中的同一个Bucket,因而,必须要可能判断获得的Bucket是否是统计以后工夫窗口内的指标数据,这便要数组每个元素都存储Bucet工夫窗口的开始工夫戳。那开始工夫如果计算呢? ...

February 13, 2022 · 2 min · jiezi

关于sentinel:sentinel整体工作流程

一, Sentinel里的ProcessorSlot责任链上一文里咱们介绍了Sentinel里的七种ProcessorSlot,它们分成两类,一类进行数据统计,一类实现降级性能。Sentinel整体工作流程是应用责任链模式将所有的ProcessorSlot依照肯定的程序组成一个单向链表辅助实现资源指标数据统计的 ProcessorSlot 必须在实现降级性能的 ProcessorSlot 的后面,起因很简略,降级性能须要根据资源的指标数据做判断,当然,如果某个 ProcessorSlot 不依赖指标数据实现降级性能,那这个 ProcessorSlot 的地位就没有束缚。 实现将 ProcessorSlot 串成一个单向链表的是 ProcessorSlotChain,这个 ProcessorSlotChain 是由 SlotChainBuilder 结构的,默认 SlotChainBuilder 结构的 ProcessorSlotChain 注册的 ProcessorSlot 以及程序如下代码所示。 public class DefaultSlotChainBuilder implements SlotChainBuilder { @Override public ProcessorSlotChain build() { ProcessorSlotChain chain = new DefaultProcessorSlotChain(); chain.addLast(new NodeSelectorSlot()); chain.addLast(new ClusterBuilderSlot()); chain.addLast(new LogSlot()); chain.addLast(new StatisticSlot()); chain.addLast(new AuthoritySlot()); chain.addLast(new SystemSlot()); chain.addLast(new FlowSlot()); chain.addLast(new DegradeSlot()); return chain; }}如果是自定义的ProcessorSlot能够通过AbstractLinkedProcessorSlot进行构建 public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> { // 以后节点的下一个节点 private AbstractLinkedProcessorSlot<?> next = null; public void setNext(AbstractLinkedProcessorSlot<?> next) { this.next = next; } @Override public void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args) throws Throwable { if (next != null) { T t = (T) obj; // 调用下一个 ProcessorSlot 的 entry 办法 next.entry(context,resourceWrapper,t,count,prioritized,args); } } @Override public void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) { if (next != null) { // 调用下一个 ProcessorSlot 的 exit 办法 next.exit(context, resourceWrapper, count, args); } }}ProcessorSlot 接口的定义如下: ...

February 13, 2022 · 2 min · jiezi

关于sentinel:sentinel介绍

sentinel的特色图 sentinel基本概念资源 资源是sentinel的要害概念,资源能够是java利用里的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它利用提供的服务,甚至能够是一段代码。只有通过 Sentinel API 定义的代码,就是资源,可能被 Sentinel 爱护起来。大部分状况下,能够应用办法签名,URL,甚至服务名称作为资源名来标示资源。规定 围绕资源的实时状态设定的规定,能够包含流量管制规定、熔断降级规定以及零碎爱护规定。所有规定能够动静实时调整。 sentinel性能与设计理念流量管制 流量管制在网络传输中是一个罕用的概念,它用于调整网络包的发送数据。然而,从零碎稳定性角度思考,在解决申请的速度上,也有十分多的考究。任意工夫到来的申请往往是随机不可控的,而零碎的解决能力是无限的。咱们须要依据零碎的解决能力对流量进行管制。Sentinel 作为一个调配器,能够依据须要把随机的申请调整成适合的形态,如下图所示:能够从上面几方面对流量进行调整: 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;运行指标,例如 QPS、线程池、零碎负载等;管制的成果,例如间接限流、冷启动、排队等。Sentinel 的设计理念是让您自由选择管制的角度,并进行灵便组合,从而达到想要的成果。 熔断降级 除了流量管制以外,升高调用链路中的不稳固资源也是 Sentinel 的使命之一。因为调用关系的复杂性,如果调用链路中的某个资源呈现了不稳固,最终会导致申请产生沉积。Sentinel 和 Hystrix 的准则是统一的: 当检测到调用链路中某个资源呈现不稳固的体现,例如申请响应工夫长或异样比例升高的时候,则对这个资源的调用进行限度,让申请疾速失败,防止影响到其它的资源而导致级联故障。 熔断降级的设计理念 在限度的伎俩上,Sentinel 和 Hystrix 采取了齐全不一样的办法。Hystrix 通过 线程池隔离 的形式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的益处是资源和资源之间做到了最彻底的隔离。毛病是除了减少了线程切换的老本(过多的线程池导致线程数目过多),还须要事后给各个资源做线程池大小的调配。 sentinel又是如何做的呢? 通过并发线程数进行限度 : 和资源池隔离的办法不同,Sentinel 通过限度资源并发线程的数量,来缩小不稳固资源对其它资源的影响。这样岂但没有线程切换的损耗,也不须要您事后调配线程池的大小。当某个资源呈现不稳固的状况下,例如响应工夫变长,对资源的间接影响就是会造成线程数的逐渐沉积。当线程数在特定资源上沉积到肯定的数量之后,对该资源的新申请就会被回绝。沉积的线程实现工作后才开始持续接管申请。通过响应工夫对资源进行降级 : 除了对并发线程数进行管制以外,Sentinel 还能够通过响应工夫来疾速降级不稳固的资源。当依赖的资源呈现响应工夫过长后,所有对该资源的拜访都会被间接回绝,直到过了指定的工夫窗口之后才从新复原。零碎负载爱护: Sentinel 同时提供零碎维度的自适应爱护能力。避免雪崩,是零碎防护中重要的一环。当零碎负载较高的时候,如果还继续让申请进入,可能会导致系统解体,无奈响应。在集群环境下,网络负载平衡会把本应这台机器承载的流量转发到其它的机器下来。如果这个时候其它的机器也处在一个边缘状态的时候,这个减少的流量就会导致这台机器也解体,最初导致整个集群不可用。针对这个状况,Sentinel 提供了对应的爱护机制,让零碎的入口流量和零碎的负载达到一个均衡,保证系统在能力范畴之内解决最多的申请。 应用示例 定义资源限流规定: //初始化规定 private static void initFlowRules() { List<FlowRule> rules = new ArrayList<>(); //限流规定的汇合 FlowRule flowRule = new FlowRule();//限流规定 flowRule.setResource("ruleTest");//资源(能够是办法名称、接口) //线程数(FLOW_GRADE_THREAD)与QPS (FLOW_GRADE_QPS) flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS); //限流的阈值的类型 flowRule.setCount(18);// QPS数 rules.add(flowRule); FlowRuleManager.loadRules(rules); }启动测试类 ...

February 12, 2022 · 1 min · jiezi

关于sentinel:聊聊如何通过自定义注解实现springmvc和sentinel整合

前言之前写过一篇文章聊聊因不失当应用alibaba sentinel而踩到的坑。其实这外面有些坑是因为在sentinel在mvc我的项目统计时,是基于mvc的拦截器来实现。这种形式会导致比方热点参数规定,比拟难获取到参数,因而要在我的项目中额定配置@SentinelResource注解才会失效。明天咱们就来聊下如何通过自定义注解把springmvc申请的性能和sentinel性能给整合起来 实现思路外围思路通过一个注解把springmvc的@RequestMapping具备的性能 + @SentinelResource具备的性能给聚合起来 实现步骤1、自定义注解@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documented@Mappingpublic @interface CircuitBreakerMapping { //----------------RequestMapping------------------------------- /** * Assign a name to this mapping. * <p><b>Supported at the type level as well as at the method level!</b> * When used on both levels, a combined name is derived by concatenation * with "#" as separator. * @see org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder * @see org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy */ String name() default ""; /** * The primary mapping expressed by this annotation. * <p>This is an alias for {@link #path}. For example * {@code @RequestMapping("/foo")} is equivalent to * {@code @RequestMapping(path="/foo")}. * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings inherit * this primary mapping, narrowing it for a specific handler method. */ @AliasFor("path") String[] value() default {}; /** * The path mapping URIs (e.g. "/myPath.do"). * Ant-style path patterns are also supported (e.g. "/myPath/*.do"). * At the method level, relative paths (e.g. "edit.do") are supported * within the primary mapping expressed at the type level. * Path mapping URIs may contain placeholders (e.g. "/${connect}"). * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings inherit * this primary mapping, narrowing it for a specific handler method. * @see org.springframework.web.bind.annotation.ValueConstants#DEFAULT_NONE * @since 4.2 */ @AliasFor("value") String[] path() default {}; /** * The HTTP request methods to map to, narrowing the primary mapping: * GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE. * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings inherit * this HTTP method restriction (i.e. the type-level restriction * gets checked before the handler method is even resolved). */ RequestMethod[] method() default {}; /** * The parameters of the mapped request, narrowing the primary mapping. * <p>Same format for any environment: a sequence of "myParam=myValue" style * expressions, with a request only mapped if each such parameter is found * to have the given value. Expressions can be negated by using the "!=" operator, * as in "myParam!=myValue". "myParam" style expressions are also supported, * with such parameters having to be present in the request (allowed to have * any value). Finally, "!myParam" style expressions indicate that the * specified parameter is <i>not</i> supposed to be present in the request. * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings inherit * this parameter restriction (i.e. the type-level restriction * gets checked before the handler method is even resolved). * <p>Parameter mappings are considered as restrictions that are enforced at * the type level. The primary path mapping (i.e. the specified URI value) * still has to uniquely identify the target handler, with parameter mappings * simply expressing preconditions for invoking the handler. */ String[] params() default {}; /** * The headers of the mapped request, narrowing the primary mapping. * <p>Same format for any environment: a sequence of "My-Header=myValue" style * expressions, with a request only mapped if each such header is found * to have the given value. Expressions can be negated by using the "!=" operator, * as in "My-Header!=myValue". "My-Header" style expressions are also supported, * with such headers having to be present in the request (allowed to have * any value). Finally, "!My-Header" style expressions indicate that the * specified header is <i>not</i> supposed to be present in the request. * <p>Also supports media type wildcards (*), for headers such as Accept * and Content-Type. For instance, * <pre class="code"> * &#064;RequestMapping(value = "/something", headers = "content-type=text/*") * </pre> * will match requests with a Content-Type of "text/html", "text/plain", etc. * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings inherit * this header restriction (i.e. the type-level restriction * gets checked before the handler method is even resolved). * @see org.springframework.http.MediaType */ String[] headers() default {}; /** * The consumable media types of the mapped request, narrowing the primary mapping. * <p>The format is a single media type or a sequence of media types, * with a request only mapped if the {@code Content-Type} matches one of these media types. * Examples: * <pre class="code"> * consumes = "text/plain" * consumes = {"text/plain", "application/*"} * </pre> * Expressions can be negated by using the "!" operator, as in "!text/plain", which matches * all requests with a {@code Content-Type} other than "text/plain". * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings override * this consumes restriction. * @see org.springframework.http.MediaType * @see javax.servlet.http.HttpServletRequest#getContentType() */ String[] consumes() default {}; /** * The producible media types of the mapped request, narrowing the primary mapping. * <p>The format is a single media type or a sequence of media types, * with a request only mapped if the {@code Accept} matches one of these media types. * Examples: * <pre class="code"> * produces = "text/plain" * produces = {"text/plain", "application/*"} * produces = MediaType.APPLICATION_JSON_UTF8_VALUE * </pre> * <p>It affects the actual content type written, for example to produce a JSON response * with UTF-8 encoding, {@link org.springframework.http.MediaType#APPLICATION_JSON_UTF8_VALUE} should be used. * <p>Expressions can be negated by using the "!" operator, as in "!text/plain", which matches * all requests with a {@code Accept} other than "text/plain". * <p><b>Supported at the type level as well as at the method level!</b> * When used at the type level, all method-level mappings override * this produces restriction. * @see org.springframework.http.MediaType */ String[] produces() default {}; //------------------------CircuitBreaker------------------------------------- EntryType entryType() default EntryType.OUT; int resourceType() default COMMON_WEB; String blockHandler() default ""; Class<?>[] blockHandlerClass() default {}; String fallback() default ""; String defaultFallback() default ""; Class<?>[] fallbackClass() default {}; Class<? extends Throwable>[] exceptionsToTrace() default {Throwable.class}; Class<? extends Throwable>[] exceptionsToIgnore() default {};}其实这个注解就是把@RequestMapping和@SentinelResource参数给整合一块 ...

January 6, 2022 · 8 min · jiezi

关于sentinel:聊聊自定义SPI如何与sentinel整合实现熔断限流

前言之前咱们聊了一下聊聊如何实现一个带有拦截器性能的SPI。过后咱们实现的外围思路是利用了责任链+动静代理。明天咱们再聊下通过动静代理如何去整合sentinel实现熔断限流 前置常识alibaba sentinel简介Sentinel 是面向分布式服务架构的流量管制组件,次要以流量为切入点,从限流、流量整形、熔断降级、零碎负载爱护、热点防护等多个维度来帮忙开发者保障微服务的稳定性。 sentinel工作流程 sentinel关键词资源 + 规定 sentinel实现模板套路Entry entry = null;// 务必保障 finally 会被执行try { // 资源名可应用任意有业务语义的字符串,留神数目不能太多(超过 1K),超出几千请作为参数传入而不要间接作为资源名 // EntryType 代表流量类型(inbound/outbound),其中零碎规定只对 IN 类型的埋点失效 entry = SphU.entry("自定义资源名"); // 被爱护的业务逻辑 // do something...} catch (BlockException ex) { // 资源拜访阻止,被限流或被降级 // 进行相应的解决操作} catch (Exception ex) { // 若须要配置降级规定,须要通过这种形式记录业务异样 Tracer.traceEntry(ex, entry);} finally { // 务必保障 exit,务必保障每个 entry 与 exit 配对 if (entry != null) { entry.exit(); }}sentinel wikihttps://github.com/alibaba/Sentinel/wiki/%E4%B8%BB%E9%A1%B5 实现思路整体实现思路:动静代理 + sentinel实现套路模板 外围代码动静代理局部1、定义动静代理接口public interface CircuitBreakerProxy { Object getProxy(Object target); Object getProxy(Object target,@Nullable ClassLoader classLoader);}2、定义JDK或者cglib具体动静实现以jdk动静代理为例子 ...

December 7, 2021 · 2 min · jiezi

关于sentinel:sentinel之滑动窗口实现

本章次要来聊一下限流神奇sentinel的滑动窗口逻辑,来看看它是如何实现限流的。 知识点什么是滑动窗口协定滑动窗口原理sentinel 如何实现的什么是滑动窗口协定在聊滑动窗口之前,咱们要先看一下固定窗口是什么,这里我间接援用掘金上叫 yz 的作者一张图:从图中咱们能够看到就是将窗口分成固定大小的若干块,在每一块中都受到阈值的限度。然而这里存在一个毛病:如果申请是在 16t 到 26t 之间超过阈值的,固定窗口是辨认不进去的。 正是因为固定窗口存在这种缺点,所以才有了滑动窗口的呈现。在网络中,滑动窗口就是用于网络流量的管制的。它就是随着工夫不停向前挪动,保障单位工夫周期内的流量不会超过阈值。 滑动窗口原理上面咱们来看一下滑动窗口的实现原理,先上图:以这张图来介绍,对于固定窗口,咱们晓得如果有申请跨两个窗口并且在每个窗口中又没有达到阈值,则理论相加可能会超过阈值。而滑动窗口顾明思义是随着工夫一直滑动的,如上图,当一次申请在0-500ms块中近300ms的时候一次发动60并发申请,窗口就会滑动到以300ms作为开始地位向后取1000ms单位的工夫作为新窗口,也就是300-1300ms,而后在此工夫窗口中的申请都会统计起来和阈值做比拟判断,这样就解决了固定窗口申请超出阈值的问题。最不便的了解就是:以以后工夫作为截止工夫,向前取单位工夫窗口的长度作为一个窗口,而后不停向前滑动。 sentinel是如何实现的为什么要讲sentinel是如何实现的,因为我最近在看sentinel源码,并且发现如同不是依照下面那个滑动窗口的思路实现的,先发一下我看到的后果我看了好几篇文章都是提到改良,其实 就是说原来的滑动窗口算法耗资源、性能不好,所以做了一些优化,然而在我看来是一种取舍,优化的后果就是没法齐全精细化统计单位窗口内的申请,看来看去我发现还是这幅图比拟合乎我的思路。上面上源码(基于1.6.0版本) 咱们晓得 sentinel 用责任链模式连贯所有插槽,每个规定对应一个插槽,其余逻辑都不看了,和限流无关,间接看FlowSlot,这个插槽就是作用流控的这里就点一下,其余逻辑不多介绍,间接看滑动窗口实现。先看下这个类com.alibaba.csp.sentinel.node.StatisticNode,这个类就是对申请的信息进行收集统计,这里要关注一下rollingCounterInSecond和rollingCounterInMinute,别离是每秒、没分钟进行滚动统计的变量,都是ArrayMetric类型。看下ArrayMetric类这个data是十分重要的,它是个LeapArray<MetricBucket>类型,理论用的是OccupiableBucketLeapArray,看下构造函数能够看到这里设置了工夫窗口大小以及块数,这个就是滑动窗口的设置,默认是 2 块,每块 500ms 。咱们在qps限流的时候会先去获取以后工夫窗口的所有曾经通过的qps,而后去和阈值做比拟。这里的data.currentWindow()十分重要,sentinel 滑动窗口的实现原理就在这里这里的TimeUtil.currentTimeMillis()可不是间接获取以后工夫,它是准实时的这里我也有点懵,不晓得为什么不间接获取System.currentTimeMillis(),而要通过一个线程不停去更新工夫,晓得起因的敌人能够留言。持续往下看之前咱们说过 sentinel 默认将工夫窗口分为 2 块,calculateTimeIdx就是计算以后工夫应该属于哪一块,找对应的下标。这是滑动的外围逻辑timeId % array.length()这里用到了除模运算,将值固定在 array.length() 的范畴内。calculateWindowStart用于计算以后工夫对应的块的起始工夫。我把正文删掉,持续往下看这里有4种状况:1)以后块不存在,新建一个;2)以后工夫属于以后块,间接返回;3)以后工夫计算出的实践起始工夫大于以后块起始工夫,重置以后块为以后工夫并返回;4)以后工夫计算出的实践起始工夫小于以后块起始工夫,工夫倒退,返回一个新块,个别不会产生,除非本人手动批改服务器工夫之类的操作; 要害看第1、3两种。第一种不存在的状况下,会用WindowWrap来把以后窗口包装起来,并用CAS进行设置。WindowWrap比较简单,就是记录了窗口块工夫大小、起始工夫以及相干数据统计,次要看newEmptyBucket,这里创立了一个新块这个块自身就是MetricBucket所有申请的指标事件都用LongAdder来统计,蕴含这些事件比方咱们获取已通过的的申请,则是获取PASS这个事件对应的数据。再来看下第三种状况,这里会去重置窗口块把块起始工夫设置为以后计算出来的块起始工夫,而后通过reset把所有事件的统计信息重置为0 问题这里是我看滑动窗口这块逻辑发现的问题,仅做探讨,先抛一副图来说比方咱们在第0、1块的时候继续申请进来,假如每块都是 50 笔,此时曾经生成2块了,随着工夫进行,在 1100ms 时又有 50 笔申请进来,此时的申请会将第0块给重置到第二块中,因为第一块记录的50笔加上第二块新来的50笔总和并没有超过阈值100,所以不会被限流,然而实际上有可能第0块还有局部申请没解决完(假如有 30 笔是在 400ms 的时候进来的),那么实践上 400-1100ms(周期 700ms)这个时间段通过的申请就有 130 笔,超过了阈值,这就呈现了固定窗口的问题。所以在 sentinel 代码中也指明了精度问题是不是咱们得依据理论状况设置更多的块,细化工夫窗口以达到更高的精读?欢送留言给出你们的意见和想法。 总结sentinel 源码的咱们认为比拟难的应该在限流这一块了,通过对源码的浏览学会了用除模运算来管制范畴,晓得如何实现滑动窗口。当然还是有一些精度的疑难存在的。参考资料https://juejin.cn/post/698285...

November 23, 2021 · 1 min · jiezi

关于sentinel:技术分享-sentinel-架构下的切换逻辑

作者:岳明强 爱可生北京分公司 DBA 团队成员,负责数据库治理平台的运维和 MySQL 问题解决。善于对 MySQL 的故障定位。 本文起源:原创投稿 *爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。 什么是 sentinelRedis 作为最受欢迎的 NoSQL 数据库之一,次要特点为:高性能、高可用、高可扩大和具备丰盛的数据结构。其中 sentinel 作为 Redis 的高可用实现架构,可能实现主库产生故障后,主动切换到从库上。那么它的原理是什么呢,接下来咱们进行揭晓。 sentinel 之间的互相发现当咱们配置 sentinel 集群的时候,每个 sentinel 只配置和主库的连贯,然而 sentinel 之间没有互相配置,那么sentinel 之间怎么辨认呢? 每个 sentinel 定时(2s)向集群下主从的 Pub/Sub 的_sentinel_:hello 发送音讯,将本人的信息写入,同时进行订阅。当检测到新的 sentinel 时就会立刻退出到集群中。 判断主节点下线失常状况下须要3个及以上奇数个的 sentinel 节点组成集群,sentinel 每隔 SENTINEL_PING_PERIOD(1000ms)工夫给主库发送一次 PING 命令 当在 is-master-down-after-milliseconds 工夫内没有返回一次后果,那么就将状态设置为 s_down(主观下线),这时候能做切换吗? 答案是不能,因为这只是单节点 sentinel 对 redis 的监控,还须要联合其余 sentinel 节点的探测后果,再判断是否进行切换。 当判断主观下线后,会向其余 sentinel 节点发送 SENTINEL is-master-down-by-addr,当取得至多超过 quorum 的 sentinel 节点反对后,那么将状态设置为 o_down (主观下线),判断节点下线。 抉择新节点当判断主节点下线后,会依据肯定的条件判断剩下节点是否选举为主: 1、从库状态失常。带有 S_DOWN 、O_DOWN 和 DISCONNECTED 状态的从服务器不会被选中 ...

August 25, 2021 · 1 min · jiezi

关于sentinel:sentinel使用篇

原本最近想要持续写jvm系列的执行引擎这一块,然而最近在钻研sentinel,感觉还是先把它记录下来,所以这一篇就介绍一下sentinel的应用。知识点sentinel应用 Sentinel介绍Sentinel能够做的事件比拟多,大体是限流、降级、零碎爱护、认证四个方面,拉一张官网图的具体介绍能够参考官网的介绍,https://github.com/alibaba/Se... 应用这里我结合实际代码进行分享如何应用,在过程中也会顺便分享一下源码的内容。 示例首先咱们须要在本人的程序中引入sentinel的外围包,即sentinel-core,我这里用的是1.6.0版本的,如下:首先我定义一个获取用户名的web资源,而后用最根本的SphU.entry进行资源拦挡。 @RequestMapping("user")@RestControllerpublic class UserController { @PostMapping("/getName") public String getName(){ try (Entry entry = SphU.entry("getName")){ return "don"; } catch (BlockException ex){ return block(ex); } } public String block(BlockException ex){ return "block"; }}如果只是这样,sentinel是不会做任何拦挡操作的,因为此时咱们还没有定义规定,entry函数跟进去咱们会看到如下函数这里就是sentienl应用了责任链模式对各个规定进行拦挡判断的逻辑。咱们能够看到有这些实现,每个都是一个插槽,能够了解为sentinel是一个主板,各个规定是主板上的插件,比方限流规定是cpu、零碎爱护规定是显卡等。这里拿限流性能来介绍,咱们看一下FlowSlot从下面图中能够看到会通过FlowRuleManager.getFlowRuleMap()去取所有的限流规定,而后遍历管制。十分清晰地答复了下面的那个问题。上面咱们定义一个简略的限流规定: @SpringBootApplicationpublic class SentineldemoApplication { public static void main(String[] args) { initSentinelRules(); SpringApplication.run(SentineldemoApplication.class, args); } private static void initSentinelRules(){ List<FlowRule> flowRules = new ArrayList<>(1); FlowRule flowRule = new FlowRule(); flowRule.setResource("getName"); flowRule.setCount(2); flowRules.add(flowRule); FlowRuleManager.loadRules(flowRules); }}我这里的例子是在springboot启动前定义好规定,大家也能够应用sentinel的spi扩大,实现InitFunc来加载。而后用postman间断发动几次申请,看一下后果:正如预期的一样返回了block。 注解应用下面介绍了个别的应用形式,还有一种更简略的应用形式,注解应用。sentinel提供了注解SentinelResource来让咱们更不便的应用拦挡性能,先上代码 @RequestMapping("user")@RestControllerpublic class UserController { @SentinelResource(value = "getName", blockHandler = "block") @PostMapping("/getName") public String getName(){ return "don"; } public String block(BlockException ex){ return "block"; }}和下面形式比,这里只加了一个注解,定义了资源名称(不定义的话默认取包名+类名:办法名称)以及阻断解决。如果只到这一步,咱们用下面的postman间断发申请会发现并没有呈现阻断。这里通过源码再给大家剖析下起因。SentinelResource注解尽管在core包里,然而SentinelResourceAspect却在sentinel-annotation-aspectj包里通过下面代码能够看出SentinelResource这个注解自身是通过AOP的形式实现的拦挡,最终也是调的SphU.entry进行规定拦挡。因为在sentinel-annotation-aspectj包里定义的注解拦挡,所以咱们要用注解的话必须要引入sentinel-annotation-aspectj包引完之后,咱们持续下面的postman申请,会发现还是没有拦挡。怎么回事?起因是SentinelResourceAspect只是定义了一个切面类,然而还没有成为spring bean,咱们来注册一下 ...

February 25, 2021 · 1 min · jiezi

关于sentinel:Sentinel-整合zuul

退出zuul-adapter依赖:<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-zuul-adapter</artifactId> <version>1.6.0</version></dependency>

December 28, 2020 · 1 min · jiezi