范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

sentinel流控效果是如何生效的

  一、sentinel简介
  Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量控制、流量路由、熔断降级、系统自适应保护等多个维度来帮助用户保障微服务的稳定性
  随着微服务的流行,服务和服务之间的稳定性变得越来越重要。伴随着公司用户量和流量的日益增加,对于数据库的压力是越来越大,Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护
  二、源码入口
  使用sentinel有两种方式,一种方式是对需要限流和降级的接口资源方法上面加入@SentinelResource注解,还有一种就是通过拦截器的方式进行资源保护限流和降级,其实两种方法执行的关键方法都是同一段代码,今天我们暂时通过注解这种aop切面的形式,sentinel源码是如何实现限流和降级的呢
  底层源码的工作还是基于SpringBoot的自动装配原理,在spring-cloud-starter-alibaba-sentinel.jar,下面的spring.fatories里面的SentinelAutoConfiguration类,我们来看看这个类的源码
  在Spring容器启动的时候实例化了一个SentinelResourceAspect类,看这个类的命名就应该可以大概猜到这个类就是一个切面类,我们点击进入@Aspect public class SentinelResourceAspect extends AbstractSentinelAspectSupport {      @Pointcut("@annotation(com.alibaba.csp.sentinel.annotation.SentinelResource)")     public void sentinelResourceAnnotationPointcut() {     }      @Around("sentinelResourceAnnotationPointcut()")     public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {         Method originMethod = resolveMethod(pjp);          SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);         if (annotation == null) {             // Should not go through here.             throw new IllegalStateException("Wrong state for SentinelResource annotation");         }         String resourceName = getResourceName(annotation.value(), originMethod);         EntryType entryType = annotation.entryType();         int resourceType = annotation.resourceType();         Entry entry = null;         try {             entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());             return pjp.proceed();         } catch (BlockException ex) {             return handleBlockException(pjp, annotation, ex);         } catch (Throwable ex) {             Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();             // The ignore list will be checked first.             if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {                 throw ex;             }             if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {                 traceException(ex);                 return handleFallback(pjp, annotation, ex);             }              // No fallback function can handle the exception, so throw it out.             throw ex;         } finally {             if (entry != null) {                 entry.exit(1, pjp.getArgs());             }         }     } }
  一看就知道这个切面类,是利用的aop的技术,切点就是我们上面说的@SentinelResource注解,所以当我们的接口方法上面加了@SentinelResource注解,执行这个接口里面的业务逻辑前会先进入这个切面类的invokeResourceWithSentinel方法,我们看看这个切面是如何执行的,我们重点看SphU.entry(resourceName, resourceType, entryType, pjp.getArgs())这个方法 public static Entry entry(String name, int resourceType, EntryType trafficType, Object[] args)     throws BlockException {     return Env.sph.entryWithType(name, resourceType, trafficType, 1, args); }
  在这个Env的类的实例化的时候会执行static代码块的逻辑public class Env {      public static final Sph sph = new CtSph();      static {         // If init fails, the process will exit.         InitExecutor.doInit();     }  }
  我们继续看看这个InitExecutor.doInit()方法里面到底做了什么动作public static void doInit() {     if (!initialized.compareAndSet(false, true)) {         return;     }     try {         List initFuncs = SpiLoader.of(InitFunc.class).loadInstanceListSorted();         List initList = new ArrayList();         for (InitFunc initFunc : initFuncs) {             RecordLog.info("[InitExecutor] Found init func: {}", initFunc.getClass().getCanonicalName());             insertSorted(initList, initFunc);         }         for (OrderWrapper w : initList) {             w.func.init();             RecordLog.info("[InitExecutor] Executing {} with order {}",                 w.func.getClass().getCanonicalName(), w.order);         }     } catch (Exception ex) {         RecordLog.warn("[InitExecutor] WARN: Initialization failed", ex);         ex.printStackTrace();     } catch (Error error) {         RecordLog.warn("[InitExecutor] ERROR: Initialization failed with fatal error", error);         error.printStackTrace();     } }
  首先用cas做了并发安全处理,保证多线程情况下面只有一个线程执行成功,然后这里主要的逻辑是 SpiLoader.of(InitFunc.class).loadInstanceListSorted(),然后会调用到里面的load方法/**  * Load all Provider instances of the specified Service, sorted by order value in class"s {@link Spi} annotation  *  * @return Sorted Provider instances list  */ public List loadInstanceListSorted() {     load();      return createInstanceList(sortedClassList); }      /**      * Load the Provider class from Provider configuration file      */     public void load() {         if (!loaded.compareAndSet(false, true)) {             return;         }          String fullFileName = SPI_FILE_PREFIX + service.getName();         ClassLoader classLoader;         if (SentinelConfig.shouldUseContextClassloader()) {             classLoader = Thread.currentThread().getContextClassLoader();         } else {             classLoader = service.getClassLoader();         }         if (classLoader == null) {             classLoader = ClassLoader.getSystemClassLoader();         }         Enumeration urls = null;         try {             urls = classLoader.getResources(fullFileName);         } catch (IOException e) {             fail("Error locating SPI configuration file, filename=" + fullFileName + ", classloader=" + classLoader, e);         }          if (urls == null || !urls.hasMoreElements()) {             RecordLog.warn("No SPI configuration file, filename=" + fullFileName + ", classloader=" + classLoader);             return;         }          while (urls.hasMoreElements()) {             URL url = urls.nextElement();              InputStream in = null;             BufferedReader br = null;             try {                 in = url.openStream();                 br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));                 String line;                 while ((line = br.readLine()) != null) {                     if (StringUtil.isBlank(line)) {                         // Skip blank line                         continue;                     }                      line = line.trim();                     int commentIndex = line.indexOf("#");                     if (commentIndex == 0) {                         // Skip comment line                         continue;                     }                      if (commentIndex > 0) {                         line = line.substring(0, commentIndex);                     }                     line = line.trim();                      Class clazz = null;                     try {                         clazz = (Class) Class.forName(line, false, classLoader);                     } catch (ClassNotFoundException e) {                         fail("class " + line + " not found", e);                     }                      if (!service.isAssignableFrom(clazz)) {                         fail("class " + clazz.getName() + "is not subtype of " + service.getName() + ",SPI configuration file=" + fullFileName);                     }                      classList.add(clazz);                     Spi spi = clazz.getAnnotation(Spi.class);                     String aliasName = spi == null || "".equals(spi.value()) ? clazz.getName() : spi.value();                     if (classMap.containsKey(aliasName)) {                         Class<? extends S> existClass = classMap.get(aliasName);                         fail("Found repeat alias name for " + clazz.getName() + " and "                                 + existClass.getName() + ",SPI configuration file=" + fullFileName);                     }                     classMap.put(aliasName, clazz);                      if (spi != null && spi.isDefault()) {                         if (defaultClass != null) {                             fail("Found more than one default Provider, SPI configuration file=" + fullFileName);                         }                         defaultClass = clazz;                     }                      RecordLog.info("[SpiLoader] Found SPI implementation for SPI {}, provider={}, aliasName={}"                             + ", isSingleton={}, isDefault={}, order={}",                         service.getName(), line, aliasName                             , spi == null ? true : spi.isSingleton()                             , spi == null ? false : spi.isDefault()                             , spi == null ? 0 : spi.order());                 }             } catch (IOException e) {                 fail("error reading SPI configuration file", e);             } finally {                 closeResources(in, br);             }         }          sortedClassList.addAll(classList);         Collections.sort(sortedClassList, new Comparator>() {             @Override             public int compare(Class<? extends S> o1, Class<? extends S> o2) {                 Spi spi1 = o1.getAnnotation(Spi.class);                 int order1 = spi1 == null ? 0 : spi1.order();                  Spi spi2 = o2.getAnnotation(Spi.class);                 int order2 = spi2 == null ? 0 : spi2.order();                  return Integer.compare(order1, order2);             }         });     }
  在这个load方法里面这里会加载到sentinel-core.jar下面META-INF目录下面的services里面的所有的实现类,通过反射实例化
  这里我们先返回进入到这个entryWithType方法里面去@Override public Entry entryWithType(String name, int resourceType, EntryType entryType, int count, Object[] args)     throws BlockException {     return entryWithType(name, resourceType, entryType, count, false, args); }    @Override     public Entry entryWithType(String name, int resourceType, EntryType entryType, int count, boolean prioritized,                                Object[] args) throws BlockException {         StringResourceWrapper resource = new StringResourceWrapper(name, entryType, resourceType);         return entryWithPriority(resource, count, prioritized, args);     }    private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)         throws BlockException {         Context context = ContextUtil.getContext();         if (context instanceof NullContext) {             // The {@link NullContext} indicates that the amount of context has exceeded the threshold,             // so here init the entry only. No rule checking will be done.             return new CtEntry(resourceWrapper, null, context);         }          if (context == null) {             // Using default context.             context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);         }          // Global switch is close, no rule checking will do.         if (!Constants.ON) {             return new CtEntry(resourceWrapper, null, context);         }          ProcessorSlot chain = lookProcessChain(resourceWrapper);          /*          * Means amount of resources (slot chain) exceeds {@link Constants.MAX_SLOT_CHAIN_SIZE},          * so no rule checking will be done.          */         if (chain == null) {             return new CtEntry(resourceWrapper, null, context);         }          Entry e = new CtEntry(resourceWrapper, chain, context);         try {             chain.entry(context, resourceWrapper, null, count, prioritized, args);         } catch (BlockException e1) {             e.exit(count, args);             throw e1;         } catch (Throwable e1) {             // This should not happen, unless there are errors existing in Sentinel internal.             RecordLog.info("Sentinel unexpected exception", e1);         }         return e;     }
  这里最后会调用到entryWithPriority这个方法里面,这里面用到了责任链的设计模式,首先会构造一个处理的链路,我们关注一下这个lookProcessChain(resourceWrapper)方法 ProcessorSlot 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 newMap = new HashMap(                     chainMap.size() + 1);                 newMap.putAll(chainMap);                 newMap.put(resourceWrapper, chain);                 chainMap = newMap;             }         }     }     return chain; }
  首先会从一个缓存map里面去取,如果为空的话, 调用SlotChainProvider.newSlotChain()构造一个链路,并且放到map里面缓存public static ProcessorSlotChain newSlotChain() {     if (slotChainBuilder != null) {         return slotChainBuilder.build();     }      // Resolve the slot chain builder SPI.     slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault();      if (slotChainBuilder == null) {         // Should not go through here.         RecordLog.warn("[SlotChainProvider] Wrong state when resolving slot chain builder, using default");         slotChainBuilder = new DefaultSlotChainBuilder();     } else {         RecordLog.info("[SlotChainProvider] Global slot chain builder resolved: {}",             slotChainBuilder.getClass().getCanonicalName());     }     return slotChainBuilder.build(); }
  这里也是用到jdk的spi机制,会加载sentinel-core.jar里面META-INF下面的services下面配置的ProcessSlot的实现类# Sentinel default ProcessorSlots 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
  最终通过DefaultSlotChainBuilder的build方法构造了一个处理器责任链,如下图
  然后依次调用 chain.entry(context, resourceWrapper, null, count, prioritized, args)方法,依次调用图中的各个slot类,这里我们重点看看FlowSlot类和DegradeSlot这两个类的entry方法@Override public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,                   boolean prioritized, Object... args) throws Throwable {     checkFlow(resourceWrapper, context, node, count, prioritized);      fireEntry(context, resourceWrapper, node, count, prioritized, args); }
  在这个FlowSlot的entry方法,首先会校验我们控制台配置的限流流控规则,最终会调用到checkFlow方法void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized)     throws BlockException {     checker.checkFlow(ruleProvider, resource, context, node, count, prioritized); }  public void checkFlow(Function> ruleProvider, ResourceWrapper resource,                           Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {         if (ruleProvider == null || resource == null) {             return;         }         Collection rules = ruleProvider.apply(resource.getName());         if (rules != null) {             for (FlowRule rule : rules) {                 if (!canPassCheck(rule, context, node, count, prioritized)) {                     throw new FlowException(rule.getLimitApp(), rule);                 }             }         }     }
  通过Collection rules = ruleProvider.apply(resource.getName())拿到我们在sentinel控制台配置的各种限流规则,然后遍历进行校验,不通过就抛出FlowException异常,这个时候整个这个责任链的执行流程就结束了,但是在之前执行的StatisticSlot的entry方法里面用try catch包裹住了,在这个SentinelResource Aspect里面会捕获BlocakExption异常,进行处理@Around("sentinelResourceAnnotationPointcut()") public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwable {     Method originMethod = resolveMethod(pjp);      SentinelResource annotation = originMethod.getAnnotation(SentinelResource.class);     if (annotation == null) {         // Should not go through here.         throw new IllegalStateException("Wrong state for SentinelResource annotation");     }     String resourceName = getResourceName(annotation.value(), originMethod);     EntryType entryType = annotation.entryType();     int resourceType = annotation.resourceType();     Entry entry = null;     try {         entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());         return pjp.proceed();     } catch (BlockException ex) {         return handleBlockException(pjp, annotation, ex);     } catch (Throwable ex) {         Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();         // The ignore list will be checked first.         if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {             throw ex;         }         if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {             traceException(ex);             return handleFallback(pjp, annotation, ex);         }          // No fallback function can handle the exception, so throw it out.         throw ex;     } finally {         if (entry != null) {             entry.exit(1, pjp.getArgs());         }     } }protected Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex)     throws Throwable {      // Execute block handler if configured.     Method blockHandlerMethod = extractBlockHandlerMethod(pjp, annotation.blockHandler(),         annotation.blockHandlerClass());     if (blockHandlerMethod != null) {         Object[] originArgs = pjp.getArgs();         // Construct args.         Object[] args = Arrays.copyOf(originArgs, originArgs.length + 1);         args[args.length - 1] = ex;         return invoke(pjp, blockHandlerMethod, args);     }      // If no block handler is present, then go to fallback.     return handleFallback(pjp, annotation, ex); }
  最终会拿到这个SentinelResource注解里面配置的异常处理方法完成调用,至此整个sentinel流控保护流程就结束了,至于在这个责任链里面是如何统计各项指标,如何判断qps是否超过配置的最大值,以及sentinel里面的熔断,断路器何时进行打开,何时进行半开,何时关闭,里面的算法很复杂,以后专门写一篇文章来分析
狄龙首次回应伤人,不爽科尔打破准则言论?名记还得向他道歉灰熊摇摆人狄龙布鲁克斯在接受采访时首次回应他对勇士后卫加里佩顿二世的那次恶意犯规,并且指出他并不是有意伤害加里佩顿二世我不是故意伤害某人,而且我非常希望能在那一刻收回那个动作布鲁克湖人不满沃格尔用兵请菲尔杰克逊找会用威少的新帅禅师菲尔杰克逊曾带领芝加哥公牛洛杉矶湖人队摘下11次总冠军,他的发言极具份量历经失败赛季,拥有詹姆斯戴维斯和威斯布鲁克的洛杉矶湖人队没太多输球借口,必须针对阵容重整,包括开除2011988年囚歌爆火黑幕,迟志强又如何欺世盗名?囚歌往事文刘浪在中国流行音乐史,囚歌是一段顽固存在。顽固到,似乎毫无征兆,就蹿于那个年代的音乐之巅。那是1988年初,不经意间,街头巷尾充斥愁啊愁十不该等以迟志强演唱名义流行的歌。38岁眉姐姐毁容式近况曝光甄嬛传11年后,她判若两人救,我真的会被一些明星下凡笑死。隔离前的彭于晏,肌肉型男,帅气逼人,谁见了都得化身大sai迷。隔离后的彭于晏,头发凌乱,面露沧桑,像是耕了几亩地刚回来的阿牛哥。还有点像邓超。传下去5部贼刺激的冒险片!吓得我心提到嗓子眼今天,给大家推荐点什么呢?正好看到一段惊险刺激的视频,推荐5部超级牛逼的冒险片,给大家的生活,来点刺激!01加勒比海盗导演戈尔维宾斯基豆瓣8。8主演约翰尼德普杰弗里拉什奥兰多布鲁姆ADataCartographybasedMixUpforPretrainedLanguageModelsMixUpMixUp是一种数据增强策略,其中通过组合训练样本的随机对及其标签在训练期间生成额外的样本。但是,选择随机对可能不是最佳选择。在这项工作中,我们提出了TDMixUp,这是地球关于蓝色星球的事实了解地球是如何形成的,它是由什么构成的等等。地球是我们的家园,是宇宙中唯一一个我们可以确定生命存在的地方。地球形成于46亿年前,由一团旋转的气体和尘埃云形成,形成了我们整个太阳系,地球是怎么来的?地球怎么来的?46亿年前,太阳诞生,初期,引力将尘埃物质聚集,原始地球形成。45亿年前,地球被小行星追尾,蹦出了月球,迷妹天天围着地球转。39亿年前,无数含水的陨石带着地球炸了20辽篮富二代继续追梦!32岁放弃数十亿资产,如今或走向教练生涯CBA本赛季的比赛已经全部结束,接下来就是长达几个月的休赛期,最近总冠军辽宁男篮也是开始了调整,毕竟队内7位球员的合同到期,包括张镇麟和郭艾伦两条超级大鱼,郭艾伦有可能离开球队,张他斩获6枚金牌,在刘德华公司当员工,苏炳添称他为苏神苏炳添在东京奥运会上,以9。83秒的优异成绩,刷新了亚洲纪录。如此优异的成绩,让无数网友都为之沸腾。他被外界称为苏神。面对这个称号,苏炳添表示,在自己的心中,还有另一位苏神。那就是阿联酋航空正式推出高端经济舱全套服务中国航空新闻网讯随着阿联酋航空正式推出从地面到空中的全套高端经济舱服务,乘客将有机会享受到一项别具特色的阿联酋航空旅行体验。阿联酋航空高端经济舱服务将自2022年6月1日正式起售。
为什么医生不建议多吃肉呢?提醒这4种肉,能不吃尽量不要吃健康迎新春随着生活水平的不断上升,人们想吃什么就能吃到什么,逐渐也离不开肉类食物了,但医生却不建议大家大量摄入肉食,一定要控制好肉类的摄入量,否则容易让脂肪在体内大量堆积,加大患慢50岁以后,烟瘾酒瘾色瘾,哪个最伤身体?进来看看有益健康50岁以后,烟瘾,酒瘾,色瘾,这三样哪个对身体伤害最大?想长寿,应该戒掉哪一个?有读者提出了一个问题,真的挺有意思的。50岁以后,人生开始进入后半程,身体的健康,是保持高质量晚年生2022年夜饭菜单新鲜出炉,有荤有素,色香味俱全,记得收藏再过四天,就是大年三十了,在这个家家户户的团圆时刻,丰盛的年夜饭不仅美味肯定也要有很好的寓意,这样看着也更吉利。废话不多说,接下来给大家分享荤素搭寓意好的年夜饭的做法。包含凉菜热菜舒适服帖的瑜伽裤穿搭,简约不失潮流感,衬托修长腿型,俏皮减龄舒适服帖的瑜伽裤穿搭简约不失潮流感,衬托修长腿型,俏皮减龄。瑜伽裤的搭配,目前仍是我们保留并希望永远留存的穿搭,不仅看似简单的单品,却很容易玩出各种时尚感。随性酷帅且非常有范儿!气护肤产品照片轻奢高级感,真珠美学带来奢华护肤体验肌肤经历了四季的冷暖变换和外界环境的侵袭,往往干燥无光疲态明显,岁末时分,是时候褒奖一下努力的自己和辛苦的肌肤了,真珠美学发酵精华金箔面膜献给心爱的自己,每天敷贴10分钟,肌肤重获摆脱痘痘烦恼,有两个因素你必须要重视痘痘虽然很容易的就出现在我们的脸上,但是想要让它从脸上彻底消失可不是那么简单的事情了。很多朋友在这个时候都会变得不理智,听别人说这个方法对祛痘有效果就上脸试试。也不管自己的皮肤是否新年新气象,图个好彩头,35岁适合佩戴什么样的手表?每当过年的时候,人们都喜欢用红色的东西装点屋子院子,乃至厂子和办公室,寓意红红火火,包括我们自己,也喜欢用红色的东西,穿红色的衣服等。手表也一样,玫瑰金色的手表也给人以充满喜气的感如何从旗袍看中国礼仪?旗袍作为得体的中国美服,端庄优雅,它不仅见证了历史的进程,还承载艺术与美感的文明。女子穿在身上,一种高贵的气质就会流露出来,在中国的传统文化中,女子讲究含蓄内敛温婉秀丽。而带着古典CBA三消息广东硬汉腰伤严重,同曦已提前出局,吉林仅六人轮换大家好呀,我是北柠,各位小伙伴们要养成先赞后看的习惯哦!球迷们最近应该都发现了广东队的球员轮换阵容出现了一定的问题,特别是在本土内线球员的轮换上,广东队能用的人实在是太少了,杜锋指热火队提出用邓罗交换戈登被火箭队拒绝,后者希望搭上伍德换希罗据美国方面消息透露,迈阿密热火队日前提出用邓罗交换戈登被火箭队拒绝,后者希望搭上伍德换回希罗。热火队本赛季后来者居上,目前战绩超越了篮网队排在东部榜首,有望冲击总冠军,帕克莱利想趁戴维斯生涯十大比赛西决三分绝杀掘金12012年10月31日,在自己的第一场NBA比赛中,状元秀安东尼戴维斯遭遇了传奇大前锋蒂姆邓肯率领的马刺,虽然最终败北,但是小浓眉全场21分7篮板的表现还是得到很多人的认可。22