前言

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>() {
@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);

先来一个简单的例子给大家直观的介绍下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>() {
@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的更多相关文章

  1. 算法与数据结构基础 - 滑动窗口(Sliding Window)

    滑动窗口基础 滑动窗口常用来解决求字符串子串问题,借助map和计数器,其能在O(n)时间复杂度求子串问题.滑动窗口和双指针(Two pointers)有些类似,可以理解为往同一个方向走的双指针.常用滑 ...

  2. VC++窗口创建过程,图形绘制,时钟程序

    创建窗口步骤: (1)注册窗口类(RegisterClassEx) (2)创建窗口(CreateWindowEx) (3)在桌面显示窗口(ShowWindow) (4)更新窗口客户区(UpdateWi ...

  3. Win32窗口创建过程

    编写窗口程序的步骤:    1 定义WinMain函数    2 定义窗口处理函数–自己定义处理消息    3 注册窗口类(往OS写入数据)    4 创建窗口 (在内存中创建窗口)    5 显示窗 ...

  4. LeetCode编程训练 - 滑动窗口(Sliding Window)

    滑动窗口基础 滑动窗口常用来解决求字符串子串问题,借助map和计数器,其能在O(n)时间复杂度求子串问题.滑动窗口和双指针(Two pointers)有些类似,可以理解为往同一个方向走的双指针.常用滑 ...

  5. [TCP/IP] 滑动窗口

    什么是滑动窗口? 滑动窗口机制是TCP协议的一种流量控制和防拥塞的机制. 滑动窗口的工作原理? 简单来讲,就是接收方和发送方分别保留一块缓冲区,作为接收和发送数据来使用,发送数据过程中,如果发送方发的 ...

  6. 深入解析Windows窗口创建和消息分发(三个核心问题:怎么将不同的窗口过程勾到一起,将不同的hwnd消息分发给对应的CWnd类去处理,CWnd如何简单有效的去处理消息,由浅入深,非常清楚) good

    笔记:争取不用看下面的内容,只看自己的笔记,就能记住这个流程,就算明白了: _tWinMain-->AfxWinMain,它调用四个函数: -->AfxWinInit用于做一些框架的初始化 ...

  7. MFC应用程序创建窗口的过程 good

    MFC应用程序中处理消息的顺序 1.AfxWndProc()      该函数负责接收消息,找到消息所属的CWnd对象,然后调用AfxCallWndProc 2.AfxCallWndProc()  该 ...

  8. vc++窗口的创建过程(MFC消息机制的经典文章)

    一.什么是窗口类  在Windows中运行的程序,大多数都有一个或几个可以看得见的窗口,而在这些窗口被创建起来之前,操作系统怎么知道该怎样创建该窗口,以及用户操作该窗口的各种消息交给谁处理呢?所以VC ...

  9. 【转】20-TCP 协议(滑动窗口——基础)

    https://blog.csdn.net/q1007729991/article/details/70142341 相信大家都遇到过这样的场景: 同学 Luffy 给你打电话,让你记下一串手机号码, ...

随机推荐

  1. ssh配置免登 Ubuntu环境

    配置之前,可能需要修改下每台机器的hostname,修改方法 1.直接修改hostname文件:sudo vi /etc/hostname 2.重启服务器:shutdown -r now Ubuntu ...

  2. AD9各种布线总结

    1.常规布线:不详细说了,是个人就知道怎么弄.需要说明的是在布线过程中,可按小键盘的*键或大键盘的数字2键添加一个过孔:按L键可以切换布线层:按数字3可设定最小线宽.典型线宽.最大线宽的值进行切换. ...

  3. Spring整合JUnit4测试时,使用注解引入多个配置文件

    转自:https://blog.csdn.net/pwh309315228/article/details/62226372 一般情况下: @ContextConfiguration(Location ...

  4. 关于cin

    今天同学调试一个简单的程序的时候发现了问题,我们两个讨论的时候弄出了好多乐子 #include <iostream> using namespace std; int main() { ; ...

  5. struts2.Action中的method属性配置

    .Action中的method属性 在struts1.x中我们知道通过继承DispatchAction可以实现把多个Action进行统一操作,在struts2中实现action的统一操作也很简单.我们 ...

  6. 【总结整理】JQuery基础学习---DOM篇

    前言: 先介绍下需要用到的浏览器提供的一些原生的方法(这里不处理低版本的IE兼容问题) 创建流程比较简单,大体如下: 创建节点(常见的:元素.属性和文本) 添加节点的一些属性 加入到文档中 流程中涉及 ...

  7. ajax的回调函数和匿名函数

    1.什么是js回调函数 一. 回调函数的作用 js代码会至上而下一条线执行下去,但是有时候我们需要等到一个操作结束之后再进行下一个操作,这时候就需要用到回调函数. 二. 回调函数的解释 因为函数实际上 ...

  8. ES Docs-1:Installation Elasticsearch-2.3.1

    installation Elasticsearch requires at least Java 7. Specifically as of this writing, it is recommen ...

  9. Error creating form bean of class com.onlinebookstore.presentation.CatalogBean

    Error creating form bean of class com.onlinebookstore.presentation.CatalogBean 可能是action form未编译 这个问 ...

  10. 【service调用dao层传参的三种方式】

    第一种方案:默认数组角标: service Public User selectUser(String name,String area); mapper: <select id="s ...