Hystrix核心篇-滑动窗口基础

2024-06-07 13:32

本文主要是介绍Hystrix核心篇-滑动窗口基础,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

RxJava可能有些小伙伴没有听过是什么东西,可能是因为大家平时在做业务需求的时候对异步编程了解得比较少,而RxJava就是这么一个响应式编程框架,RxJava在安卓上面用得非常多,做安卓的朋友肯定对它很熟悉。那我这里为什么要讲这个呢?因为spring cloud中服务治理框架Hystrix中大量用到了RxJava的响应式编程,为了便于理解,这里也简单给大家介绍一下。这里介绍的版本是RxJava 1.X版本的, 而在去年的早些时候,官方便宣布,将在一段时间后不再对 RxJava 1.x 进行维护,推出了RxJava2.X版本,既然有新的,为什么不介绍新的呢?因为目前最新的Hystrix版本1.5.12中使用的RxJava是1.2版本的,而2.X版本的api改动还是比较大的,所以为了大家能更加简单的理解Hystrix,所以这里是对1.X版本的介绍。

响应式编程是什么

响应式编程是一种基于异步数据流概念的编程模式,有点类似于JAVA里面的lambda表达式,相信大家都很熟悉lambda吧。数据流,stream,大家肯定不陌生,我们可以对stream有很多操作,filter、map、reduce 等常见操作。然后响应式中的核心就是响应二字,响应什么呢?响应的是事件,event 。 而流就是一个按照时间进行排序的事件序列。RxJava里面的事件是基于观察者模式,事件流将从上往下,从订阅源传递到观察者。

RxJava中重要概念

RxJava 有四个基本概念:Observable (可观察者,即被观察者)、 Observer (观察者)、 Subscriber (订阅,是Observer的抽象实现类,本质上使用是一样的)、事件。ObservableObserver 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 ObserverObservable 就像是一个生产者,在不断的生产消息,而SubscriberObserver 就像是一个消费者,在不断的消费消息

另外, RxJava 的事件回调方法还定义了两个特殊的事件,在Hystrix中用得也非常多:onCompleted()onError()

  • onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的 onNext() 发出时,需要触发 onCompleted() 方法作为标志。
  • onError(): 事件队列异常。在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出。

怎么做

说了这么多概念,估计大家都是一头雾水,我们直接来些实际的,加深大家的印象理解。用多的自然而然就会了,就懂了,这里说得可能不是最全的,但是说的都是Hystrix中用得很多的一些操作符,加深大家对Hystrix的理解,看源码就会容易一些。

例子

Observable<String> producer = Observable.create(new Observable.OnSubscribe<String>() {@Overridepublic void call(Subscriber<? super String> subscriber) {subscriber.onNext("apple");subscriber.onNext("orange");subscriber.onCompleted();}});
Subscriber<String> consumer = new Subscriber<String>() {@Overridepublic void onNext(String s) {LOG.info("我收到的水果有 = {}" , s);}@Overridepublic void onCompleted() {}@Overridepublic void onError(Throwable e) {}};
producer.subscribe(consumer);

先来一个简单的例子给大家直观的介绍下ObservableSubscriber 做了些什么,Observable 使用了onNext方法生产了2个水果,Apple和orange ,然后调用了onCompleted方法结束了这次生产, 消费者用onnext方法收到了2个水果,所以消费者就将收到的水果打印出来了,没有做任何处理

执行结果如下:

2018-04-27 10:21:11.440 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :我收到的水果有 = apple
2018-04-27 10:21:11.440 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :我收到的水果有 = orange

然后再给大家介绍一下Hystrix中用得非常多的操作符

create

Observable.create(new Observable.OnSubscribe<String>() {@Overridepublic void call(Subscriber<? super String> subscriber) {subscriber.onNext("item1");subscriber.onNext("item2");subscriber.onCompleted();}
});
//在上面的例子中已经跟大家讲过了,create就是创建一个Observable,来生产消息

from

List<String> fruitList = Arrays.asList("apple","orange");Observable.from(fruitList).subscribe(new Action1<String>() {@Overridepublic void call(String fruit) {LOG.info("fruit = {}" , fruit);}});

上面订阅者的代码被我简化了,直接new 一个Action1, 是subscribe支持的一种订阅方式,跟Subscriber是一样的道理,只是更加简化。然后我们再用lambda表达式简化一下就是这样的了

List<String> fruitList = Arrays.asList("apple","orange");Observable.from(fruitList).subscribe(fruit -> LOG.info("fruit = {}" , fruit));

执行结果如下:

2018-04-27 10:30:59.030 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :fruid = apple
2018-04-27 10:30:59.030 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :fruid = orange

defer

只有当订阅者订阅才创建Observable,为每个订阅创建一个新的Observable。内部通过OnSubscribeDefer在订阅时调用Func0创建Observable

List<String> fruitList = Arrays.asList("apple","orange");Observable.defer(new Func0<Observable<String>>() {@Overridepublic Observable<String> call() {return Observable.from(fruitList);}}).subscribe(new Action1<String>() {@Overridepublic void call(String fruit) {LOG.info("defer fruit = {}" , fruit);}});

不知道大家理解了没有,每次生产消息都会生产一个新的消息生产者

执行结果如下:

2018-04-27 10:37:26.209 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :defer fruit = apple
2018-04-27 10:37:26.209 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :defer fruit = orange

startWith

在生产的第一个消息前加上一个或者一些消息,看例子比较直观

 List<String> fruitList = Arrays.asList("apple","orange");Observable.from(fruitList).startWith("before apple","before apple2").subscribe(fruit->LOG.info("fruit = {}" , fruit));

执行结果如下:

2018-04-27 10:40:52.221 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :fruit = before apple
2018-04-27 10:40:52.221 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :fruit = before apple2
2018-04-27 10:40:52.222 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :fruit = apple
2018-04-27 10:40:52.222 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :fruit = orange

Filter

跟lambda里面的filter很像,也是用来筛选数据的,filter接收的Func1第二个参数是Boolean,定死的

List<Integer> list = Arrays.asList(10, 5, 3, 2, 1, 0);
Observable.from(list).filter(new Func1<Integer, Boolean>() {@Overridepublic Boolean call(Integer integer) {return integer>4;}}).subscribe(num->LOG.info("比4大的num = {}" , num));

Map

Map是将需要生产的数据经过Func1进行变换之后,然后在发送给消费者。第二个参数是Object类型的,可以转化成一个Object对象

List<Integer> list = Arrays.asList(10, 5, 3, 2, 1, 0);Observable.from(list).map(new Func1<Integer, Object>() {@Overridepublic Object call(Integer integer) {return integer+"变成str";}}).subscribe(s -> LOG.info("s = {}" , s));

输出如下:

2018-04-27 11:08:02.743 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :s = 10变成str
2018-04-27 11:08:02.747 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :s = 5变成str
2018-04-27 11:08:02.747 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :s = 3变成str
2018-04-27 11:08:02.748 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :s = 2变成str
2018-04-27 11:08:02.748 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :s = 1变成str
2018-04-27 11:08:02.748 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :s = 0变成str

FlatMap

跟map有些类似,但是也有很大的区别,map是一个对象变成另外一个对象,而flatMap可以把一个对象转化为多个对象 , 其实FlatMap是将一个对象转成了一个Observable 对象,转完之后,最开始的生产者并不发送这个 Observable, 而是将它激活,于是它开始发送事件,每一个创建出来的 Observable 发送的事件,都被汇入同一个 Observable ,而这个 Observable 负责将这些事件统一交给 Subscriber 的回调方法,这样也就产生了一对多的概念,就像是把对象铺平了一样,flat

我会跟lambda的flatMap做一个对比,还是很像的

List<Integer> list = Arrays.asList(10, 5, 3, 2, 1, 0);
List<Object> flatMapcollect = list.stream().flatMap(new Function<Integer, Stream<?>>() {@Overridepublic Stream<?> apply(Integer integer) {//通过list里面的元素创建一个list返回回去List<Integer> list1 = Arrays.asList(integer, integer + 10, integer + 100, integer + 1000);return list1.stream();}}).collect(Collectors.toList());flatMapcollect.forEach(s->LOG.info("complex integer  = {}" , s));

打印的结果是一开始的每个元素10 ,5等都被加上了10 100 1000 然后输出了,相当于平铺了

2018-04-27 11:50:21.186 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :complex integer  = 10   // 10本来数字
2018-04-27 11:50:21.191 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :complex integer  = 20   //加上10之后
2018-04-27 11:50:21.191 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :complex integer  = 110  //加上100之后
2018-04-27 11:50:21.191 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :complex integer  = 1010  //加上1000之后
2018-04-27 11:50:21.191 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :complex integer  = 5
2018-04-27 11:50:21.191 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :complex integer  = 15
2018-04-27 11:50:21.191 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :complex integer  = 105
2018-04-27 11:50:21.191 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :complex integer  = 1005
//限于篇幅,后面的略,都是 源数 加上10 100 1000后输出的

RxJava里面的flatMap, 我乘以10,乘以100然后加下|转成一个String

List<Integer> list = Arrays.asList(10, 5, 3, 2, 1, 0);Observable.from(list).flatMap(new Func1<Integer, Observable<?>>() {@Overridepublic Observable<?> call(Integer num) {List<String> strings = Arrays.asList("|" + num + "|", "|" + num * 10 + "|", "|" + num * 100 + "|");return Observable.from(strings);}}).subscribe(new Action1<Object>() {@Overridepublic void call(Object o) {LOG.info("新转化后的字符串是 = {}" , o);}});

输出如下:

2018-04-27 11:57:13.316 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :新转化后的字符串是 = |10|
2018-04-27 11:57:13.320 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :新转化后的字符串是 = |100|
2018-04-27 11:57:13.320 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :新转化后的字符串是 = |1000|
2018-04-27 11:57:13.320 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :新转化后的字符串是 = |5|
2018-04-27 11:57:13.320 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :新转化后的字符串是 = |50|
2018-04-27 11:57:13.320 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :新转化后的字符串是 = |500|
2018-04-27 11:57:13.320 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :新转化后的字符串是 = |3|
2018-04-27 11:57:13.321 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :新转化后的字符串是 = |30|
2018-04-27 11:57:13.321 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :新转化后的字符串是 = |300|

reduce

reduce是一个聚合函数,接收上次运算的结果,放在下次的参数中,然后输出最后的结果,结果只输出一次。有点类似于递归。跟scan操作符很像,但是有区别,大家看下scan的输出就知道是什么区别了。

RxJava里面的reduce

Observable.from(list).reduce(new Func2<Integer, Integer, Integer>() {@Overridepublic Integer call(Integer result, Integer num) {LOG.info("开始前: result {}, num = {}" , result,num);result+=num;return result;}}).subscribe(new Action1<Integer>() {@Overridepublic void call(Integer result) {LOG.info("result = {}" , result);}});

打印结果如下:

2018-04-27 13:47:29.237 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :开始前: result 10, num = 5
2018-04-27 13:47:29.241 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :开始前: result 15, num = 3
2018-04-27 13:47:29.241 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :开始前: result 18, num = 2
2018-04-27 13:47:29.241 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :开始前: result 20, num = 1
2018-04-27 13:47:29.241 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :开始前: result 21, num = 0
2018-04-27 13:47:29.241 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :result = 21

lambda里面的reduce

List<Integer> list = Arrays.asList(10, 5, 3, 2, 1, 0);Integer sum = list.stream().reduce((result, sum1) -> {result += sum1;return result;}).get();LOG.info("sum = {}" , sum);
//sum是21,就是累加起来

scan

scan和reduce都是把上一次操作的结果做为第二次的参数传递给第二次Observable使用,但是scan每次操作之后先把数据输出,然后在调用scan的回调函数进行第二次操作,看例子

Observable.from(list).scan(new Func2<Integer, Integer, Integer>() {@Overridepublic Integer call(Integer result, Integer num) {LOG.info("开始前: result {}, num = {}" , result,num);result+=num;return result;}}).subscribe(new Action1<Integer>() {@Overridepublic void call(Integer result) {LOG.info("result = {}" , result);}});
2018-04-27 13:47:29.245 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :result = 10
2018-04-27 13:47:29.245 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :开始前: result 10, num = 5
2018-04-27 13:47:29.245 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :result = 15
2018-04-27 13:47:29.245 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :开始前: result 15, num = 3
2018-04-27 13:47:29.245 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :result = 18
2018-04-27 13:47:29.245 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :开始前: result 18, num = 2
2018-04-27 13:47:29.245 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :result = 20
2018-04-27 13:47:29.245 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :开始前: result 20, num = 1
2018-04-27 13:47:29.245 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :result = 21
2018-04-27 13:47:29.245 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :开始前: result 21, num = 0
2018-04-27 13:47:29.245 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :result = 21

每次运算后都会调用订阅者

window

Hystrix 滑动窗口的核心用的就是window操作符,那么window有什么作用呢?他能将Observable的数据分拆成一些Observable窗口,然后把Observable窗口推送给订阅者,而不是一个数据,是一个Observable。来点例子更加直白

window(int count, int skip)

List<Integer> list = Arrays.asList(10, 5, 3, 2, 1, 0);Observable.from(list).window(2, 2).subscribe(new Action1<Observable<Integer>>() {@Overridepublic void call(Observable<Integer> integerObservable) {integerObservable.reduce((sum, num) -> sum+=num).subscribe(new Action1<Integer>() {@Overridepublic void call(Integer integer) {LOG.info("我被2个打印一次 = {}" , integer);}});}});

window里面有2个参数,第一个参数2 表示 选取2个事件,比如说10,5 5,3 等,第二个参数是skip,表示跳跃2个事件,来选取,所以是3组窗口,里面是 (10,5)(3,2)(1,0)

输出:

2018-04-27 14:06:08.703 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :我被2个打印一次 = 15
2018-04-27 14:06:08.707 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :我被2个打印一次 = 5
2018-04-27 14:06:08.707 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :我被2个打印一次 = 1

window(long timespan, TimeUnit unit)

CountDownLatch countDownLatch = new CountDownLatch(1);Observable inputEventStream = Observable.create(new Observable.OnSubscribe<Object>() {@Overridepublic void call(Subscriber<? super Object> subscriber) {subscriber.onNext("我是生产者.........");}});inputEventStream.window(1000,TimeUnit.MILLISECONDS).subscribe(new Action1() {@Overridepublic void call(Object o) {Calendar calendar = Calendar.getInstance();int i = calendar.get(Calendar.SECOND);LOG.info("我会{}就被唤醒触发...",i);}});countDownLatch.await();

输出:

2018-04-27 14:26:18.721 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :我会18就被唤醒触发...
2018-04-27 14:26:19.722 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :我会19就被唤醒触发...
2018-04-27 14:26:20.721 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :我会20就被唤醒触发...
2018-04-27 14:26:21.721 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :我会21就被唤醒触发...
2018-04-27 14:26:22.722 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :我会22就被唤醒触发...
2018-04-27 14:26:23.721 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :我会23就被唤醒触发...
2018-04-27 14:26:24.721 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :我会24就被唤醒触发...
2018-04-27 14:26:25.721 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :我会25就被唤醒触发...
.....
...
..

这里用CountDownLatch 阻塞了主线程的关闭,如果不用锁,那么主线程关闭了之后,你就看不到定时输出了。

第二个demo,用到了window另外的一个函数,第一个参数是缓存在这个window的间隔时间,第二个参数是时间单位 , 1s内收到的所有的生产消息都会缓存到window里面,然后统一发出给订阅者。就好像一个时间轴上面,有个窗子在收集数据,1s钟之后收集好了之后,就发送出去,然后到了第二个窗子,这就是Hystrix滑动窗口的精髓所在。

学以致用

/*** 两个数字相加,reduce,scan用*/public static final Func2<Integer, Integer, Integer> PUBLIC_SUM =(integer, integer2) -> integer + integer2;public static final Func1<Observable<Integer>, Observable<Integer>> WINDOW_SUM =//跳过第一个数据,因为给了scan一个默认值0,这个值需要跳过,如果不设置就不需要跳过window -> window.scan(0, PUBLIC_SUM).skip(1);public static final Func1<Observable<Integer>, Observable<Integer>> INNER_BUCKET_SUM =integerObservable -> integerObservable.reduce(0, PUBLIC_SUM);@Testpublic void testWindowSlide() throws InterruptedException {CountDownLatch countDownLatch = new CountDownLatch(1);BehaviorSubject<Integer> behaviorSubject = BehaviorSubject.create();behaviorSubject// 1秒作为一个基本块,横向移动.window(1000, TimeUnit.MILLISECONDS)//将flatMap汇总平铺成一个事件,然后累加成一个Observable<Integer>对象,比如说1s内有10个对象,被累加起来.flatMap(INNER_BUCKET_SUM)//对这个对象2个发送,步长为1.window(2,1)//对窗口里面的进行求和,用的scan, 每次累加都会打印出来.flatMap(WINDOW_SUM).subscribe((Integer integer) ->// 输出统计数据到日志LOG.info("[{}] call ...... {}",Thread.currentThread().getName(), integer));for (int i = 0; i < 1000; i++) {//200ms生产一个数据,behaviorSubject.onNext(i);LOG.info("i = {}" ,i);Thread.sleep(200);}countDownLatch.await();}

输出:

2018-04-27 15:46:06.547 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 0
2018-04-27 15:46:06.756 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 1
2018-04-27 15:46:07.010 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 2
2018-04-27 15:46:07.211 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 3
2018-04-27 15:46:07.411 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 4
2018-04-27 15:46:07.517 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :[RxComputationScheduler-1] call ...... 10
2018-04-27 15:46:07.517 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :[RxComputationScheduler-1] call ...... 10
2018-04-27 15:46:07.611 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 5
2018-04-27 15:46:07.811 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 6
2018-04-27 15:46:08.011 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 7
2018-04-27 15:46:08.211 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 8
2018-04-27 15:46:08.411 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 9
2018-04-27 15:46:08.517 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :[RxComputationScheduler-1] call ...... 45
2018-04-27 15:46:08.517 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :[RxComputationScheduler-1] call ...... 35
2018-04-27 15:46:08.611 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 10
2018-04-27 15:46:08.811 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 11
2018-04-27 15:46:09.011 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 12
2018-04-27 15:46:09.211 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 13
2018-04-27 15:46:09.411 INFO  [#][#] <main> com.dzy.learn.other.NormalTest :i = 14
2018-04-27 15:46:09.517 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :[RxComputationScheduler-1] call ...... 95
2018-04-27 15:46:09.517 INFO  [#][#] <RxComputationScheduler-1> com.dzy.learn.other.NormalTest :[RxComputationScheduler-1] call ...... 60

解析,第一个window产生了一个滑动窗口,每秒钟就会把生产者生产的消息累加起来,第二个window是积累2个对象,然后进行发送,每次跳一个数字,第二个window是建立在第一个windows累加之后的基础上的,可能有点难理解,我们来看第一个window产生的序列如下:

0  10  35  60  85  ......

有的同学可能会问,你怎么知道,我看的log日志,打印出来的序列是 10 10 、 45 35 、95 60 、 145 85 、因为这里用的scan,每次累加之后都会把源数打印一遍,所以是0 10 35 60 85 。第二个window就在这个基础上进行累加 0+10 10+35 35+60 60+85,这样就完成了一个滑动窗口的监控过程

结语

这里总结的也许不是最全的,也许不是最新的版本,但是是Hystrix中用到的,结合Hystrix进行针对性讲解,对Hystrix的理解更加深刻,如有错误,望加以斧正,谢谢。

这篇关于Hystrix核心篇-滑动窗口基础的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1039314

相关文章

MySQL中my.ini文件的基础配置和优化配置方式

《MySQL中my.ini文件的基础配置和优化配置方式》文章讨论了数据库异步同步的优化思路,包括三个主要方面:幂等性、时序和延迟,作者还分享了MySQL配置文件的优化经验,并鼓励读者提供支持... 目录mysql my.ini文件的配置和优化配置优化思路MySQL配置文件优化总结MySQL my.ini文件

bat脚本启动git bash窗口,并执行命令方式

《bat脚本启动gitbash窗口,并执行命令方式》本文介绍了如何在Windows服务器上使用cmd启动jar包时出现乱码的问题,并提供了解决方法——使用GitBash窗口启动并设置编码,通过编写s... 目录一、简介二、使用说明2.1 start.BAT脚本2.2 参数说明2.3 效果总结一、简介某些情

基于Redis有序集合实现滑动窗口限流的步骤

《基于Redis有序集合实现滑动窗口限流的步骤》滑动窗口算法是一种基于时间窗口的限流算法,通过动态地滑动窗口,可以动态调整限流的速率,Redis有序集合可以用来实现滑动窗口限流,本文介绍基于Redis... 滑动窗口算法是一种基于时间窗口的限流算法,它将时间划分为若干个固定大小的窗口,每个窗口内记录了该时间

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

C 语言基础之数组

文章目录 什么是数组数组变量的声明多维数组 什么是数组 数组,顾名思义,就是一组数。 假如班上有 30 个同学,让你编程统计每个人的分数,求最高分、最低分、平均分等。如果不知道数组,你只能这样写代码: int ZhangSan_score = 95;int LiSi_score = 90;......int LiuDong_score = 100;int Zhou

使用JS/Jquery获得父窗口的几个方法(笔记)

<pre name="code" class="javascript">取父窗口的元素方法:$(selector, window.parent.document);那么你取父窗口的父窗口的元素就可以用:$(selector, window.parent.parent.document);如题: $(selector, window.top.document);//获得顶级窗口里面的元素 $(