什么是背压(Backpressure)
(most from zhuanlan.zhihu.com/p/24473022?…)
背压是指在异步场景中,被观察者发送事件速度远快于观察者的处理速度的情况下,一种告诉上游的被观察者降低发送速度的策略。简而言之,背压是流速控制的一种策略。
若被观察者发送事件的速度太快,而观察者处理太慢,而且还没有做相应背压措施,可能抛出MissingBackpressureException
压力异常示例
Observable.interval(1, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.newThread())
.subscribe(aLong -> {
Log.w("TAG","---->"+aLong);
});
上面的interval操作符,在1毫秒产生一个事件,速率过快,订阅者”消费”事件来不及处理,就会出现异常。
自带背压处理的操作符
用自带背压处理的操作符来处理压力。
过滤策略
使用之前讲的”过滤操作符”,就可以有效缓解压力。
比如,使用throttleFirst来获取一段周期时间内的首个事件。
Observable.interval(1, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.newThread())
.throttleFirst(200, TimeUnit.MILLISECONDS)
.subscribe(aLong -> {
Log.w("TAG","---->"+aLong);
});
缓存策略
缓存就是虽然被观察者发送事件速度很快,观察者处理不过来,但是可以选择先缓存一部分,然后慢慢读。
主要用到的是buffer操作符
Observable.interval(1, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.newThread())
//这个操作符简单理解就是把100毫秒内的事件打包成list发送
.buffer(100,TimeUnit.MILLISECONDS)
.subscribe(aLong -> {
Log.w("TAG","---->"+aLong);
});
按需拉取策略
就是需要”消费”多少个事件,自己告诉被观察者,最终实现了上游被观察者发送事件的速度的控制
主要使用request(long n)。这是一个protected方法
Observable.range(1, 10000)//生产从1到10000的数
.observeOn(Schedulers.newThread())
.subscribe(new Subscriber<Integer>() {
@Override
public void onStart() {
super.onStart();
//在onStart中通知被观察者先发送一个事件
request(1);
}
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Integer integer) {
Log.w("TAG","---->"+ integer);
request(1); //处理完毕之后,再通知被观察者发送下一个事件
}
});
上面的代码中,其实可以去掉request相关代码,因 range –> observeOn,这一段过程本身就是响应式拉取数据。
observeOn这个操作符内部有一个缓冲区RxRingBuffer,其在Android环境下长度是16,它会告诉range最多发送16个事件,充满缓冲区即可
让不支持背压的Observable“支持”背压
对于不支持背压的Observable除了使用上述两类生硬的操作符之外,还有更好的选择:onBackpressureBuffer、onBackpressureDrop。
onBackpressurebuffer:把Observable发送出来的事件做缓存,当request方法被调用的时候,
给下层流发送一个item(如果给这个缓存区设置了大小,那么超过了这个大小就会抛出异常)。
onBackpressureDrop:将Observable发送的事件抛弃掉,直到Subscriber再次调用request(n)方法的时候,
就发送给它这之后的n个事件。
使用了这两种操作符,可以让原本不支持背压的Observable“支持”背压了。
Observable.interval(1, TimeUnit.MILLISECONDS)
.onBackpressureBuffer()
.observeOn(Schedulers.newThread())
.subscribe(aLong -> {
Log.w("TAG--","---->"+aLong);
});