Hystrix核心基础 - 滑动窗口创建过程及demo
前言
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
的抽象实现类,本质上使用是一样的)、事件。Observable
和 Observer
通过 subscribe()
方法实现订阅关系,从而 Observable
可以在需要的时候发出事件来通知 Observer
。Observable
就像是一个生产者,在不断的生产消息,而Subscriber
和 Observer
就像是一个消费者,在不断的消费消息
另外, RxJava 的事件回调方法还定义了两个特殊的事件,在Hystrix中用得也非常多:onCompleted()
和 onError()
。
onCompleted()
: 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava 规定,当不会再有新的onNext()
发出时,需要触发onCompleted()
方法作为标志。onError()
: 事件队列异常。在事件处理过程中出异常时,onError()
会被触发,同时队列自动终止,不允许再有事件发出。
怎么做
说了这么多概念,估计大家都是一头雾水,我们直接来些实际的,加深大家的印象理解。用多的自然而然就会了,就懂了,这里说得可能不是最全的,但是说的都是Hystrix中用得很多的一些操作符,加深大家对Hystrix的理解,看源码就会容易一些。
例子
Observable<String> producer = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
subscriber.onNext("apple");
subscriber.onNext("orange");
subscriber.onCompleted();
}
});
Subscriber<String> consumer = new Subscriber<String>() {
@Override
public void onNext(String s) {
LOG.info("我收到的水果有 = {}" , s);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
};
producer.subscribe(consumer);
先来一个简单的例子给大家直观的介绍下Observable
和 Subscriber
做了些什么,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>() {
@Override
public 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>() {
@Override
public 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>>() {
@Override
public Observable<String> call() {
return Observable.from(fruitList);
}
}).subscribe(new Action1<String>() {
@Override
public 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>() {
@Override
public 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>() {
@Override
public 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<?>>() {
@Override
public 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<?>>() {
@Override
public Observable<?> call(Integer num) {
List<String> strings = Arrays.asList("|" + num + "|", "|" + num * 10 + "|", "|" + num * 100 + "|");
return Observable.from(strings);
}
}).subscribe(new Action1<Object>() {
@Override
public 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>() {
@Override
public Integer call(Integer result, Integer num) {
LOG.info("开始前: result {}, num = {}" , result,num);
result+=num;
return result;
}
}).subscribe(new Action1<Integer>() {
@Override
public 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>() {
@Override
public Integer call(Integer result, Integer num) {
LOG.info("开始前: result {}, num = {}" , result,num);
result+=num;
return result;
}
}).subscribe(new Action1<Integer>() {
@Override
public 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>>() {
@Override
public void call(Observable<Integer> integerObservable) {
integerObservable.reduce((sum, num) -> sum+=num).subscribe(new Action1<Integer>() {
@Override
public 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>() {
@Override
public void call(Subscriber<? super Object> subscriber) {
subscriber.onNext("我是生产者.........");
}
});
inputEventStream.window(1000,TimeUnit.MILLISECONDS).subscribe(new Action1() {
@Override
public 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);
@Test
public 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核心基础 - 滑动窗口创建过程及demo的更多相关文章
- 算法与数据结构基础 - 滑动窗口(Sliding Window)
滑动窗口基础 滑动窗口常用来解决求字符串子串问题,借助map和计数器,其能在O(n)时间复杂度求子串问题.滑动窗口和双指针(Two pointers)有些类似,可以理解为往同一个方向走的双指针.常用滑 ...
- VC++窗口创建过程,图形绘制,时钟程序
创建窗口步骤: (1)注册窗口类(RegisterClassEx) (2)创建窗口(CreateWindowEx) (3)在桌面显示窗口(ShowWindow) (4)更新窗口客户区(UpdateWi ...
- Win32窗口创建过程
编写窗口程序的步骤: 1 定义WinMain函数 2 定义窗口处理函数–自己定义处理消息 3 注册窗口类(往OS写入数据) 4 创建窗口 (在内存中创建窗口) 5 显示窗 ...
- LeetCode编程训练 - 滑动窗口(Sliding Window)
滑动窗口基础 滑动窗口常用来解决求字符串子串问题,借助map和计数器,其能在O(n)时间复杂度求子串问题.滑动窗口和双指针(Two pointers)有些类似,可以理解为往同一个方向走的双指针.常用滑 ...
- [TCP/IP] 滑动窗口
什么是滑动窗口? 滑动窗口机制是TCP协议的一种流量控制和防拥塞的机制. 滑动窗口的工作原理? 简单来讲,就是接收方和发送方分别保留一块缓冲区,作为接收和发送数据来使用,发送数据过程中,如果发送方发的 ...
- 深入解析Windows窗口创建和消息分发(三个核心问题:怎么将不同的窗口过程勾到一起,将不同的hwnd消息分发给对应的CWnd类去处理,CWnd如何简单有效的去处理消息,由浅入深,非常清楚) good
笔记:争取不用看下面的内容,只看自己的笔记,就能记住这个流程,就算明白了: _tWinMain-->AfxWinMain,它调用四个函数: -->AfxWinInit用于做一些框架的初始化 ...
- MFC应用程序创建窗口的过程 good
MFC应用程序中处理消息的顺序 1.AfxWndProc() 该函数负责接收消息,找到消息所属的CWnd对象,然后调用AfxCallWndProc 2.AfxCallWndProc() 该 ...
- vc++窗口的创建过程(MFC消息机制的经典文章)
一.什么是窗口类 在Windows中运行的程序,大多数都有一个或几个可以看得见的窗口,而在这些窗口被创建起来之前,操作系统怎么知道该怎样创建该窗口,以及用户操作该窗口的各种消息交给谁处理呢?所以VC ...
- 【转】20-TCP 协议(滑动窗口——基础)
https://blog.csdn.net/q1007729991/article/details/70142341 相信大家都遇到过这样的场景: 同学 Luffy 给你打电话,让你记下一串手机号码, ...
随机推荐
- DSP/BIOS程序启动顺序
基于TI的DSP芯片的应用程序分为两种:一般应用程序:DSP/BIOS应用程序. 为简化编程,TI提供了一套C的编程接口,它以API和宏的形式封装了TI的所有硬件模块,这套接口统称DSP/BIOS.D ...
- 【转】Pro Android学习笔记(十五):用户界面和控制(3):Button控件
目录(?)[-] 基础Button ImageButton ToggleButton CheckBox RadioButton 基础Button Button是最常用的基础控件之一,在Android中 ...
- Java enum(枚举)使用详解之三
DK1.5引入了新的类型——枚举.在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便. 用法一:常量 在JDK1.5 之前,我们定义常量都是: publicstaticfianl... ...
- Vue之vue.js声明式渲染
Html: <div id="app"> {{ message }} </div> Vue: var app = new Vue({ el: '#app', ...
- centos6.5安装dubbo管控台教程(四)
阅读此文之前,需要先安装zookeeper. 阅读文章: http://www.cnblogs.com/duenboa/articles/6665169.html 1. 下载文件 dubbo-ad ...
- NEKOGAMES
http://bbs.3dmgame.com/thread-4133434-1-1.html
- \阶段4-独挡一面\项目-基于视频压缩的实时监控系统\Sprint2-采集端图像采集子系统设计
1.在编写程序前有一个流程,思维导图: 初始化:包括初始化摄像头:注册事件到epoll 然后是开始启动采集:一旦开始采集我们的摄像头就会有数据了,它会触发事件处理函数:我们在这里的处理是保存这个图像: ...
- android开源项目:图片下载缓存库picasso
picasso是Square公司开源的一个Android图形缓存库,地址http://square.github.io/picasso/,可以实现图片下载和缓存功能. picasso有如下特性: 在a ...
- HTML5 & CSS3编程入门经典 ((美)Rob Larsen) pdf扫描版
HTML和CSS是构建网页所需要了解的两种核心编程语言,拉尔森编著的这本<HTML5&CSS3编程入门经典>详细介绍了这两种语言. <HTML5&CSS3编程入门经典 ...
- 如何配置使用Dnsmasq
此文已由作者赵斌授权网易云社区发布 欢迎访问网易云社区,了解更多网易技术产品运营经验. 一.前言 最近为了测试内容分发网络(Content Delivery Network,简称 CDN)CDN在调用 ...