当前位置: 首页 > 后端技术 > Java

Sentinel的整体工作流程

时间:2023-04-02 00:30:09 Java

一、Sentinel中的ProcessorSlot职责链在上一篇文章中,我们介绍了Sentinel中的七个ProcessorSlot。它们分为两类,一类用于数据统计,一类用于降级。Sentinel的整体工作流程是采用责任链模式,将所有的ProcessorSlot按照一定的顺序组成一个单向链表。辅助完成资源指标数据统计的ProcessorSlot必须在实现降级功能的ProcessorSlot之前。原因很简单。降级功能需要基于资源。当然,如果一个ProcessorSlot不依赖指标数据实现降级功能,那么ProcessorSlot的位置就没有限制。ProcessorSlotChain用于将ProcessorSlots串成一个单向链表。这个ProcessorSlotChain是由SlotChainBuilder构建的。默认情况下,注册的ProcessorSlots和SlotChainBuilder构建的ProcessorSlotChain的顺序如下代码所示。公共类DefaultSlotChainBuilder实现SlotChainBuilder{@OverridepublicProcessorSlotChainbuild(){ProcessorSlotChainchain=newDefaultProcessorSlotChain();chain.addLast(newNodeSelectorSlot());chain.addLast(newClusterBuilderSlot());chain.addLast(newLogSlot());chain.addLast(newStatisticSlot());chain.addLast(newAuthoritySlot());chain.addLast(newSystemSlot());chain.addLast(newFlowSlot());chain.addLast(newDegradeSlot());返回链;}}如果是自定义的ProcessorSlot可以通过AbstractLinkedProcessorSlot进行构造publicabstractclassAbstractLinkedProcessorSlotimplementsProcessorSlot{//当前节点的下一个节点privateAbstractLinkedProcessorSlotnext=null;publicvoidsetNext(AbstractLinkedProcessorSlotnext){this.next=next;}@OverridepublicvoidfireEntry(Context上下文,ResourceWrapperresourceWrapper,Objectobj,intcount,booleanprioritized,Object...args)throwsThrowable{if(next!=null){Tt=(T)obj;//调用下一个ProcessorSlot的入口方法next.entry(context,resourceWrapper,t,count,prioritized,args);}}@OverridepublicvoidfireExit(Contextcontext,ResourceWrapperresourceWrapper,intcount,Object...args){if(next!=null){//调用下一个ProcessorSlot的退出方法next.exit(context,resourceWrapper,count,参数);}}}ProcessorSlot接口定义如下:publicinterfaceProcessorSlot{//入口方法voidentry(Contextcontext,ResourceWrapperresourceWrapper,Tparam,intcount,booleanprioritized,Object...args)throwsThrowable;//调用下一个ProcessorSlot#entry方法voidfireEntry(Contextcontext,ResourceWrapperresourceWrapper,Objectobj,intcount,booleanprioritized,Object...args)throwsThrowable;//退出方法voidexit(Contextcontext,ResourceWrapperresourceWrapper,intcount,Object...args);//调用下一个ProcessorSlot#exit方法voidfireExit(Contextcontext,ResourceWrapperresourceWrapper,intcount,Object...args);}主要方法参数:context:当前调用链接上下文resourceWrapper:资源ID。param:通用参数,一般用于传递DefaultNode。count:Sentinel包装了需要保护的资源。这和锁的实现是一样的。它需要在继续执行之前获取锁。count函数与并发编程AQS中tryAcquire方法的参数相同。该计数表示应用程序占用的共享资源数。只有应用了足够多的共享资源,才能继续执行。比如线程池有200个线程,当前方法执行需要申请3个线程执行,则count为3。count的值一般为1。当配置的限流阈值类型为限流规则是线程,也就是说需要申请一个线程。当限流规则配置的限流阈值类型为qps时,表示需要申请1个token(假设token使用的是桶算法)。prioritized:表示是否对请求进行优先级排序,SphU#entry传入的值为false。args:调用方法传递的参数,用于实现热点参数限流。二、Sentinel的整体工作流程下面是一个不带Sentinel的适配器提供的例子ContextUtil.enter("上下文名称,例如:sentinel_spring_web_context");Entryentry=null;try{entry=SphU.entry("资源名称,例如:/rpc/openfein/demo",EntryType.IN(orEntryType.OUT));//执行业务方法returndoBusiness();}catch(Exceptione){if(!(einstanceofBlockException)){Tracer.trace(e);}throwe;}finally{if(entry!=null){entry.exit(1);}ContextUtil.exit();}该过程分为5个步骤:调用ContextUtil#enter方法。负责为当前调用链接创建Context,创建后保存在ThreadLocal中,并为Conetxt创建EntranceNode调用SphU#entry方法。这里是整体链接中的核心调用,CtSph负责为资源创建ResourceWrapper对象并为资源构建全局唯一的ProcessorSlotChain,为资源创建CtEntry并将CtEntry赋值给当前调用链接的Context.curEntry,最后调用ProcessorSlotChain#entry方法完成对单向链表入口方法的一次调用。如果抛出异常且异常类型不是BlockException,则调用Tracer#trace方法记录异常,为当前资源的DefaultNode自增异常号调用Entry#exit方法;调用ContextUtil#exit方法。再补充一点:调用入口方法后,首先进入FlowSlot的入口方法进行限流过滤:args)throwsThrowable{checkFlow(resourceWrapper,context,node,count,prioritized);fireEntry(context,resourceWrapper,node,count,prioritized,args);}然后查看对应的checkFlow方法:publicvoidcheckFlow(Function>ruleProvider,ResourceWrapperresource,Contextcontext,DefaultNodenode,intcount,booleanprioritized)throwsBlockException{if(ruleProvider==null||resource==null){return;}集合规则=ruleProvider。应用(资源.getName());if(rules!=null){for(FlowRulerule:rules){if(!canPassCheck(rule,context,node,count,prioritized)){thrownewFlowExce选项(rule.getLimitApp(),规则);}}}}这里获取到我们设置的限流过滤的FlowRule循环匹配资源。这就是Sentinel可以做到限流的原因。参考文章:06Sentinel中的责任链模式及Sentinel的整体工作流程Sentinel限流实现原理