Sentinel的SPI和责任链模式学习

简介: 通常spi可以配合责任链模式、策略模式使用。此时spi,类似于一个上下文的过程,拿到所有的实现class。与之类似的还有:SpringUtil.getBeans或者ApplicationContext.getBeansOfType。在一些固定的场景下,处理业务的流程过程通常比较明确,第一步做什么,第二步做什么,第三步做什么的时候,这个时候就可以使用责任链模式在处理。也即存在固定的模式的时候,就可以使用。同时在Netty、Sentinel中有重要使用场景,其过程是一个pipeline流水线的过程。

sentinel作为限流降级的流量防卫兵,类似于Hystix。

一、SPI的使用场景

通常spi可以配合责任链模式、策略模式使用。此时spi,类似于一个上下文的过程,拿到所有的实现class。

与之类似的还有:SpringUtil.getBeans或者ApplicationContext.getBeansOfType。

二、责任链模式的使用场景

在一些固定的场景下,处理业务的流程过程通常比较明确,第一步做什么,第二步做什么,第三步做什么的时候,这个时候就可以使用责任链模式在处理。也即存在固定的模式的时候,就可以使用。同时在Netty、Sentinel中有重要使用场景,其过程是一个pipeline流水线的过程。

三、sentinel是如何实现相关责任链模式的组装的呢?

首先通过entry=SphU.entry(KEY);

进入到com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder#build这个方法,进行加载,进行实列化。

这个过程中,包括两个过程加载load和创建实列化的过程。而这个load的过程就是执行spi获取slot列表的过程。

完成之后,就可以通过链拿到entry,也即chain.entry(context, resourceWrapper, null, count, prioritized, args);

拿到链路信息,然后执行entry入口操作,进入到fireEntry。这个过程会进入入链过程,这样就可以将所有的slot串联起来。

四、代码中的实现过程

image-20230202153756387.png

执行里面的请求方法:

可以看到入口方法:

entry=SphU.entry(KEY);

进行责任链骨架实列化:

List<ProcessorSlot>sortedSlotList=SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();

执行spi操作:

/**

    * Load all Provider instances of the specified Service, sorted by order value in class's {@link Spi} annotation

    *

    * @return Sorted Provider instances list

    */

   publicList<S>loadInstanceListSorted() {

       load();

       returncreateInstanceList(sortedClassList);

   }

可以看到load的过程是会将slot进行加载:

com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot

com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot

com.alibaba.csp.sentinel.slots.logger.LogSlot

com.alibaba.csp.sentinel.slots.statistic.StatisticSlot

com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot

com.alibaba.csp.sentinel.slots.system.SystemSlot

com.alibaba.csp.sentinel.slots.block.flow.FlowSlot

com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot

然后创建实列列表,创建实列的过程其实是一个double check的过程。

privateScreateInstance(Class<?extendsS>clazz, booleansingleton) {

       Sinstance=null;

       try {

           if (singleton) {

               instance=singletonMap.get(clazz.getName());

               if (instance==null) {

                   synchronized (this) {

                       instance=singletonMap.get(clazz.getName());

                       if (instance==null) {

                           instance=service.cast(clazz.newInstance());

                           singletonMap.put(clazz.getName(), instance);

                       }

                   }

               }

           } else {

               instance=service.cast(clazz.newInstance());

           }

       } catch (Throwablee) {

           fail(clazz.getName() +" could not be instantiated");

       }

       returninstance;

   }

可以看到相关的slot,分为两类:

第一类是负责资源指标数据统计的ProcessorSlot:

com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot

com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot

第二类是实现限流、熔断、降级的ProcessorSlot:

com.alibaba.csp.sentinel.slots.logger.LogSlot

com.alibaba.csp.sentinel.slots.statistic.StatisticSlot

com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot

com.alibaba.csp.sentinel.slots.system.SystemSlot

com.alibaba.csp.sentinel.slots.block.flow.FlowSlot

com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot

其中:NodeSelectorSlot必须在ClusterBuilderSlot之前实列化,否则的话,会报错。

同时我们可以看到slot是一个单向链表,而Entry则是一个双向链表,entry可进可退。

既然是链表,那么必然有一个前驱节点和后继节点之间会产生联系:

publicinterfaceProcessorSlot<T> {

   voidentry(Contextcontext, ResourceWrapperresourceWrapper, Tparam, intcount, booleanprioritized,

              Object... args) throwsThrowable;

   voidfireEntry(Contextcontext, ResourceWrapperresourceWrapper, Objectobj, intcount, booleanprioritized,

                  Object... args) throwsThrowable;

   voidexit(Contextcontext, ResourceWrapperresourceWrapper, intcount, Object... args);

   voidfireExit(Contextcontext, ResourceWrapperresourceWrapper, intcount, Object... args);

}

因此可以看到SlotChain产生的联系:

entry->fireEntry

exit->fireExit

同时可以看到fireEntry中的方法:

@Override

   publicvoidfireEntry(Contextcontext, ResourceWrapperresourceWrapper, Objectobj, intcount, booleanprioritized, Object... args) throwsThrowable {

       //next的过程

       if (next!=null) {

           next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);

       }

   }

fireEntry的过程即是一个next的过程。

那么节点的数据又在哪里呢? chainMap,从lookProcessChain可以看到答案,存在内存中。

   ProcessorSlot<Object>lookProcessChain(ResourceWrapperresourceWrapper) {

       //通过资源拿到处理器槽链,如果链为空,则double check

       ProcessorSlotChainchain=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) {

                       returnnull;

                   }

                   // 创建一个新的槽链

                   chain=SlotChainProvider.newSlotChain();

                   Map<ResourceWrapper, ProcessorSlotChain>newMap=newHashMap<ResourceWrapper, ProcessorSlotChain>(

                       chainMap.size() +1);

                   newMap.putAll(chainMap);

                   newMap.put(resourceWrapper, chain);

                   chainMap=newMap;

               }

           }

       }

       returnchain;

   }

数据结构为:

privatestaticvolatileMap<ResourceWrapper, ProcessorSlotChain>chainMap

       =newHashMap<ResourceWrapper, ProcessorSlotChain>();

这样的话,整个骨架就可以通过entry搭起来了。

与之类似的,可以实现其功能的还有:SpringUtil.getBeans或者ApplicationContext.getBeansOfType。为啥不使用这个呢?这个不是更方便吗?

原因在于,如果使用这个,必须引进Spring框架。同时为了轻量级一些,方便维护。

五、功能

完成这个过程后,我们可以看到页面上的相关指标:

image.png

没有流控前:

image.png

流控后:

image-20230202211733631.png

参考书籍:吴就业  实战Alibaba Sentinel:深度解析微服务高并发流量治理


目录
相关文章
|
25天前
|
算法 Java API
Sentinel学习圣经:从入门到精通 Sentinel,最全详解 (40+图文全面总结)
尼恩给大家做一下系统化、体系化的梳理,联合社群小伙伴,来一个Sentinel学习圣经:从入门到精通Sentinel。
|
6月前
|
Java 数据安全/隐私保护 Sentinel
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
|
SpringCloudAlibaba 监控 Java
SpringCloud Alibaba学习(五):Sentinel的介绍与搭建
SpringCloud Alibaba学习(五):Sentinel的介绍与搭建
391 1
SpringCloud Alibaba学习(五):Sentinel的介绍与搭建
|
SpringCloudAlibaba Java UED
SpringCloud Alibaba学习(八):Sentinel的热点规则
热点即经常访问的数据,很多时候我们希望统计或者限制某个热点数据中访问频次最高的TopN数据,并对其访问进行限流或者其它操作 。
267 0
SpringCloud Alibaba学习(八):Sentinel的热点规则
|
SpringCloudAlibaba Nacos 数据库
SpringCloud Alibaba学习(十一):Sentinel的规则持久化
在前面学习Sentinel时我们发现:一旦重启应用,sentinel规则将消失,生产环境需要将配置规则进行持久化 ,非常不方便。这就需要本文介绍的sentinel的规则持久化。
222 0
SpringCloud Alibaba学习(十一):Sentinel的规则持久化
|
SpringCloudAlibaba 负载均衡 Java
SpringCloud Alibaba学习(十):Sentinel的服务熔断功能(Sentinel整合Ribbon+OpenFeign+fallback)
SpringCloud Alibaba学习(十):Sentinel的服务熔断功能(Sentinel整合Ribbon+OpenFeign+fallback)
326 0
SpringCloud Alibaba学习(十):Sentinel的服务熔断功能(Sentinel整合Ribbon+OpenFeign+fallback)
|
SpringCloudAlibaba API Sentinel
SpringCloud Alibaba学习(九):Sentinel的@SentinelResource注解详解
SpringCloud Alibaba学习(九):Sentinel的@SentinelResource注解详解
171 0
SpringCloud Alibaba学习(九):Sentinel的@SentinelResource注解详解
|
SpringCloudAlibaba Sentinel 微服务
SpringCloud Alibaba学习(七):Sentinel的降级规则
Sentinel 熔断降级会在调用链路中某个资源出现不稳定状态时(例如调用超时或异常比例升高),对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联错误。
224 0
SpringCloud Alibaba学习(七):Sentinel的降级规则
|
SpringCloudAlibaba Sentinel 微服务
SpringCloud Alibaba学习(六):Sentinel的流控规则
SpringCloud Alibaba学习(六):Sentinel的流控规则
186 0
SpringCloud Alibaba学习(六):Sentinel的流控规则
|
算法 Sentinel
Sentinel学习二
如果我们不对Sentinel的异常提示做自定义,那么此时的提示是非常不详细的。如果做了自定义,就可以看到下面自定义的提示。sentinel提供了@SentinelResource注解帮助我们来实现自定义的熔断限流后的自定义方法处理,可以根据sentinel的aop可以看到处理的example,从而更为清晰的认识sentinel强大的功能。
111 0
Sentinel学习二