日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
一篇帶給你Sentinel流控原理

我們?cè)陧?xiàng)目中添加 Spring Cloud Sentinel 依賴添加后 spring-cloud-starter-alibaba-sentinel 在 Spring-Boot 啟動(dòng)的過程中回去初始化 spring.factories 中的配置信息,如:SentinelWebAutoConfiguration 、SentinelAutoConfiguration 等配置文件來初始化

創(chuàng)新互聯(lián)主營(yíng)托克托網(wǎng)站建設(shè)的網(wǎng)絡(luò)公司,主營(yíng)網(wǎng)站建設(shè)方案,app開發(fā)定制,托克托h5小程序開發(fā)搭建,托克托網(wǎng)站營(yíng)銷推廣歡迎托克托等地區(qū)企業(yè)咨詢

再講代碼之前我先聲明一下我的版本號(hào)sentinel 1.8.0 。后續(xù)的所有內(nèi)容均基于該版本進(jìn)行

@ResoureSetinel 工作原理

配置流控規(guī)則我們最簡(jiǎn)單的方式就是通過 @ResoureSetinel 的方式來管理,該注解可以直接定義流控規(guī)則、降級(jí)規(guī)則。下面是一個(gè)簡(jiǎn)單的使用例子:

 
 
 
 
  1. @SentinelResource(value = "ResOrderGet", 
  2.                   fallback = "fallback", 
  3.                   fallbackClass = SentinelResourceExceptionHandler.class, 
  4.                   blockHandler = "blockHandler", 
  5.                   blockHandlerClass = SentinelResourceExceptionHandler.class 
  6.                  ) 
  7. @GetMapping("/order/get/{id}") 
  8. public CommonResult getStockDetails(@PathVariable Integer id) { 
  9.   StockModel stockModel = new StockModel(); 
  10.   stockModel.setCode("STOCK==>1000"); 
  11.   stockModel.setId(id); 
  12.   return CommonResult.success(stockModel); 

如果大家熟悉 Spring 相關(guān)的組件大家都可以想到,這里多半是通過Spring Aop. 的方式來攔截 getStockDetails 方法。我們先看看SentinelAutoConfiguration 配置文件,我們可以找到 SentinelResourceAspect Bean 的定義方法。

 
 
 
 
  1. @Bean 
  2. @ConditionalOnMissingBean 
  3. public SentinelResourceAspect sentinelResourceAspect() { 
  4.    return new SentinelResourceAspect(); 

讓后我們?cè)賮砜纯?SentinelResourceAspect 具體是怎么處理的,源碼如下:

 
 
 
 
  1. // 定義 Pointcut 
  2. @Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)") 
  3. public void sentinelResourceAnnotationPointcut() { 
  4. // Around 來對(duì)被標(biāo)記 @SentinelResource 注解的方法進(jìn)行處理 
  5. @Around("sentinelResourceAnnotationPointcut()") 
  6. public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable { 
  7.   Method originMethod = resolveMethod(pjp); 
  8.   // 獲取注解信息 
  9.   SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class); 
  10.   // 獲取資源名稱 
  11.   String resourceName = getResourceName(annotation.value(), originMethod); 
  12.   EntryType entryType = annotation.entryType(); 
  13.   int resourceType = annotation.resourceType(); 
  14.   Entry entry = null; 
  15.   try { 
  16.     // 執(zhí)行 entry 
  17.     entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs()); 
  18.     // 執(zhí)行業(yè)務(wù)方法 
  19.     Object result = pjp.proceed(); 
  20.     // 返回 
  21.     return result; 
  22.   } catch (BlockException ex) { 
  23.     // 處理 BlockException 
  24.     return handleBlockException(pjp, annotation, ex); 
  25.   } catch (Throwable ex) { 
  26.     Class[] exceptionsToIgnore = annotation.exceptionsToIgnore(); 
  27.     // The ignore list will be checked first. 
  28.     if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) { 
  29.       throw ex; 
  30.     } 
  31.     if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) { 
  32.       traceException(ex); 
  33.       // 處理降級(jí) 
  34.       return handleFallback(pjp, annotation, ex); 
  35.     } 
  36.     // No fallback function can handle the exception, so throw it out. 
  37.     throw ex; 
  38.   } 

我們總結(jié)一下, @SentinelResource 的執(zhí)行過程, 首先是通過 Aop 進(jìn)行攔截,然后通過 SphU.entry 執(zhí)行對(duì)應(yīng)的流控規(guī)則,最后調(diào)用業(yè)務(wù)方法。如果觸發(fā)流控規(guī)則首先處理流控異常 BlockException 然后在判斷是否有服務(wù)降級(jí)的處理,如果有就調(diào)用 fallback 方法。通過 handleBlockException 、handleFallback 進(jìn)行處理。

責(zé)任鏈模式處理流控

通過上面的梳理,我們知道對(duì)于流控的過程,核心處理方法就是 SphU.entry 。在這個(gè)方法中其實(shí)主要就是初始化流控 Solt 和執(zhí)行 Solt. 在這個(gè)過程中會(huì)對(duì):簇點(diǎn)定義、流量控制、熔斷降級(jí)、系統(tǒng)白名單等頁(yè)面功能進(jìn)行處理。

1. 初始化責(zé)任鏈

下面是初始化 Solt 的核心代碼在 SphU.entryWithPriority

 
 
 
 
  1. // 刪減部分代碼 
  2. private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args) 
  3.   throws BlockException { 
  4.   // 初始化責(zé)任鏈 
  5.   ProcessorSlot chain = lookProcessChain(resourceWrapper); 
  6.   Entry e = new CtEntry(resourceWrapper, chain, context); 
  7.   try { 
  8.     // 執(zhí)行 entry 
  9.     chain.entry(context, resourceWrapper, null, count, prioritized, args); 
  10.   } catch (BlockException e1) { 
  11.     e.exit(count, args); 
  12.     // 異常拋出,讓 SentinelResourceAspect.invokeResourceWithSentinel 統(tǒng)一處理 
  13.     throw e1; 
  14.   } catch (Throwable e1) { 
  15.     // This should not happen, unless there are errors existing in Sentinel internal. 
  16.     RecordLog.info("Sentinel unexpected exception", e1); 
  17.   } 
  18.   return e; 
  19. 通過 lookProcessChain 方法我逐步的查找,我們可以看到最終的責(zé)任鏈初始化類,默認(rèn)是 DefaultSlotChainBuilder

     
     
     
     
    1. public class DefaultSlotChainBuilder implements SlotChainBuilder { 
    2.     @Override 
    3.     public ProcessorSlotChain build() { 
    4.         ProcessorSlotChain chain = new DefaultProcessorSlotChain(); 
    5.         // Note: the instances of ProcessorSlot should be different, since they are not stateless. 
    6.         // 通過 SPI 去加載所有的  ProcessorSlot 實(shí)現(xiàn),通過 Order 排序 
    7.         List sortedSlotList = SpiLoader.loadPrototypeInstanceListSorted(ProcessorSlot.class); 
    8.         for (ProcessorSlot slot : sortedSlotList) { 
    9.             if (!(slot instanceof AbstractLinkedProcessorSlot)) { 
    10.                 RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain"); 
    11.                 continue; 
    12.             } 
    13.                       // 添加到 chain 尾部 
    14.             chain.addLast((AbstractLinkedProcessorSlot) slot); 
    15.         } 
    16.         return chain; 
    17.     } 

    2. 責(zé)任鏈的處理過程

    我們可以通過斷點(diǎn)的方式來查看在 sortedSlotList 集合中所有的 solt 順序如下圖所示:

    我們可以通過如下的順序進(jìn)行逐個(gè)的簡(jiǎn)單的分析一下

    • NodeSelectorSolt
    • CusterBuilderSolt
    • LogSlot
    • StatisicSlot
    • AuthoritySolt
    • SystemSolts
    • ParamFlowSolt
    • FlowSolt
    • DegradeSlot

    對(duì)于 Sentinel 的 Slot 流控協(xié)作流程可以參考官方給出的文檔, 如下圖所示:

    FlowSolt 流控

    通過 NodeSelectorSolt、CusterBuilderSolt、StatisicSlot 等一系列的請(qǐng)求數(shù)據(jù)處理,在 FlowSolt會(huì)進(jìn)入流控規(guī)則,所有的 Solt 都會(huì)執(zhí)行 entry 方法, 如下所示

     
     
     
     
    1. // FlowSolt 的 entry 方法 
    2. @Override 
    3. public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, 
    4.                   boolean prioritized, Object... args) throws Throwable { 
    5.   // 檢查流量 
    6.   checkFlow(resourceWrapper, context, node, count, prioritized); 
    7.   fireEntry(context, resourceWrapper, node, count, prioritized, args); 

    在后續(xù)的流程中,會(huì)執(zhí)進(jìn)行判斷具體的流控策略,默認(rèn)是快速失敗,會(huì)執(zhí)行 DefaultController 方法。

     
     
     
     
    1. // DefaultController 
    2. @Override 
    3. public boolean canPass(Node node, int acquireCount, boolean prioritized) { 
    4.   // 當(dāng)前資源的調(diào)用次數(shù) 
    5.   int curCount = avgUsedTokens(node); 
    6.   // 當(dāng)前資源的調(diào)用次數(shù) + 1 > 當(dāng)前閾值 
    7.   if (curCount + acquireCount > count) { 
    8.     // 刪減比分代碼 
    9.     // 不通過 
    10.     return false; 
    11.   } 
    12.   // 通過 
    13.   return true; 
    14. private int avgUsedTokens(Node node) { 
    15.   if (node == null) { 
    16.     return DEFAULT_AVG_USED_TOKENS; 
    17.   } 
    18.   return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int)(node.passQps()); 

    如果上面返回不通過會(huì)回到,那么會(huì)拋出 FlowException

     
     
     
     
    1. public void checkFlow(Function> ruleProvider, ResourceWrapper resource, 
    2.                       Context context, DefaultNode node, int count, boolean prioritized) throws BlockException { 
    3.   if (ruleProvider == null || resource == null) { 
    4.     return; 
    5.   } 
    6.   Collection rules = ruleProvider.apply(resource.getName()); 
    7.   if (rules != null) { 
    8.     for (FlowRule rule : rules) { 
    9.       if (!canPassCheck(rule, context, node, count, prioritized)) { 
    10.         // 流控規(guī)則不通過,會(huì)拋出 FlowException 
    11.         throw new FlowException(rule.getLimitApp(), rule); 
    12.       } 
    13.     } 
    14.   } 

    然后會(huì)在 StatisticSlot 中增加統(tǒng)計(jì)信息, 最后會(huì)拋出給 SentinelResourceAspect 進(jìn)行處理,完成流控功能。我們?cè)賮砜纯催@個(gè)異常信息,如果是BlockException 異常,會(huì)進(jìn)入 handleBlockException 方法處理, 如果是其他的業(yè)務(wù)異常首先會(huì)判斷是否有配置 fallback 處理如果有,就調(diào)用 handleFallback 沒有就繼續(xù)往外拋,至此完成流控功能

     
     
     
     
    1. try { 
    2.   entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs()); 
    3.   Object result = pjp.proceed(); 
    4.   return result; 
    5. } catch (BlockException ex) { 
    6.   return handleBlockException(pjp, annotation, ex); 
    7. } catch (Throwable ex) { 
    8.   Class[] exceptionsToIgnore = annotation.exceptionsToIgnore(); 
    9.   // The ignore list will be checked first. 
    10.   if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) { 
    11.     throw ex; 
    12.   } 
    13.   if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) { 
    14.     traceException(ex); 
    15.     return handleFallback(pjp, annotation, ex); 
    16.   } 
    17.   // No fallback function can handle the exception, so throw it out. 
    18.   throw ex; 

    DegradeSlot 降級(jí)

    斷路器的作用是當(dāng)某些資源一直出現(xiàn)故障時(shí),將觸發(fā)斷路器。斷路器不會(huì)繼續(xù)訪問已經(jīng)發(fā)生故障的資源,而是攔截請(qǐng)求并返回故障信號(hào)。

    Sentinel 在 DegradeSlot 這個(gè) Slot 中實(shí)現(xiàn)了熔斷降級(jí)的功能,它有三個(gè)狀態(tài) OPEN 、HALF_OPEN、CLOSED 以ResponseTimeCircuitBreaker RT 響應(yīng)時(shí)間維度來分析, 斷路器工作的過程。下面是一個(gè)標(biāo)準(zhǔn)斷路器的工作流程:

    在 Sentinel 實(shí)現(xiàn)的源碼過程如下圖所示:

    Sentinel 通過 Web 攔截器

    Sentinel 在默認(rèn)情況下, 不使用 @ResourceSentinel 注解實(shí)現(xiàn)流控的時(shí)候, Sentinel 通過攔截器進(jìn)行流控實(shí)現(xiàn)的。初始化類在 SentinelWebAutoConfiguration 它實(shí)現(xiàn)了 WebMvcConfigurer 接口,在 sentinelWebInterceptor 方法初始化 SentinelWebInterceptor 等 Bean。

     
     
     
     
    1. @Bean 
    2. @ConditionalOnProperty(name = "spring.cloud.sentinel.filter.enabled", 
    3.                        matchIfMissing = true) 
    4. public SentinelWebInterceptor sentinelWebInterceptor( 
    5.   SentinelWebMvcConfig sentinelWebMvcConfig) { 
    6.   return new SentinelWebInterceptor(sentinelWebMvcConfig); 

    我們?cè)?SentinelWebInterceptor 的核心方法 preHandle 中處理,這里面我們又可以看到 SphU.entry 熟悉的過程調(diào)用流控的責(zé)任鏈。由于邏輯都類似,此處不再多說。代碼如下:

     
     
     
     
    1. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 
    2.   throws Exception { 
    3.   try { 
    4.     String resourceName = getResourceName(request); 
    5.     if (StringUtil.isEmpty(resourceName)) { 
    6.       return true; 
    7.     } 
    8.     if (increaseReferece(request, this.baseWebMvcConfig.getRequestRefName(), 1) != 1) { 
    9.       return true; 
    10.     } 
    11.     // Parse the request origin using registered origin parser. 
    12.     String origin = parseOrigin(request); 
    13.     String contextName = getContextName(request); 
    14.     ContextUtil.enter(contextName, origin); 
    15.     Entry entry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_WEB, EntryType.IN); 
    16.     request.setAttribute(baseWebMvcConfig.getRequestAttributeName(), entry); 
    17.     return true; 
    18.   } catch (BlockException e) { 
    19.     try { 
    20.       handleBlockException(request, response, e); 
    21.     } finally { 
    22.       ContextUtil.exit(); 
    23.     } 
    24.     return false; 
    25.   } 

    參考文檔

    https://github.com/alibaba/Sentinel/wiki

    https://martinfowler.com/bliki/CircuitBreaker.html


    網(wǎng)站欄目:一篇帶給你Sentinel流控原理
    文章路徑:http://m.5511xx.com/article/coipsss.html