关于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

如何基于-Nacos-和-Sentinel-实现灰度路由和流量防护一体化

基于Alibaba Nacos和Sentinel,实现灰度路由和流量防护一体化的解决方案,发布在最新的 Nepxion Discovery 5.4.0 版,具体参考: 源码主页,请访问 源码主页指南主页,请访问 指南主页文档主页,请访问 文档主页 Nepxion Discovery框架在实现灰度发布和路由功能前提下,结合Nacos和Sentinel,对流量再实施一层防护措施,更能达到企业级的流量安全控制的目的。它的功能包括: 封装远程配置中心和本地规则文件的读取逻辑,即优先读取远程配置,如果不存在或者规则错误,则读取本地规则文件。动态实现远程配置中心对于规则的热刷新封装NacosDataSource和ApolloDataSource,支持Nacos和Apollo两个远程配置中心,零代码实现Sentinel功能。更多的远程配置中心,请参照Sentinel官方的DataSource并自行集成支持原生的流控规则、降级规则、授权规则、系统规则、热点参数流控规则支持扩展LimitApp的机制,通过动态的Http Header方式实现组合式防护机制,包括基于服务名、基于灰度组、基于灰度版本、基于灰度区域、基于机器地址和端口等防护机制,支持自定义任意的业务参数组合实现该功能,例如,根据传入的微服务灰度版本号+用户名,组合在一起进行熔断支持微服务侧Actuator、Swagger和Rest三种方式的规则写入支持控制台侧基于微服务名的Actuator、Swagger和Rest三种方式的批量规则写入支持开关关闭上述功能spring.application.strategy.sentinel.enabled=true,默认是关闭的[Nacos] 阿里巴巴中间件部门开发的新一代集服务注册发现中心和配置中心为一体的中间件。它是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施,支持几乎所有主流类型的“服务”的发现、配置和管理,更敏捷和容易地构建、交付和管理微服务平台 [Sentinel] 阿里巴巴中间件部门开发的新一代以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性的分布式系统的流量防卫兵。它承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等 环境搭建及依赖引入服务端在Discovery框架原有依赖的基础上,再引入如下依赖 <dependency> <groupId>com.nepxion</groupId> <artifactId>discovery-plugin-strategy-starter-service-sentinel</artifactId> <version>${discovery.version}</version></dependency><dependency> <groupId>com.nepxion</groupId> <artifactId>discovery-plugin-strategy-sentinel-starter-nacos</artifactId> <!-- <artifactId>discovery-plugin-strategy-sentinel-starter-apollo</artifactId> --> <version>${discovery.version}</version></dependency>原生Sentinel注解参照下面代码,为接口方法增加@SentinelResource注解,value为sentinel-resource,blockHandler和fallback是防护其作用后需要执行的方法 @RestController@ConditionalOnProperty(name = DiscoveryConstant.SPRING_APPLICATION_NAME, havingValue = "discovery-guide-service-b")public class BFeignImpl extends AbstractFeignImpl implements BFeign { private static final Logger LOG = LoggerFactory.getLogger(BFeignImpl.class); @Override @SentinelResource(value = "sentinel-resource", blockHandler = "handleBlock", fallback = "handleFallback") public String invoke(@PathVariable(value = "value") String value) { value = doInvoke(value); LOG.info("调用路径:{}", value); return value; } public String handleBlock(String value, BlockException e) { return value + "-> B server sentinel block, cause=" + e.getClass().getName() + ", rule=" + e.getRule() + ", limitApp=" + e.getRuleLimitApp(); } public String handleFallback(String value) { return value + "-> B server sentinel fallback"; }}原生Sentinel规则Sentinel在配置中心订阅的Key格式,如下: ...

October 15, 2019 · 2 min · jiezi

聊聊sentinel的SentinelGatewayFilter

序本文主要研究一下sentinel的SentinelGatewayFilter SentinelGatewayFilterSentinel-1.6.2/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/sc/SentinelGatewayFilter.java public class SentinelGatewayFilter implements GatewayFilter, GlobalFilter { private final GatewayParamParser<ServerWebExchange> paramParser = new GatewayParamParser<>( new ServerWebExchangeItemParser()); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR); Mono<Void> asyncResult = chain.filter(exchange); if (route != null) { String routeId = route.getId(); Object[] params = paramParser.parseParameterFor(routeId, exchange, r -> r.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID); String origin = Optional.ofNullable(GatewayCallbackManager.getRequestOriginParser()) .map(f -> f.apply(exchange)) .orElse(""); asyncResult = asyncResult.transform( new SentinelReactorTransformer<>(new EntryConfig(routeId, EntryType.IN, 1, params, new ContextConfig(contextName(routeId), origin))) ); } Set<String> matchingApis = pickMatchingApiDefinitions(exchange); for (String apiName : matchingApis) { Object[] params = paramParser.parseParameterFor(apiName, exchange, r -> r.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME); asyncResult = asyncResult.transform( new SentinelReactorTransformer<>(new EntryConfig(apiName, EntryType.IN, 1, params)) ); } return asyncResult; } private String contextName(String route) { return SentinelGatewayConstants.GATEWAY_CONTEXT_ROUTE_PREFIX + route; } Set<String> pickMatchingApiDefinitions(ServerWebExchange exchange) { return GatewayApiMatcherManager.getApiMatcherMap().values() .stream() .filter(m -> m.test(exchange)) .map(WebExchangeApiMatcher::getApiName) .collect(Collectors.toSet()); }}SentinelGatewayFilter实现了GatewayFilter、GlobalFilter接口;其filter方法主要是获取route信息,然后对asyncResult进行transform,这里使用的是SentinelReactorTransformerSentinelReactorTransformerSentinel-1.6.2/sentinel-adapter/sentinel-reactor-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/reactor/SentinelReactorTransformer.java ...

July 8, 2019 · 4 min · jiezi

这个注解一次搞定限流与熔断降级SentinelResource

在之前的《使用Sentinel实现接口限流》一文中,我们仅依靠引入Spring Cloud Alibaba对Sentinel的整合封装spring-cloud-starter-alibaba-sentinel,就完成了对所有Spring MVC接口的限流控制。然而,在实际应用过程中,我们可能需要限流的层面不仅限于接口。可能对于某个方法的调用限流,对于某个外部资源的调用限流等都希望做到控制。呢么,这个时候我们就不得不手工定义需要限流的资源点,并配置相关的限流策略等内容了。 今天这篇我们就来一起学习一下,如何使用@SentinelResource注解灵活的定义控制资源以及如何配置控制策略。 自定义资源点下面的例子基于您已经引入了Spring Cloud Alibaba Sentinel为基础,如果您还不会这些,建议优先阅读《使用Sentinel实现接口限流》。 第一步:在应用主类中增加注解支持的配置: @SpringBootApplicationpublic class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } // 注解支持的配置Bean @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); }}第二步:在需要通过Sentinel来控制流量的地方使用@SentinelResource注解,比如下面以控制Service逻辑层的某个方法为例: @Slf4j@Servicepublic class TestService { @SentinelResource(value = "doSomeThing") public void doSomeThing(String str) { log.info(str); }}到这里一个需要被保护的方法就定义完成了。下面我们分别说说,定义了资源点之后,我们如何实现不同的保护策略,包括:限流、降级等。 如何实现限流与熔断降级在定义了资源点之后,我们就可以通过Dashboard来设置限流和降级策略来对资源点进行保护了。同时,也可以通过@SentinelResource来指定出现限流和降级时候的异常处理策略。下面,就来一起分别看看限流和降级都是如何实现的。 实现限流控制第一步:在Web层调用这个被保护的方法: @RestControllerpublic class TestController { @Autowired private TestService testService; @GetMapping("/hello") public String hello() { estService.doSomeThing("hello " + new Date()); return "didispace.com"; }}第二步:启动测试应用,启动Sentinel-Dashboard。发一个请求到/hello接口上,使得Sentinel-Dashboard上可以看到如下图所示的几个控制点: ...

July 2, 2019 · 2 min · jiezi

开发者说Sentinel-流控功能在-SpringMVCSpringBoot-上的实践

从用户的视角来感受一个开源项目的成长,是我们推出「开发者说」专栏的初衷,即在开发者进行开源项目选型时,提供更为立体的项目信息。专栏所有内容均来自作者原创/投稿,本文是「开发者说」的第6篇,作者 Jason Joo,@友乐活(北京),Sentinel Committer. 1st:《深度剖析开源分布式事务方案 Seata 的事务协调器》 2nd:《RocketMQ 消息发送的高可用设计》 3st:《消息队列 Kafka 和 RocketMQ 之我见》 4th:《如何参与定义一款 IDE 插件》 5th:《基于 Nacos 的网关灰度路由和服务权重灰度》 集成 Sentinel 前生流控在分布式系统中是较为基本的需求,其需要在系统负载、服务质量、流量甄别、安全⻛控等⽅⾯进⾏保障,并根据业务需求,进⾏动态调整或⼈工临时介入,尤其是在⼀些事件性的时期,以实现快速控制和恢复服务的效果。 流控手段一般挂载在流量网关和业务内的逻辑。 流量网关常见于 Nginx 这类代理层,通过扩展插件、Lua脚本进⾏针对 IP/Path/Query 等形式的流控。业务内则⼤多在局部或框架层进行信号量、线程池、超时时间或其它逻辑来实现流控。前者主要体现在运维的可操作性,不侵⼊业务线,而后者则针对性更强,但有侵⼊性或修改时需要部署,⾯向业务团队可控。 两种类型的流控往往⽐较割裂(由不同的团队在不共享的空间内进行控制),常出现指标的不协调性。 为了解决这⼀问题,我们开始汇总现有的需求,调研相关的系统,并准备实现⼀套可以同时面向业务和运维,进行应用级隔离和满足基本规则类型需求的流控实现,预期是在 Nginx 端利用LuaJIT实现一套更为强大的流控模块。 调研过程中,适逢 Sentinel 0.1/0.2的发布,⽀持servlet集成(URL限流),带有操作⾯板(Dashboard),支持基本的实时状况查看、实时的修改分发规则、全局负载和单点熔断,能基于QPS、信号量等形式进行流控。除了零侵入以外,基本满⾜我们的需求,所以准备基于 Sentinel 进行方案落地尝试。 集成 Sentinel 的实践我们的基本需求如下: 基于 URL 做流控基于 Dashboard 做动态修改规则业务端针对 SpringMVC/SpringBoot⽀持异步 Servlet (后续提出)sentinel-transport 监听端⼝可定制(涉及防⽕墙配置、同⼀节点多服务)集成适配基于 Sentinel 所提供的功能、适配方式,需要进行基本的配置和修改。 集成方式现有项⽬流量⼊口部分⼤多为基于 SpringMVC 的项目,少部分为 SpringBoot 项目,并且从运维部署的角度看,⽬前主要有普通运⾏方式(JVM/Tomcat)和容器化方式。 普通运行方式:尽量避免修改 JVM 启动参数,参数通过集中配置中心或 properties ⽂件来定义;容器化⽅式:参数⼤多是通过 ENV 环境变量进行定义。所以我们根据实际的需求,将 Sentinel 初始化⼯作进⾏了封装,基于 SpringMVC 提供了XML初始化方式,基于 SpringBoot 提供了注解初始化方式,例如: ...

June 14, 2019 · 1 min · jiezi

Spring-Cloud-Alibaba基础教程Sentinel-Dashboard中修改规则同步到Nacos

上一篇我们介绍了如何通过改造Sentinel Dashboard来实现修改规则之后自动同步到Apollo。下面通过这篇,详细介绍当使用Nacos作为配置中心之后,如何实现Sentinel Dashboard中修改规则同步到Nacos。关于下面改造的原理和分析可以见上一篇《Sentinel Dashboard中修改规则同步到Apollo》的头两节内容,这里不重复介绍了。 代码实现下面直接来看看如何实现的具体改造步骤,这里参考了Sentinel Dashboard源码中关于Nacos实现的测试用例。但是由于考虑到与Spring Cloud Alibaba的结合使用,略作修改。 第一步:修改pom.xml中的sentinel-datasource-nacos的依赖,将<scope>test</scope>注释掉,这样才能在主程序中使用。 <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <!--<scope>test</scope>--></dependency>第二步:找到resources/app/scripts/directives/sidebar/sidebar.html中的这段代码: <li ui-sref-active="active"> <a ui-sref="dashboard.flowV1({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则 </a></li>修改为: <li ui-sref-active="active"> <a ui-sref="dashboard.flow({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则 </a></li>第三步:在com.alibaba.csp.sentinel.dashboard.rule包下新建一个nacos包,用来编写针对Nacos的扩展实现。 第四步:创建Nacos的配置类,具体代码如下: @Configurationpublic class NacosConfig { @Bean public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() { return JSON::toJSONString; } @Bean public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class); } @Bean public ConfigService nacosConfigService() throws Exception { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "localhost"); return ConfigFactory.createConfigService(properties); }}如果用到了namespace隔离环境,可以在nacosConfigService方法中再加入配置,比如:properties.put(PropertyKeyConst.NAMESPACE, "130e71fa-97fe-467d-ad77-967456f2c16d"); ...

May 22, 2019 · 2 min · jiezi

Sentinel-Dashboard中修改规则同步到Apollo存储

在之前的两篇教程中我们分别介绍了如何将Sentinel的限流规则存储到Nacos和Apollo中。同时,在文末的思考中,我都指出了这两套整合方案都存在一个不足之处:不论采用什么配置中心,限流规则都只能通过Nacos界面或Apollo界面来完成修改才能得到持久化存储,而在Sentinel Dashboard中修改限流规则虽然可以生效,但是不会被持久化到配置中心。而在这两个配置中心里存储的数据是一个Json格式,当存储的规则越来越多,对该Json配置的可读性与可维护性会变的越来越差。所以,下面我们就来继续探讨这个不足之处,并给出相应的解决方案。本文以Apollo存储为例,下一篇介绍Nacos的改在示例。 问题分析在实际操作之前,我们先通过下图了解一下之前我们所实现的限流规则持久化方案的配置数据流向图: 蓝色箭头代表了限流规则由配置中心发起修改的更新路径橙色箭头代表了限流规则由Sentinel Dashboard发起修改的更新路径从图中可以很明显的看到,Sentinel Dashboard与业务服务之间本身是可以互通获取最新限流规则的,这在没有整合配置中心来存储限流规则的时候就已经存在这样的机制。最主要的区别是:配置中心的修改都可以实时的刷新到业务服务,从而被Sentinel Dashboard读取到,但是对于这些规则的更新到达各个业务服务之后,并没有一个机制去同步到配置中心,作为配置中心的客户端也不会提供这样的逆向更新方法。 改造方案关于如何改造,现来解读一下官方文档中关于这部分的说明: 要通过 Sentinel 控制台配置集群流控规则,需要对控制台进行改造。我们提供了相应的接口进行适配。 从 Sentinel 1.4.0 开始,我们抽取出了接口用于向远程配置中心推送规则以及拉取规则: DynamicRuleProvider<T>: 拉取规则DynamicRulePublisher<T>: 推送规则对于集群限流的场景,由于每个集群限流规则都需要唯一的 flowId,因此我们建议所有的规则配置都通过动态规则源进行管理,并在统一的地方生成集群限流规则。 我们提供了新版的流控规则页面,可以针对应用维度推送规则,对于集群限流规则可以自动生成 flowId。用户只需实现 DynamicRuleProvider 和 DynamicRulePublisher 接口,即可实现应用维度推送(URL: /v2/flow)。 这段内容什么意思呢?简单的说就是Sentinel Dashboard通过DynamicRuleProvider和DynamicRulePublisher两个接口来获取和更新应用的动态规则。默认情况下,就如上一节中Sentinel Dashboard与各业务服务之间的两个箭头,一个接口负责获取规则,一个接口负责更新规则。 所以,只需要通过这两个接口,实现对配置中心中存储规则的读写,就能实现Sentinel Dashboard中修改规则与配置中心存储同步的效图如下: ![图片上传中...] 其中,绿色箭头为公共公共部分,即:不论从培中心修改,还是从Sentinel Dashboard修改都会触发的操作。这样的话,从上图的两处修改起点看,所有涉及的部分都能获取到一致的限流规则了。 代码实现下面继续说说具体的代码实现,这里参考了Sentinel Dashboard源码中关于Apollo实现的测试用例。但是由于考虑到与Spring Cloud Alibaba的结合使用,略作修改。 第一步:修改pom.xml中的Apollo OpenAPi的依赖,将<scope>test</scope>注释掉,这样才能在主程序中使用。 <dependency> <groupId>com.ctrip.framework.apollo</groupId> <artifactId>apollo-openapi</artifactId> <version>1.2.0</version> <!--<scope>test</scope>--></dependency>第二步:找到resources/app/scripts/directives/sidebar/sidebar.html中的这段代码: <li ui-sref-active="active"> <a ui-sref="dashboard.flowV1({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则 </a></li>修改为: <li ui-sref-active="active"> <a ui-sref="dashboard.flow({app: entry.app})"> <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控规则 </a></li>第三步:在com.alibaba.csp.sentinel.dashboard.rule包下新建一个apollo包,用来编写针对Apollo的扩展实现。 第四步:创建Apollo的配置类,定义Apollo的portal访问地址以及第三方应用访问的授权Token(通过Apollo管理员账户登录,在“开放平台授权管理”功能中创建),具体代码如下: @Configurationpublic class ApolloConfig { @Bean public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() { return JSON::toJSONString; } @Bean public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() { return s -> JSON.parseArray(s, FlowRuleEntity.class); } @Bean public ApolloOpenApiClient apolloOpenApiClient() { ApolloOpenApiClient client = ApolloOpenApiClient.newBuilder() .withPortalUrl("https://apollo.xxx.com") // TODO 根据实际情况修改 .withToken("open api token") // TODO 根据实际情况修改 .build(); return client; }}第五步:实现Apollo的配置拉取实现。 ...

May 7, 2019 · 2 min · jiezi

Spring Cloud Alibaba基础教程:Sentinel使用Nacos存储规则

通过上一篇《使用Sentinel实现接口限流》的介绍,相信大家对Sentinel已经有了初步的认识。在Spring Cloud Alibaba的整合封装之下,接口限流这件事情可以非常轻易的整合到我们的Spring Cloud应用中。但是,通过上篇的整合,依然还不能完美的满足我们日常的生产需求。其中,非常重要的一点就是限流规则的持久化问题。不少细心的读者也在留言中提出了Dashboard中设置的限流规则在应用重启之后就丢失了的问题。那么,接下来我们就来说说Sentinel的规则持久化如何实现。使用Nacos存储限流规则Sentinel自身就支持了多种不同的数据源来持久化规则配置,目前包括以下几种方式:文件配置Nacos配置ZooKeeper配置Apollo配置本文我们就来一起动手尝试一下,使用Spring Cloud Alibaba的中整合的配置中心Nacos存储限流规则。准备工作下面我们将同时使用到Nacos和Sentinel Dashboard,所以可以先把Nacos和Sentinel Dashboard启动起来。默认配置下启动后,它们的访问地址(后续会用到)为:Nacos:http://localhost:8848/Sentinel Dashboard:http://localhost:8080/如果还没入门Nacos和Sentinel Dashboard可以通过文末的系列目录先学习之前的内容。应用配置第一步:在Spring Cloud应用的pom.xml中引入Spring Cloud Alibaba的Sentinel模块和Nacos存储扩展: <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <version>1.4.0</version> </dependency></dependencies>第二步:在Spring Cloud应用中添加配置信息:spring.application.name=alibaba-sentinel-datasource-nacosserver.port=8003# sentinel dashboardspring.cloud.sentinel.transport.dashboard=localhost:8080# sentinel datasource nacos :http://blog.didispace.com/spring-cloud-alibaba-sentinel-2-1/spring.cloud.sentinel.datasource.ds.nacos.server-addr=localhost:8848spring.cloud.sentinel.datasource.ds.nacos.dataId=${spring.application.name}-sentinelspring.cloud.sentinel.datasource.ds.nacos.groupId=DEFAULT_GROUPspring.cloud.sentinel.transport.dashboard:sentinel dashboard的访问地址,根据上面准备工作中启动的实例配置spring.cloud.sentinel.datasource.ds.nacos.server-addr:nacos的访问地址,,根据上面准备工作中启动的实例配置spring.cloud.sentinel.datasource.ds.nacos.groupId:nacos中存储规则的groupIdspring.cloud.sentinel.datasource.ds.nacos.dataId:nacos中存储规则的dataId这里对于dataId使用了${spring.application.name}变量,这样可以根据应用名来区分不同的规则配置。注意:Spring Cloud Alibaba的Sentinel整合文档中有一些小问题,比如:并没有spring.cloud.sentinel.datasource.ds2.nacos.rule-type这个参数。可能是由于版本迭代更新,文档失修的缘故。读者在使用的时候,可以通过查看org.springframework.cloud.alibaba.sentinel.datasource.config.DataSourcePropertiesConfiguration和org.springframework.cloud.alibaba.sentinel.datasource.config.NacosDataSourceProperties两个类来分析具体的配置内容,会更为准确。比如,Nacos存储的具体配置类源码如下:public class NacosDataSourceProperties extends AbstractDataSourceProperties { private String serverAddr; private String groupId; private String dataId; // commercialized usage private String endpoint; private String namespace; private String accessKey; private String secretKey;}上面我们使用了前三个属性,后四个属性是阿里云的商业化产品使用的,这里就不具体介绍了,有使用阿里云商业产品的童鞋可以了解一下。第三步:创建应用主类,并提供一个rest接口,比如:@SpringBootApplicationpublic class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @GetMapping("/hello") public String hello() { return “didispace.com”; } }}第四步:Nacos中创建限流规则的配置,比如:其中:Data ID、Group就是上面第二步中配置的内容。配置格式选择JSON,并在配置内容中填入下面的内容:[ { “resource”: “/hello”, “limitApp”: “default”, “grade”: 1, “count”: 5, “strategy”: 0, “controlBehavior”: 0, “clusterMode”: false }]可以看到上面配置规则是一个数组类型,数组中的每个对象是针对每一个保护资源的配置对象,每个对象中的属性解释如下:resource:资源名,即限流规则的作用对象limitApp:流控针对的调用来源,若为 default 则不区分调用来源grade:限流阈值类型(QPS 或并发线程数);0代表根据并发数量来限流,1代表根据QPS来进行流量控制count:限流阈值strategy:调用关系限流策略controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)clusterMode:是否为集群模式这里我们只做简单的配置解释,以便于理解这里的配置作用。实际上这里还有非常多可配置选项和规则,更复杂的配置后面我们单独开一篇来深入学习。第五步:启动应用。如果一些顺利,可以看到类似下面的日志,代表已经成功从Nacos加载了一条限流规则:2019-04-16 14:24:42.919 INFO 89484 — [ main] o.s.c.a.s.c.SentinelDataSourceHandler : [Sentinel Starter] DataSource ds-sentinel-nacos-datasource start to loadConfig2019-04-16 14:24:42.938 INFO 89484 — [ main] o.s.c.a.s.c.SentinelDataSourceHandler : [Sentinel Starter] DataSource ds-sentinel-nacos-datasource load 1 FlowRule通过postman或者curl访问几下localhost:8003/hello接口:$ curl localhost:8003/hellodidispace.com此时,在Sentinel Dashboard中就可以看到当前我们启动的alibaba-sentinel-datasource-nacos服务。点击左侧菜单中的流控规则,可以看到已经存在一条记录了,具体如下:这条记录就是上面我们在Nacos中配置的限流规则。深入思考在完成了上面的整合之后,对于接口流控规则的修改就存在两个地方了:Sentinel控制台、Nacos控制台。这个时候,需要注意当前版本的Sentinel控制台不具备同步修改Nacos配置的能力,而Nacos由于可以通过在客户端中使用Listener来实现自动更新。所以,在整合了Nacos做规则存储之后,需要知道在下面两个地方修改存在不同的效果:Sentinel控制台中修改规则:仅存在于服务的内存中,不会修改Nacos中的配置值,重启后恢复原来的值。Nacos控制台中修改规则:服务的内存中规则会更新,Nacos中持久化规则也会更新,重启后依然保持。代码示例本文介绍内容的客户端代码,示例读者可以通过查看下面仓库中的alibaba-sentinel-datasource-nacos项目:Github:https://github.com/dyc87112/SpringCloud-Learning/Gitee:https://gitee.com/didispace/SpringCloud-Learning/如果您对这些感兴趣,欢迎star、follow、收藏、转发给予支持!参考资料下面是Sentinel的仓库地址与官方文档,读者也可以自己查阅文档学习:GithubSentinel官方文档:动态规则Spring Cloud Alibaba Sentinel文档专题推荐Spring Boot基础教程Spring Cloud基础教程 ...

April 17, 2019 · 1 min · jiezi

Spring Cloud Alibaba基础教程:使用Sentinel实现接口限流

最近管点闲事浪费了不少时间,感谢网友libinwalan的留言提醒。及时纠正路线,继续跟大家一起学习Spring Cloud Alibaba。Nacos作为注册中心和配置中心的基础教程,到这里先告一段落,后续与其他结合的内容等讲到的时候再一起拿出来说,不然内容会有点跳跃。接下来我们就来一起学习一下Spring Cloud Alibaba下的另外一个重要组件:Sentinel。Sentinel是什么Sentinel的官方标题是:分布式系统的流量防卫兵。从名字上来看,很容易就能猜到它是用来作服务稳定性保障的。对于服务稳定性保障组件,如果熟悉Spring Cloud的用户,第一反应应该就是Hystrix。但是比较可惜的是Netflix已经宣布对Hystrix停止更新。那么,在未来我们还有什么更好的选择呢?除了Spring Cloud官方推荐的resilience4j之外,目前Spring Cloud Alibaba下整合的Sentinel也是用户可以重点考察和选型的目标。Sentinel的功能和细节比较多,一篇内容很难介绍完整。所以下面我会分多篇来一一介绍Sentinel的重要功能。本文就先从限流入手,说说如何把Sentinel整合到Spring Cloud应用中,以及如何使用Sentinel Dashboard来配置限流规则。通过这个简单的例子,先将这一套基础配置搭建起来。使用Sentinel实现接口限流Sentinel的使用分为两部分:sentinel-dashboard:与hystrix-dashboard类似,但是它更为强大一些。除了与hystrix-dashboard一样提供实时监控之外,还提供了流控规则、熔断规则的在线维护等功能。客户端整合:每个微服务客户端都需要整合sentinel的客户端封装与配置,才能将监控信息上报给dashboard展示以及实时的更改限流或熔断规则等。下面我们就分两部分来看看,如何使用Sentienl来实现接口限流。部署Sentinel Dashboard本文采用的spring cloud alibaba版本是0.2.1,可以查看依赖发现当前版本使用的是sentinel 1.4.0。为了顺利完成本文的内容,我们可以挑选同版本的sentinel dashboard来使用是最稳妥的。下载地址:https://github.com/alibaba/Se…其他版本:https://github.com/alibaba/Se…同以往的Spring Cloud教程一样,这里也不推荐大家跨版本使用,不然可能会出现各种各样的问题。通过命令启动:java -jar sentinel-dashboard-1.4.0.jarsentinel-dashboard不像Nacos的服务端那样提供了外置的配置文件,比较容易修改参数。不过不要紧,由于sentinel-dashboard是一个标准的spring boot应用,所以如果要自定义端口号等内容的话,可以通过在启动命令中增加参数来调整,比如:-Dserver.port=8888。默认情况下,sentinel-dashboard以8080端口启动,所以可以通过访问:localhost:8080来验证是否已经启动成功,如果一切顺利的话,可以看到如下页面:整合Sentinel第一步:在Spring Cloud应用的pom.xml中引入Spring Cloud Alibaba的Sentinel模块: <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency></dependencies>第二步:在Spring Cloud应用中通过spring.cloud.sentinel.transport.dashboard参数配置sentinel dashboard的访问地址,比如:spring.application.name=alibaba-sentinel-rate-limitingserver.port=8001# sentinel dashboardspring.cloud.sentinel.transport.dashboard=localhost:8080第三步:创建应用主类,并提供一个rest接口,比如:@SpringBootApplicationpublic class TestApplication { public static void main(String[] args) { SpringApplication.run(TestApplication.class, args); } @Slf4j @RestController static class TestController { @GetMapping("/hello") public String hello() { return “didispace.com”; } }}第四步:启动应用,然后通过postman或者curl访问几下localhost:8001/hello接口。$ curl localhost:8001/hellodidispace.com此时,在上一节启动的Sentinel Dashboard中就可以当前我们启动的alibaba-sentinel-rate-limiting这个服务以及接口调用的实时监控了。具体如下图所示:配置限流规则在完成了上面的两节之后,我们在alibaba-sentinel-rate-limiting服务下,点击簇点链路菜单,可以看到如下界面:其中/hello接口,就是我们上一节中实现并调用过的接口。通过点击流控按钮,来为该接口设置限流规则,比如:这里做一个最简单的配置:阈值类型选择:QPS单机阈值:2综合起来的配置效果就是,该接口的限流策略是每秒最多允许2个请求进入。点击新增按钮之后,可以看到如下界面:其实就是左侧菜单中流控规则的界面,这里可以看到当前设置的所有限流策略。验证限流规则在完成了上面所有内容之后,我们可以尝试一下快速的调用这个接口,看看是否会触发限流控制,比如:$ curl localhost:8001/hellodidispace.com$ curl localhost:8001/hellodidispace.com$ curl localhost:8001/helloBlocked by Sentinel (flow limiting)可以看到,快速的调用两次/hello接口之后,第三次调用被限流了。代码示例本文介绍内容的客户端代码,示例读者可以通过查看下面仓库中的alibaba-sentinel-rate-limiting项目:Github:https://github.com/dyc87112/SpringCloud-Learning/Gitee:https://gitee.com/didispace/SpringCloud-Learning/如果您对这些感兴趣,欢迎star、follow、收藏、转发给予支持!参考资料下面是Sentinel的仓库地址与官方文档,读者也可以自己查阅文档学习:GithubSentinel官方文档Spring Cloud Alibaba Sentinel文档专题推荐Spring Boot基础教程Spring Cloud基础教程 ...

April 11, 2019 · 1 min · jiezi

Alibaba Sentinel(1):快速上手

Alibaba Sentinel 是一个灵活的系统负载控制框架,通过控制接口和方法的调用来保证系统负载不会过大,维持正常响应速度。该项目的地址是 https://github.com/alibaba/Se… 。但是阿里的文档一贯看起来一头雾水,所以本文介绍如何用一个最简单的项目来上手。如果你熟悉 Spring Boot,那么几分钟就可以搞定。1、创建一个 Maven 项目首先创建一个空的 Maven 项目,加上 Spring Boot 的依赖。pom.xml 看起来是下面的样子,你可以直接拿来用:<?xml version=“1.0” encoding=“UTF-8”?><project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.demo</groupId> <artifactId>sentinel-test</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.1.3.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement></project>这里没有将 Spring Boot 作为 parent 项目,而是使用 <dependencyManagement>,但效果是一样的。2、添加依赖关系在 pom.xml 中,首先添加 Spring Boot 依赖:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId></dependency>然后添加 Sentinel。在这个例子当中,我们将用 注解 来实现负载控制,所以添加 sentinel-annotation-aspectj 依赖:<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-annotation-aspectj</artifactId> <version>1.5.1</version></dependency>添加了这两个依赖就 OK 了。3、启动类Spring Boot 项目需要一个 main() 方法来启动,我们命名这个类为 com.demo.SentinelTestApplication,其内容如下:@SpringBootApplicationpublic class SentinelTestApplication { public static void main(String[] args) throws Exception { SpringApplication.run(SentinelTestApplication.class, args); }}目前这个类还没有具体的内容,但我们应该先运行一下,确保前面做的事情没有出错,然后再添加内容。4、需要进行访问控制的服务 DemoService我们在 com.demo 包下创建一个名为 DemoService 的类,这个类包含一个需要做访问控制的 call() 方法:@Servicepublic class DemoService { private int counter; @SentinelResource(value = “DemoService.call”, blockHandler = “callBlocked”) public void call() { System.out.println(“Hello (” + ++counter + “)”); } public void callBlocked(BlockException ex) { System.err.println(“Blocked (” + ++counter + “) : " + ex.toString()); }}所谓访问控制就是当某个方法调用过于频繁时,拒绝掉一部分调用。什么才叫过于频繁,我们可以通过自定义控制规则的方式来告诉 Sentinel。定义控制规则的部分放在后面介绍,我们现在先关注业务本身,也就是 DemoService 类。这个类包含两个方法,其中 call() 方法是主角,正常的业务会调用这个方法;而 callBlocked() 则会在 call() 方法被拒绝掉时调用。call() 方法上面的 @SentinelResource 注解标明了该方法是需要进行访问控制的。Sentinel 将需要进行访问控制的方法都称作资源。这个注解有两个属性,value 属性表示该资源的名称,我们通过名称为不同的资源制定不同的控制规则。blockHandler 属性表示方法被拒绝时应该调用哪个替代方法,这个替代方法必须在同一个类当中,且参数列表要在原方法参数列表的基础上再添加一个 BlockException 类型的参数。5、编写控制规则Sentinel 将控制规则包装为 com.alibaba.csp.sentinel.slots.block.flow.FlowRule 类。它包含下面几个属性:resource : 该规则针对哪个资源;grade : 从哪个方面进行度量,如该方法的每秒调用次数,或同时调用该方法的线程数等等。count : 度量阈值。超过这个阈值则会拒绝调用该方法。strategy : 多个规则之间的搭配策略,具体参考这里。下面我们在 SentinelTestApplication 类里面添加一个创建规则的方法,同时在 main() 方法里面初始化它: private static void initRules() throws Exception { FlowRule rule1 = new FlowRule(); rule1.setResource(“DemoService.call”); rule1.setGrade(RuleConstant.FLOW_GRADE_QPS); rule1.setCount(5); // 每秒调用最大次数为 5 次 List<FlowRule> rules = new ArrayList<>(); rules.add(rule1); // 将控制规则载入到 Sentinel com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager.loadRules(rules); } public static void main(String[] args) throws Exception { initRules(); // Sentinel 载入规则不一定非要在 Spring 初始化之前,在这之后也可以。 SpringApplication.run(SentinelTestApplication.class, args); }这样 Sentinel 的规则就设置完毕。6、启用 Sentinel 注解的 AOP 拦截Spring 提供 AOP 机制来实现方法调用的拦截,这是 Sentinel 实现控制规则的原理。Sentinel 提供 com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect 类,我们要在 Spring 容器中加入这个 bean 才能让 @SentinelResource 注解起作用。我们需要在 SentinelTestApplication 类里面添加下面的代码: @Bean public SentinelResourceAspect sentinelResourceAspect() { return new SentinelResourceAspect(); }当然,在实际项目里面这一步可以放到自动配置当中。7、测试控制规则最后我们写一个方法来测试控制规则是否起作用,同样是在 SentinelTestApplication 类里面: @Autowired private DemoService demoService; @PostConstruct public void run() { for (int i = 0; i < 10; i++) { demoService.call(); } }实际运行 main() 方法时,你将会看到这样的输出:Hello (1)Hello (2)Hello (3)Hello (4)Hello (5)Blocked (6) : com.alibaba.csp.sentinel.slots.block.flow.FlowExceptionBlocked (7) : com.alibaba.csp.sentinel.slots.block.flow.FlowExceptionBlocked (8) : com.alibaba.csp.sentinel.slots.block.flow.FlowExceptionBlocked (9) : com.alibaba.csp.sentinel.slots.block.flow.FlowExceptionBlocked (10) : com.alibaba.csp.sentinel.slots.block.flow.FlowException通过这个例子,你应该大概了解 Sentinel 运作的机制了。在这个基础上,Sentinel 还能实现控制规则的实时修改、远程配置、状态监控等等,它是个非常强大的框架。 ...

April 10, 2019 · 2 min · jiezi

Redis Sentinel 哨兵模式

Redis Sentinel 哨兵模式1.网络架构网络结构如下,通过Sentinel监控Master和Slave服务器:2.配置启动配置文件如下:vi /etc/redis-sentinel.confport 26379 dir “/tmp"logfile “/var/log/redis/sentinel_20086.log”# 进程守护daemonize yes# 格式:sentinel <option_name> <master_name> <option_value># 最后的一个2代表在sentinel集群中,多少个节点认为master死了,才能真正认为该master不可用sentinel monitor T1 127.0.0.1 6379 2 # sentinel会向master发送心跳确认存活# 如果master在“一定时间范围”内不回应PONG 或者是回复了一个错误消息# 那么这个sentinel会主观地认为这个master已经不可用了# (subjectively down, 也简称为SDOWN)。# down-after-milliseconds 用来指定这个“一定时间范围”,单位是毫秒,默认30秒。sentinel down-after-milliseconds T1 15000# failover过期时间。# 当failover开始后,在此时间内仍然没有触发任何failover操作,当前sentinel将会认为此次failoer失败。# 默认180秒,即3分钟。sentinel failover-timeout T1 120000# 在发生failover时,这个选项指定了最多可以有多少个slave同时对新的master进行同步。# 这个数字越小,完成failover所需的时间就越长;# 这个数字越大,就意味着越多的slave因为replication而不可用。# 可以通过将这个值设为 1 来保证每次只有一个slave处于不能处理命令请求的状态。sentinel parallel-syncs T1 1# sentinel 连接密码验证# sentinel auth-pass <master_name> xxxxx# 发生切换之后执行的一个自定义脚本# sentinel notification-script <master-name> <script-path># sentinel client-reconfig-script <master-name> <script-path>Sentinel 也需要集群化,以确保:某个/些 Sentinel节点挂了,仍然可以实现Redis主从切换;Redis客户端可以通过任意一个Sentinel读取/写入信息。启动Sentinel:redis-sentinel /path/to/sentinel.conf启动后,通过Sentinel的自动识别,配置文件中会自动加上已识别的Redis集群节点和Sentinel集群节点。3.实现流程Sentinel集群通过配置文件发现master,启动时会监控master;向master发送info命令,获取其所有slave节点;Sentinel集群向Redis主从服务器发送hello信息(心跳),包括Sentinel本身的ip、端口、id等内容,以此来向其他Sentinel宣告自己的存在;Sentinel集群通过订阅接收其他Sentinel发送的hello信息,以此来发现监视同一个主服务器的其他Sentinel;集群之间会互相创建命令连接用于通信,因为已经有主从服务器作为发送和接收hello信息的中介,Sentinel之间不会创建订阅连接;Sentinel集群使用ping命令来检测实例的状态,如果在指定的时间内(down-after-milliseconds)没有回复或则返回错误的回复,那么该实例被判为下线;当failover主备切换被触发后,并不会马上进行,还需要Sentinel中的大多数sentinel授权后才可以进行failover,即进行failover的Sentinel会去获得指定quorum个的Sentinel的授权,成功后进入ODOWN状态。如在5个Sentinel中配置了2个quorum,等到2个Sentinel认为master死了就执行failover。Sentinel向选为master的slave发送 SLAVEOF NO ONE 命令,选择slave的条件是Sentinel首先会根据slaves的优先级来进行排序,优先级越小排名越靠前。如果优先级相同,则查看复制的下标,哪个从master接收的复制数据多,哪个就靠前。如果优先级和下标都相同,就选择进程ID较小的。Sentinel被授权后,它将会获得宕掉的master的一份最新配置版本号(config-epoch),当failover执行结束以后,这个版本号将会被用于最新的配置,通过广播形式通知其它sentinel,其它的sentinel则更新对应master的配置。注意:因为redis采用的是异步复制,没有办法避免数据的丢失。可以在redis.conf通过以下配置来使得数据不会丢失:// master最少得有多少个健康的slave存活才能执行写命令 min-slaves-to-write 1// 延迟小于min-slaves-max-lag秒的slave才认为是健康的slavemin-slaves-max-lag 10当所有Slave都不符合条件时,master将停止写入。4.故障转移发生故障转移时,需要进行领导者选举。sentinel首先会根据slaves的优先级来进行排序,优先级越小排名越靠前;如果优先级相同,则查看复制的下标,哪个从master接收的复制数据多,哪个就靠前;如果优先级和下标都相同,就选择RunID较小的那个;如果一个redis的slave优先级配置为0,那么它将永远不会被选为master。故障转移过程领导者Sentinel需要将一个salve提升为master,此slave必须为状态良好,不能处于SDOWN/ODOWN状态。“+failover-triggered”: Leader开始进行failover,此后紧跟着“+failover-state-wait-start”,wait数秒。“+failover-state-select-slave”: Leader开始查找合适的slave“+selected-slave”: 已经找到合适的slave“+failover-state-sen-slaveof-noone”: Leader向slave发送“slaveof no one”指令,此时slave已经完成角色转换,此slave即为master“+failover-state-wait-promotition”: 等待其他sentinel确认slave“+promoted-slave”:确认成功“+failover-state-reconf-slaves”: 开始对slaves进行reconfig操作“+slave-reconf-sent”:向指定的slave发送“slaveof”指令,告知此slave跟随新的master“+slave-reconf-inprog”: 此slave正在执行slaveof + SYNC过程,如过slave收到“+slave-reconf-sent”之后将会执行slaveof操作。“+slave-reconf-done”: 此slave同步完成,此后leader可以继续下一个slave的reconfig操作。循环 step 7“+failover-end”: 故障转移结束“+switch-master”:故障转移成功后,各个sentinel实例开始监控新的master。5.增加和删除节点Sentinel会通过PUB/SUB自动识别新增节点。删除流程:停止所要删除的sentinel发送一个SENTINEL RESET 命令给所有其它的sentinel实例,如果你想要重置指定master上面的sentinel,只需要把号改为特定的名字,注意,需要一个接一个发,每次发送的间隔不低于30秒(down-after-milliseconds);检查一下所有的sentinels是否都有一致的当前sentinel数;参考资料:https://www.cnblogs.com/zhouj…http://www.cnblogs.com/zhouji...https://segmentfault.com/u/be… ...

March 12, 2019 · 1 min · jiezi

Redis Sentinel 哨兵模式搭建小结

背景最近项目需求,接触到了Redis的搭建,简单记录下搭建过程中遇到的坑总体配置192.168.1.100:6379 -> master 192.168.1.101:6379 -> slave 192.168.1.102:6379 -> slave 192.168.1.100:26379 -> sentinel 192.168.1.101:26379 -> sentinel 192.168.1.102:26379 -> sentinel搭建步骤1.安装redis# 解压tar -xvf /usr/local/redis-3.2.11.tar.gzmkdir -p /usr/local/redis/bincp /usr/local/redis/src/{redis-benchmark,redis-check-aof,redis-check-rdb,redis-cli,redis-sentinel,redis-server,redis-trib.rb} /usr/local/redis/binmkdir -p /u01/redis/{6379/{log,data,pid,conf},26379/{log,data,pid,conf}# 添加环境变量echo “export PATH=/usr/local/redis/bin:$PATH” >> /etc/profilesource /etc/profile2.redis-6379配置redis节点配置基本如下,把如下配置分别cp到三台虚拟机的/u01/redis/6379/conf/redis_6379.confbind 0.0.0.0protected-mode nodaemonize yespidfile “/u01/redis/6379/pid/redis_6379.pid"port 6379tcp-backlog 511timeout 0tcp-keepalive 0loglevel noticelogfile “/u01/redis/6379/log/redis_6379.log"databases 16stop-writes-on-bgsave-error yesrdbcompression yesrdbchecksum yesdbfilename “dump.rdb"dir “/u01/redis/6379/data"slave-serve-stale-data yesslave-read-only yesrepl-diskless-sync norepl-diskless-sync-delay 5repl-disable-tcp-nodelay noslave-priority 100min-slaves-to-write 1min-slaves-max-lag 10appendonly noappendfilename “appendonly.aof"appendfsync everysecno-appendfsync-on-rewrite noauto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mbaof-load-truncated yeslua-time-limit 5000slowlog-log-slower-than 10000slowlog-max-len 128latency-monitor-threshold 0notify-keyspace-events ““hash-max-ziplist-entries 512hash-max-ziplist-value 64list-max-ziplist-entries 512启动服务# 在三台虚拟机上分别执行redis-server /u01/redis/6379/conf/redis_6379.conf建立主从关系# 在192.168.1.101redis-cli -p 6379 SLAVEOF 192.168.1.100 6379# 在192.168.1.102redis-cli -p 6379 SLAVEOF 192.168.1.100 6379查看Replication192.168.1.101:6379> info replication# Replicationrole:masterconnected_slaves:2min_slaves_good_slaves:2slave0:ip=192.168.1.102,port=6379,state=online,offset=9577826,lag=1slave1:ip=192.168.1.103,port=6379,state=online,offset=9577965,lag=0master_repl_offset:9577965repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:8529390repl_backlog_histlen:1048576192.168.1.102:6379> info replication# Replicationrole:slavemaster_host:192.168.1.101master_port:6379master_link_status:upmaster_last_io_seconds_ago:0master_sync_in_progress:0slave_repl_offset:9600220slave_priority:100slave_read_only:1connected_slaves:0min_slaves_good_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0192.168.1.103:6379> info replication# Replicationrole:slavemaster_host:192.168.1.101master_port:6379master_link_status:upmaster_last_io_seconds_ago:0master_sync_in_progress:0slave_repl_offset:9612675slave_priority:100slave_read_only:1connected_slaves:0min_slaves_good_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:03.sentinel-6379配置sentinel节点配置基本如下,把如下配置分别cp到三台虚拟机的/u01/redis/26379/conf/sentinel_26379.confsentinel monitor mymaster 后监控的是redis中的master节点,也就是192.168.1.100,所以这个文件在三台机器上是相同的port 26379bind 0.0.0.0daemonize yesprotected-mode nodir “/u01/redis/26379/tmp"logfile “/u01/redis/26379/log/sentinel_26379.log"sentinel monitor mymaster 192.168.1.100 6379 1等待启动完毕后观察/u01/redis/26379/conf/sentinel_26379.conf文件变化查看sentinel状态用info sentinelredis-cli -h 192.168.1.100 -p 26379 info sentinel# Sentinelsentinel_masters:1sentinel_tilt:0sentinel_running_scripts:0sentinel_scripts_queue_length:0sentinel_simulate_failure_flags:0master0:name=zhuanche01,status=ok,address=192.168.1.100:6379,slaves=2,sentinels=3总结我搭建的时候遇到了192.168.1.101、192.168.1.102上的sentinel启动后一段时间出错的问题,后来发现是没有监控master再就是出问题了多看log来年要多写笔记,年纪大了,记忆力越来越差! ...

December 27, 2018 · 1 min · jiezi

聊聊sentinel的SystemSlot

序本文主要研究一下sentinel的SystemSlotSystemSlotsentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemSlot.javapublic class SystemSlot extends AbstractLinkedProcessorSlot<DefaultNode> { @Override public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object… args) throws Throwable { SystemRuleManager.checkSystem(resourceWrapper); fireEntry(context, resourceWrapper, node, count, args); } @Override public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object… args) { fireExit(context, resourceWrapper, count, args); }}这里是通过SystemRuleManager.checkSystem(resourceWrapper)进行系统限流判断SystemRuleManager public static void checkSystem(ResourceWrapper resourceWrapper) throws BlockException { // 确定开关开了 if (checkSystemStatus.get() == false) { return; } // for inbound traffic only if (resourceWrapper.getType() != EntryType.IN) { return; } // total qps double currentQps = Constants.ENTRY_NODE == null ? 0.0 : Constants.ENTRY_NODE.successQps(); if (currentQps > qps) { throw new SystemBlockException(resourceWrapper.getName(), “qps”); } // total thread int currentThread = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.curThreadNum(); if (currentThread > maxThread) { throw new SystemBlockException(resourceWrapper.getName(), “thread”); } double rt = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.avgRt(); if (rt > maxRt) { throw new SystemBlockException(resourceWrapper.getName(), “rt”); } // 完全按照RT,BBR算法来 if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) { if (currentThread > 1 && currentThread > Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000) { throw new SystemBlockException(resourceWrapper.getName(), “load”); } } } public static double getCurrentSystemAvgLoad() { return statusListener.getSystemAverageLoad(); }先判断qps,在判断总线程数、之后判断rt,最后判断系统负载有没有超过限制StatisticNodesentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java @Override public long successQps() { return rollingCounterInSecond.success() / IntervalProperty.INTERVAL; } @Override public int curThreadNum() { return curThreadNum.get(); } @Override public long avgRt() { long successCount = rollingCounterInSecond.success(); if (successCount == 0) { return 0; } return rollingCounterInSecond.rt() / successCount; } @Override public long maxSuccessQps() { return rollingCounterInSecond.maxSuccess() * SampleCountProperty.sampleCount; } @Override public long minRt() { return rollingCounterInSecond.minRt(); }successQps、curThreadNum、avgRt、maxSuccessQps、minRt指标在StatisticNode上进行维护SystemStatusListenersentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.javapublic class SystemStatusListener implements Runnable { volatile double currentLoad = -1; volatile String reason = StringUtil.EMPTY; static final int processor = ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors(); public double getSystemAverageLoad() { return currentLoad; } @Override public void run() { try { if (!SystemRuleManager.getCheckSystemStatus()) { return; } // system average load OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); currentLoad = operatingSystemMXBean.getSystemLoadAverage(); StringBuilder sb = new StringBuilder(); if (currentLoad > SystemRuleManager.getHighestSystemLoad()) { sb.append(“load:”).append(currentLoad).append(";"); sb.append(“qps:”).append(Constants.ENTRY_NODE.passQps()).append(";"); sb.append(“rt:”).append(Constants.ENTRY_NODE.avgRt()).append(";"); sb.append(“thread:”).append(Constants.ENTRY_NODE.curThreadNum()).append(";"); sb.append(“success:”).append(Constants.ENTRY_NODE.successQps()).append(";"); sb.append(“minRt:”).append(Constants.ENTRY_NODE.minRt()).append(";"); sb.append(“maxSuccess:”).append(Constants.ENTRY_NODE.maxSuccessQps()).append(";"); RecordLog.info(sb.toString()); } } catch (Throwable e) { RecordLog.info(“could not get system error “, e); } }}系统负载是通过SystemRuleManager定时调度SystemStatusListener,通过OperatingSystemMXBean去获取 static { checkSystemStatus.set(false); statusListener = new SystemStatusListener(); scheduler.scheduleAtFixedRate(statusListener, 5, 1, TimeUnit.SECONDS); currentProperty.addListener(listener); } 小结sentinel的SystemSlot是通过判断系统相关指标来进行限流,主要的指标为qps、总线程数、rt、系统负载。docSystemSlot ...

September 5, 2018 · 2 min · jiezi