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:深度解析微服务高并发流量治理


目录
相关文章
|
Java BI Sentinel
阿里Sentinel核心源码解析-责任链模式最佳实践(下)
阿里Sentinel核心源码解析-责任链模式最佳实践
240 0
阿里Sentinel核心源码解析-责任链模式最佳实践(下)
|
算法 数据安全/隐私保护 Sentinel
阿里Sentinel核心源码解析-责任链模式最佳实践(中)
阿里Sentinel核心源码解析-责任链模式最佳实践
248 0
阿里Sentinel核心源码解析-责任链模式最佳实践(中)
|
BI Sentinel
阿里Sentinel核心源码解析-责任链模式最佳实践(上)
阿里Sentinel核心源码解析-责任链模式最佳实践
214 0
阿里Sentinel核心源码解析-责任链模式最佳实践(上)
|
1天前
|
Java 数据安全/隐私保护 Sentinel
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
微服务学习 | Spring Cloud 中使用 Sentinel 实现服务限流
|
2天前
|
Java API Nacos
第十二章 Spring Cloud Alibaba Sentinel
第十二章 Spring Cloud Alibaba Sentinel
12 0
|
18天前
|
SpringCloudAlibaba 监控 Java
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
|
1月前
|
Java Nacos Sentinel
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(九)Nacos+Sentinel+Seata
205 0
|
1月前
|
SpringCloudAlibaba 监控 Java
SpringCloud Alibaba Sentinel实现熔断与限流--学习笔记
SpringCloud Alibaba Sentinel实现熔断与限流--学习笔记
24 0
|
1月前
|
SpringCloudAlibaba Sentinel 索引
【九】SpringCloud Alibaba之整合Sentinel(实现热点控制)
【九】SpringCloud Alibaba之整合Sentinel(实现热点控制)
34 1
|
1月前
|
SQL SpringCloudAlibaba Sentinel
【八】SpringCloud Alibaba之整合Sentinel(实现流量控制3)
【八】SpringCloud Alibaba之整合Sentinel(实现流量控制3)
33 2