1、什么是Sentineljava
Sentinel,中文翻译为哨兵,是为微服务提供流量控制、熔断降级的功能,它和Hystrix提供的功能同样,能够有效的解决微服务调用产生的“雪崩效应”,为微服务系统提供了稳定性的解决方案。随着Hystrix进入了维护期,再也不提供新功能,Sentinel是一个不错的替代方案。一般状况下,Hystrix采用线程池对服务的调用进行隔离,Sentinel采用了用户线程对接口进行隔离,两者相比,Hystrix是服务级别的隔离,Sentinel提供了接口级别的隔离,Sentinel隔离级别更加精细,另外Sentinel直接使用用户线程提供限制,相比Hystrix的线程池隔离,减小了线程切换的开销。另外Sentinel的DashBoard提供了在线更改限流规则的配置,也更加的优化。node
2、开源生态spring
Sentinel和Hystrix数组
功能 | Sentinel | Hystrix |
---|---|---|
隔离策略 | 信号量隔离(并发线程数限流) | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间、异常比率、异常数 | 基于异常比率 |
实时统计实现 | 滑动窗口(LeapArray) | 滑动窗口(基于RxJava) |
动态规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于QPS,支持基于调用关系的限流 | 有限的支持 |
流量整形 | 支持预热模式、匀速器模式、预热排队模式 | 不支持 |
系统自适应保护 | 支持 | 不支持 |
控制台 | 可配置规则、查看秒级监控、机器发现等 | 简单的监控查看 |
3、Sentinel特性浏览器
一、丰富的应用场景缓存
Sentinel承接了阿里巴巴近十年双十一大促流量的核心场景,例如秒杀(突发流量控制在系统容量能够承受的范围)、消息削峰填谷、实时熔断下游不可用应用等。并发
二、完备的实时监控app
Sentinel同时提供实时的监控功能。咱们能够在控制台中看到接入应用的单台机器秒级数据,甚至500台如下规模的集群的汇总进行状况。框架
三、普遍的开源生态async
Sentinel提供开箱即用的与其它开源框架的整合模块,例如与spring cloud、dubbo、grpc的整合。咱们只须要引入响应的依赖并进行简单的配置便可快速的接入Sentinel。
四、完美的SPI扩展点
Sentinel提供简单易用、完善的SPI扩展点。咱们能够经过实现扩展点,快速的定制逻辑。例如定制规则管理、适配数据源等。
4、资源和规则
资源是Sentinel的关键概念。它能够是java应用程序中的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至能够是一段代码。只是经过Sentinel API定义的代码,就是资源,可以被Sentinel保护起来,大部分状况下,可使用方法签名,URL,甚至服务名称做为资源名来表示资源。
围绕资源的实时状态设定的规则,能够包括流量控制规则、熔断降级规则以及系统保护规则。全部规则能够动态实时调整。
Sentinel中调用SphU或者SphO的entry方法获取限流资源,不一样的是前者获取限流资源失败时会跑BlockException异常,后者返回false,两者的实现都是基于CtSph类完成的。
5、核心概念
一、Resource
resource是Sentinel中最重要的一个概念,Sentinel经过资源来保护具体的业务代码或其它后方服务。Sentinel把复杂的逻辑给屏蔽了,用户只须要为受保护的代码或服务定义一个资源,而后定义规则就能够了,剩下的统统交给Sentinel来处理。而且资源和规则是解耦的,规则甚至能够在运行时动态修改。定义完资源后,就能够经过在程序中埋点来保护你本身的服务,埋点的方式有两种:
(1)try-catch方式(经过SphU.entry(...)),当catch到BlockException时执行异常处理或fallback。
(2)if-else方式(经过SphO.entry(...)),当返回false时执行异常处理或fallback。
以上两种方式都是经过硬编码的形式定义资源而后进行资源埋点的,对业务代码的侵入太大,从0.1.1版本开始,Sentinel加入了注解的支持,能够经过注解来定义资源,具体的注解为:SentinelResource。经过注解除了能够定义资源外,还能够指定blockHandler和fallback方法。
在Sentinel中具体表示资源的类:ResourceWrapper,它是一个抽象的包装类,包装了资源的Name和EntryType。他有两个实现类,分别是:StringResourceWrapper和MethodResourceWrapper。顾名思义,StringResourceWrapper是经过对一串字符进行包装,是一个通用的资源包装类,MethodResourceWrapper是对方法调用的包装。
二、Context
Context是对资源操做时的上下文环境,每一个资源操做(针对resource的entry和exit)必须属于一个Context,若是程序中未指定Context,会建立name为“Sentinel_default_context”的默认Context。一个Context生命周期内可能有多个资源操做,Context生命周期内的最后一个资源exit时会清理该Context,这也预示着整个Context生命周期的结束。Context主要属性以下:
public class Context { // context名字,默认名字 "sentinel_default_context" private final String name; // context入口节点,每一个context必须有一个entranceNode private DefaultNode entranceNode; // context当前entry,Context生命周期中可能有多个Entry,全部curEntry会有变化 private Entry curEntry; // The origin of this context (usually indicate different invokers, e.g. service consumer name or origin IP). private String origin = ""; private final boolean async; }
一个Context生命周期内Context只能初始化一次,存到ThreadLocal中,而且只有在非NULL时才会进行初始化。若是想在调用SphU.entry()或SphO.entry()前,自定义一个context,则经过ContextUtil.enter()方法来建立。context保存在ThreadLocal中,每次执行的时候会优先到ThreadLocal中获取,为null时会建立一个context。当Entry执行exit方法时,若是entry的parent节点为null,表示当前context中最外层的entry了,此时将threadLocals中的context清空。
三、Entry
每次执行SphU.entry()或SphO.entry()都会返回一个Entry,Entry表示一次资源操做,内部会保存单签invocation信息。在一个context声明周期中屡次资源操做,也就是对应多个Entry,parent/child结构保存在Entry实例中,Entry类CtEntry结构以下:
class CtEntry extends Entry { protected Entry parent = null; protected Entry child = null; protected ProcessorSlot<Object> chain; protected Context context; } public abstract class Entry implements AutoCloseable { private long createTime; private Node curNode; /** * {@link Node} of the specific origin, Usually the origin is the Service Consumer. */ private Node originNode; private Throwable error; // 是否出现异常 protected ResourceWrapper resourceWrapper; // 资源信息 }
四、DefaultNode
Node默认实现类DefaultNode,该类还有一个子类EntranceNode;context有一个entranceNode属性,Entry中有一个curNode属性。
看到这里,你是否是有疑问?为何一个context有且仅有一个DefaultNode,咱们的resouece跑哪去了呢,其实,这里的一个context有且仅有一个DefaultNode是在NodeSelectorSlot范围内,NodeSelectorSlot是ProcessorSlotChain中的一环,获取ProcessorSlotChain是根据Resource维度来的。总结为一句话就是:针对同一个Resource,多个context对应多个DefaultNode;针对不一样Resource,(不论是否是同一个context)对应多个不一样DefaultNode。这还没看明白 : ),好吧,我不bb了,上图吧:
DefaultNode结构以下:
public class DefaultNode extends StatisticNode { private ResourceWrapper id; /** * The list of all child nodes. * 子节点集合 */ private volatile Set<Node> childList = new HashSet<>(); /** * Associated cluster node. */ private ClusterNode clusterNode; }
一个Resouce只有一个clusterNode,多个defaultNode对应一个clusterNode,若是defaultNode.clusterNode为null,则在ClusterBuilderSlot.entry中会进行初始化。
同一个Resource,对应同一个ProcessorSlotChain,这块处理逻辑在lookProcessChain方法中,以下:
ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) { ProcessorSlotChain chain = chainMap.get(resourceWrapper); if (chain == null) { synchronized (LOCK) { chain = chainMap.get(resourceWrapper); if (chain == null) { // Entry size limit. if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) { return null; } chain = SlotChainProvider.newSlotChain(); Map<ResourceWrapper, ProcessorSlotChain> newMap = newHashMap<ResourceWrapper, ProcessorSlotChain>( chainMap.size() + 1); newMap.putAll(chainMap); newMap.put(resourceWrapper, chain); chainMap = newMap; } } } return chain; }
五、StatisticNode
StatisticNode中保存了资源的实时统计数据(基于滑动时间窗口机制),经过这些统计数据,sentinel才能进行限流、降级等一系列操做。StatisticNode属性以下:
public class StatisticNode implements Node { /** * 秒级的滑动时间窗口(时间窗口单位500ms) */ private transient volatile Metric rollingCounterInSecond = newArrayMetric(SampleCountProperty.SAMPLE_COUNT, IntervalProperty.INTERVAL); /** * 分钟级的滑动时间窗口(时间窗口单位1s) */ private transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000,false); /** * The counter for thread count. * 线程个数用户触发线程数流控 */ private LongAdder curThreadNum = new LongAdder(); } public class ArrayMetric implements Metric { private final LeapArray<MetricBucket> data; } public class MetricBucket { // 保存统计值 private final LongAdder[] counters; // 最小rt private volatile long minRt; }
其中MetricBucket.counters数组大小为MetricEvent枚举值的个数,每一个枚举对应一个统计项,好比PASS表示经过个数,限流可根据经过的个数和设置的限流规则配置count大小比较,得出是否触发限流操做,全部枚举值以下:
public enum MetricEvent { PASS, // Normal pass. BLOCK, // Normal block. EXCEPTION, SUCCESS, RT, OCCUPIED_PASS }
六、Slot
Slot是sentinel中很是重要的概念,sentinel的工做流程就是围绕着一个个插槽所组成的插槽链来展开的。须要注意的是每一个插槽都有本身的职责,他们各司其职完美的配合,经过必定的编排顺序,来达到最终的限流降级。默认的各个插槽之间的顺序是固定的,由于有的插槽须要依赖其余的插槽计算出来的结果才能进行工做。
sentinel经过SlotChainBuilder做为SPI接口,使得Slot Chain具有了扩展的能力。咱们能够经过实现SlotChainBuilder接口加入自定义Slot而且定义编排各个slot之间的排序,从而能够给sentinel添加自定义的功能。
那SlotChain是在哪建立的呢?是在 CtSph.lookProcessChain() 方法中建立的,而且该方法会根据当前请求的资源先去一个静态的HashMap中获取,若是获取不到才会建立,建立后会保存到HashMap中。这就意味着,同一个资源会全局共享一个SlotChain。默认生成ProcessorSlotChain为:
// DefaultSlotChainBuilder 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 SystemSlot()); chain.addLast(new AuthoritySlot()); chain.addLast(new FlowSlot()); chain.addLast(new DegradeSlot()); return chain;
6、springcloud如何使用sentinel
学习了sentinel核心概念以后,感受整我的都很差了,真的是晦涩难懂,来个helloworld,轻松一下。
一、pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
二、 controller
@RestController public class TestController { @GetMapping(value = "/hello") @SentinelResource("hello") public String hello() { return "Hello Sentinel"; } }
三、引入dashboard
直接下载sentinel-dashboard的jar包。
默认是8080端口,在浏览器输入:localhost:8080,默认帐号密码:sentinel:sentinel,看到控制台界面为部署成功。
四、application.properties
server.port=8088 spring.application.name=spring-cloud-alibaba-sentinel-demo # sentinel dashboard spring.cloud.sentinel.transport.dashboard=localhost:8080
五、 启动spring boot 项目,继续访问localhost:8080,会看到以下界面
六、 使用Sentinel实现接口限流(在控制台)
七、测试
经过上面的配置,实现的是/hello接口qps最大是2,若是qps大于2,则快速失败,配置完成,点击保存,咱们快速刷新浏览器,会发现快速失败
7、总结
本文主要介绍了Sentinel的概念、特性、与Hystrix的区别、一些核心概念和与SpringCloud的简单整合。随着微服务的流行,服务和服务之间的稳定性变得愈来愈重要。 Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。
每一篇博客都是一种经历,程序猿生涯的痕迹,知识改变命运,命运要由本身掌控,愿你游历半生,归来还是少年。
欲速则不达,欲达则欲速!
更多精彩内容,首发公众号【素小暖】,欢迎关注。