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

sentinel基于滑动窗口实现实时指标统计原理

  sentinel通过责任链模式,让每个slot来实现一种功能来实现流量控制、熔断降级等功能。其中,最重要的一个Slot非StatisticSlot莫属,它通过统计单位时间的调用数、成功数、失败数等,为流量控制、熔断降级等提供数据支撑,而StatisticSlot的底层是基于滑动窗口实现实时指标统计的,下面介绍一下StatisticSlot的工作过程一、StatisticSlot的入口
  sentinel将多个slot串联起来,每个slot在处理完成后,将数据传递给下一个slot,这些slot都是通过函数entry作为处理数据的入口,StatisticSlot的入口如下: @Override public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args) throws Throwable {     try {         // 触发下一个Slot的entry方法         fireEntry(context, resourceWrapper, node, count, args);         // 如果能通过SlotChain中后面的Slot的entry方法,说明没有被限流或降级         // 统计信息         node.increaseThreadNum();         node.addPassRequest();         // 省略部分代码     } catch (BlockException e) {         context.getCurEntry().setError(e);         // Add block count.         node.increaseBlockedQps();         // 省略部分代码         throw e;     } catch (Throwable e) {         context.getCurEntry().setError(e);         // Should not happen         node.increaseExceptionQps();         // 省略部分代码         throw e;     } }
  StatisticSlot的功能主要有:
  1、通过现有的统计数据进行规则校验,如果校验通过则可以对监控的接口进行访问。
  2、校验通过则进行实时统计数据的更新。
  3、如果被block或出现了异常了,则重新更新node中block的指标或异常指标。
  通过代码可以发现,StatisticSlot主要是通过DefaultNode node来进行实时统计数据的更新,
  下面来看一下DefaultNode的代码:public class DefaultNode extends StatisticNode {         private ResourceWrapper id;       private ClusterNode clusterNode;        // 省略部分代码       //每通过一次,增加一次passRequest     public void addPassRequest(int count) {         super.addPassRequest(count);         this.clusterNode.addPassRequest(count);     }     // 省略部分代码        }
  通过代码可以看出,DefaultNode主要通过继承StatisticNode来实现统计功能。除此之外,DefaultNode还有一个成员变量ClusterNode,ClusterNode主要是记录所有的context中实时指标的总和。DefaultNode与ClusterNode的关系如下:
  DefaultNode:保存着某个resource在某个context中的实时指标,每个DefaultNode都指向一个ClusterNode
  ClusterNode:保存着某个resource在所有的context中实时指标的总和,同样的resource会共享同一个ClusterNode,不管他在哪个context中二、StatisticNode与ArrayMetric
  从上面的分析中,我们知道实时指标的统计主要在StatisticNode中实现。下面介绍一下StatisticNodeprivate transient Metric rollingCounterInSecond = new ArrayMetric(1000 / SampleCountProperty.sampleCount, IntervalProperty.INTERVAL);   private transient Metric rollingCounterInMinute = new ArrayMetric(1000, 2 * 60);   @Override public void addPassRequest() {     rollingCounterInSecond.addPass();     rollingCounterInMinute.addPass(); }
  两个变量rollingCounterInSecond和rollingCounterInMinute,分别统计一秒钟的实时指标和一分钟的实时指标,他们对应的类是ArrayMetric。 public class ArrayMetric implements Metric {       private final LeapArray data;       public ArrayMetric(int sampleCount, int intervalInMs) {         this.data = new OccupiableBucketLeapArray(sampleCount, intervalInMs);     }       public ArrayMetric(int sampleCount, int intervalInMs, boolean enableOccupy) {         if (enableOccupy) {             this.data = new OccupiableBucketLeapArray(sampleCount, intervalInMs);         } else {             this.data = new BucketLeapArray(sampleCount, intervalInMs);         }     }     @Override     public void addSuccess(int count) {         WindowWrap wrap = data.currentWindow();         wrap.value().addSuccess(count);     } } }
  在ArrayMetric中,定义了类似于数组的变量data,它里面的每一个元素类似于一个桶,每个桶对应一个时间段,存放这个时间段的统计数据。以BucketLeapArray 为例 public class BucketLeapArray extends LeapArray {       public BucketLeapArray(int sampleCount, int intervalInMs) {         super(sampleCount, intervalInMs);     } }
  BucketLeapArray 主要继承LeapArray,LeapArray的代码如下: public abstract class LeapArray {      //单个窗口的长度,即每个窗口统计的时间段的大小    protected int windowLengthInMs;     //窗口的个数,即array的大小     protected int sampleCount;    //整个数组统计的时间段的大小,intervalInMs=windowLengthInMs*sampleCount     protected int intervalInMs;       //一个数组,每个元素用来记录对应时间的数据     protected final AtomicReferenceArray> array; }
  由此可见,数组array中的WindowWrap具有统计数据指标的能力。
  在ArrayMetric的函数addSuccess中,增加统计数据主要是通过MetricBucket中的addSuccess来实现,那我们就看一下MetricBucket。 public enum MetricEvent {         PASS,       BLOCK,     EXCEPTION,     SUCCESS,     RT,     OCCUPIED_PASS } public class MetricBucket {       private final LongAdder[] counters;       public void addSuccess(int n) {         add(MetricEvent.SUCCESS, n);     }          public void addPass(int n) {         add(MetricEvent.PASS, n);     }     public MetricBucket add(MetricEvent event, long n) {         counters[event.ordinal()].add(n);         return this;     } }
  在MetricBucket中,定义了一个数组counters,数组中的元素分别用来记录单位时间内的pass、block、exception、success、rt等。
  由此可见,StatisticNode主要是通过ArrayMetric来确定好滑动窗口的大小( windowLengthInMs)
  和个数(sampleCount),ArrayMetric中的数组data的size就是sampleCount,它的每一个元素就是MetricBucket ,即存放数据的桶。三、滑动窗口
  在使用滑动窗口统计数据时,需要知道当前数据应该落到哪个桶里面,下面介绍一下sentinel中滑动窗口的数据存放原理。先看一下滑动窗口数组的定义 public abstract class LeapArray {       // 时间窗口的长度     protected int windowLengthInMs;     // 采样窗口的个数     protected int sampleCount;     // 以毫秒为单位的时间间隔     protected int intervalInMs;       // 采样的时间窗口数组     protected AtomicReferenceArray> array;       /**      * LeapArray对象      * @param windowLength 时间窗口的长度,单位:毫秒      * @param intervalInSec 统计的间隔,单位:秒      */     public LeapArray(int sampleCount, int intervalInMs) {                 this.windowLengthInMs = intervalInMs / sampleCount;         this.intervalInMs = intervalInMs;         this.sampleCount = sampleCount;           this.array = new AtomicReferenceArray<>(sampleCount);     } }
  在LeapArray中,定义数组array,默认窗口大小为500ms,窗口个数为2(intervalInSec 默认为1000,sampleCount默认为2)
  现在继续回到 ArrayMetric.addPass() 方法中 @Override public void addPass() {     WindowWrap wrap = data.currentWindow();     wrap.value().addPass(); }
  主要是通过currentWindow()来获取当前窗口  public WindowWrap currentWindow() {         return currentWindow(TimeUtil.currentTimeMillis());  }   @Override public WindowWrap currentWindow(long time) {     // time每增加一个windowLength的长度,timeId就会增加1,时间窗口就会往前滑动一个     long timeId = time / windowLength;     // Calculate current index.     // idx被分成[0,arrayLength-1]中的某一个数,作为array数组中的索引     int idx = (int)(timeId % array.length());       // Cut the time to current window start.     long currentWindowStart = time - time % windowLength;       while (true) {         // 从采样数组中根据索引获取缓存的时间窗口         WindowWrap old = array.get(idx);         // array数组长度不宜过大,否则old很多情况下都命中不了,就会创建很多个WindowWrap对象         if (old == null) {             // 如果没有获取到,则创建一个新的             WindowWrap window = new WindowWrap(windowLength, currentWindowStart, new Window());             // 通过CAS将新窗口设置到数组中去             if (array.compareAndSet(idx, null, window)) {                 // 如果能设置成功,则将该窗口返回                 return window;             } else {                 // 否则当前线程让出时间片,等待                 Thread.yield();             }         // 如果当前窗口的开始时间与old的开始时间相等,则直接返回old窗口         } else if (currentWindowStart == old.windowStart()) {             return old;         // 如果当前时间窗口的开始时间已经超过了old窗口的开始时间,则放弃old窗口         // 并将time设置为新的时间窗口的开始时间,此时窗口向前滑动         } else if (currentWindowStart > old.windowStart()) {             if (addLock.tryLock()) {                 try {                     // if (old is deprecated) then [LOCK] resetTo currentTime.                     return resetWindowTo(old, currentWindowStart);                 } finally {                     addLock.unlock();                 }             } else {                 Thread.yield();             }         // 这个条件不可能存在         } else if (currentWindowStart < old.windowStart()) {             // Cannot go through here.             return new WindowWrap(windowLength, currentWindowStart, new Window());         }     } }
  这段代码的逻辑如下:
  1、获取当前时间,用当前时间对窗口大小windowLength求差,得到时间的索引timeId,再用timeId对数组的长度取模,得到数组的下标idx
  2、根据数组下标idx得到数组中的元素 WindowWrap old 。
  2.1、如果old为空,说明没有初始化,整个时候就需要创建一个新的窗口,再将窗口放入到数组中。
  2.2、如果old不为空,且当时时间段的开始时间和old的开始时间相同,则说明当前时间对应的窗口就是old,直接返回。
  2.3、如果当前时间段的开始时间大于old的开始时间,说明old是属于上一轮数组的时间窗口,则需要执行函数resetWindowTo(old, currentWindowStart)。函数resetWindowTo就是将old的时间设置为当前时间窗口的时间,并且清理old中之前统计的数据,即将old清空,时间重置为当前时间窗口的开始时间。
  这个滑动窗口的难点在于,时间timeId会不断增加,但是窗口数组data是固定大小(假设大小为2,数组下标为0、1)。所以刚开始时间窗口的索引idx=0,会落地array[0]中,然后时间增加,落到array[1]中,然后时间继续增加,idx=0,但是不能让这一次的数据落到上一次的时间窗口中,需要需要执行resetWindowTo,将上次统计到的数据情况。
  另外,sentinel会运行一个线程,定时将滑动窗口中统计的数据写入到本地文件中,所以不用担心执行resetWindowTo会丢失掉之前统计的数据。

货拉拉上线行程录音功能并试运行车载设备长沙坠车事件后,货拉拉向社会公布了一系列安全整改举措,公告发布14天后,货拉拉于3月11日宣布,跟车搬家订单的全程录音功能上线承载录像和信息采集功能的安心拉智能行驶记录仪,已开始在仁王2PC版焕新上阵华硕Z590吹雪主板申请出战由TeamNinja制作的硬核动作游戏仁王2,近期相继推出了PC版以及相应的更新。这款游戏延续了仁王系列的黑暗风格,并加入全新的战斗方式外形邪恶丑陋的各路妖怪以及由妖怪产生的常暗世11代酷睿好CP华硕Z590吹雪畅游AC英灵殿全球知名游戏商育碧于3月16日推出刺客信条英灵殿1。2。0更新,为游戏加入了奥斯塔拉节活动,带给玩家独特的活动和特殊奖励。同时本次更新加入了三个新技能以及新的相机模式更新幻化功能并小度新品发布会推多款泛智能终端,智能助手应用势能爆发3月18日,百度旗下人工智能品牌小度举行了唤醒春季新品发布会。会上,小度正式发布了全球首款主动纠正坐姿的平板电脑小度智能学习平板,以及小度WiFi6智能路由器等多款IoT设备,小度便携式定位终端使用GNSS天线一体化模块有哪些优势?SKYLAB能够实现定位的终端产品,产品PCB板上一定有支持位置输出的定位模块,可能是单独的GPS模块,北斗模块,也可能是GNSS天线一体化定位模块。像户外测量人员巡检人员以及执法人员佩戴的便全新旗舰地宝T9再颠覆体验,科沃斯如何在技术和研发上持续领先近日,科沃斯发布了2021年首款旗舰级扫地机器人新品,地宝DEEBOTT9。基于消费者日益多样化的地面清洁需求,科沃斯在技术研发与产品创新上也是不断加大力度,让此次发布的新品地宝D比亚迪汉DM正式开启保电功能OTA升级兼顾动力2月25日,我们从比亚迪官方获悉,即日起,比亚迪将向全国汉DM车主开始陆续推送保电功能OTA升级。此次升级服务面向汉DM全系车型且免费升级,升级方式有以下两种一是用户在车辆PAD屏双肾造型太明显BMWX1最新渲染图BMWX1是华晨宝马旗下定位于紧凑型SUV的一款车型,目前在汽车市场上进行售卖的为去年9月上市的2021款车型,北京经销商最高优惠6万左右。最近,网络上曝光了海外版新款BMWX1的李现同款银色尤物4。21首销!荣耀30系列颜值功能全面升级2020年,智能手机市场的竞争愈发激烈。智能手机想要在机海中脱颖而出,获得消费者的青睐,更强的产品力与更具竞争力的价格缺一不可。纵观茫茫机海一片,找出这样的机型简直像大海捞针,千篇拒绝公模机,魅族Note9这些细节你都知道吗?春节刚过,三月份的手机市场就变得热闹起来了,3月6日,魅族科技正式发布魅族Note9手机,售价1398元起,3月11日正式发售,如果你在3月25日前购买,那么还能享受魅族提供的24SKYLAB新推出低成本串口WiFi蓝牙组合模块LCS2028,国产方案为满足物联网智能家居智能楼宇等行业领域国产无线模块的需求,SKYLAB加快了国产化步伐,最新推出了一款基于国产方案的低成本串口WiFi蓝牙组合模块LCS2028。LCS2028高性
等等党又赢了,骁龙888IMX766,12256GB旗舰售价亲民OPPO子品牌有realme和一加两个品牌,realme主打极致质价比手机,追求品质和性价比相融合,而一加手机主打旗舰,没有中低端机,发布的每一款手机均是旗舰机,价格方面目前优势比华为的鸿蒙余承东公布最新数据!谷歌的鸿蒙或许要凉本以为垄断操作系统的老美,断粮华为之后会将其排在沙滩上,却不曾想华为还有自己的备胎,提前将自主研发的鸿蒙系统转正。虽然前期的表现受到了部分用户的质疑,但在鸿蒙系统及生态的不断优化之三星猎户座芯片vivoS15e发售,S12闻声价崩,网友必须疯抢5nm旗舰芯片70万跑分无压力vivoS15e新机的12GB256GB配置加上内存融合技术,5nm旗舰芯片70万跑分无压力vivoS15e新机让你随时回到游戏开黑状态。5nm旗舰芯荣耀Magic4pro和iPhone13pro比怎么选?价格不再是影响购买的因素老师说大家觉得今年的荣耀Magic4系列怎么样,别人不知道,但是小编觉得就是算是今年不可多得的整个系列三款机型都挺值得大家入手的机型!大家都知道每年各家的顶级旗舰重点区别就是某些侧支持OIS防抖!骁龙870神机RedmiK40S首降价1949元极致性价比5月2日消息,小米京东自营旗舰店显示,RedmiK40S开启秒杀,8GB128GB首次降至1949元,有亮黑银迹幽芒幻镜四种配色可供选择。这款旗舰是RedmiK40的小迭代款,和KvivoX80Pro全线开售,新一代性能强者登场作为全能旗舰手机的vivoX80Pro,性能是其跻身第一梯队实力的一面,而除了性能之外,该机的综合表现也十分出色。该款新机搭载了4nm旗舰芯片,不仅配备了天玑9000,还提供了全新河西走廊荒漠区土壤微生物地理分布格局及驱动机制研究取得进展近年来,研究表明,不同的微生物类群,特别是细菌和真菌,在全球范围内表现出不同的纬度纵向和海拔多样性分布格局,而关于荒漠生态系统的生物地理格局的研究有待提升。中国科学院西北生态环境资国际科技合作基地建设专项高精度单片三轴集成MEMS陀螺联合研发合作示范项目取得重大进展河北新闻网讯(河北日报记者王璐丹通讯员赵书海)日前,中国电科十三所承担的省科技厅国际科技合作基地建设专项高精度单片三轴集成MEMS陀螺联合研发合作示范项目顺利完成研发任务,研发的高新科技革命和产业革命改变全球制造业格局一第四次工业革命深入推进,5G可能成为先锋力量第四次工业革命推动科技创新将呈现多点突破交叉汇聚的的态势。国际金融危机后,主要经济体加大对新技术的战略性投入,一些关键新技术出现革命性联合体在单片机编程中的应用01联合体之前的文章枚举和结构体的结合文中提到,结构体就像是打包封装,把一些有共同特征的变量封装在内部。结构体是一种构造类型或复杂类型,它可以包含多个类型不同的成员。在C语言中,还小米11ultra这款手机怎么样?最近啊我看到很多人都在聊小米11ultra这款手机,那么这款手机究竟怎么样呢?首先是外观方面正面采用了一款三星的微曲屏,这款屏幕的屏幕素质还是挺不错的,两边在日常生活中误触的几率不