1、定义
背压是指在异步场景中,被观察者发送事件速度远快于观察者的处理速度的情况下,一种告诉上游的被观察者降低发送速度的策略。简而言之,背压是流速控制的一种策略。
Flowable (被观察者)/ Subscriber (观察者)
2、举个栗子-没有背压情况下
使用背压策略之前,我们先看一下,无背压策略情况下会出现什么问题?
如图中所示, 其中蓝色的框框就是zip
给我们的水缸
! 它将每根水管发出的事件保存起来, 等两个水缸都有事件了之后就分别从水缸中取出一个事件来组合, 当其中一个水缸是空的时候就处于等待的状态.
Observable<Integer> observable1 = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
for (int i = 0; ; i++) { //无限循环发事件
emitter.onNext(i);
}
}
}).subscribeOn(Schedulers.io());
Observable<String> observable2 = Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("A");
}
}).subscribeOn(Schedulers.io());
Observable.zip(observable1, observable2, new BiFunction<Integer, String, String>() {
@Override
public String apply(Integer integer, String s) throws Exception {
return integer + s;
}
}).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<String>() {
@Override
public void accept(String s) throws Exception {
Logger.d(s);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Logger.w(throwable);
}
});
在这个例子中, 我们分别创建了两根水管, 第一根水管用机器指令的执行速度来无限循环发送事件, 第二根水管随便发送点什么, 由于我们没有发送Complete
事件, 因此第一根水管会一直发事件到它对应的水缸里去。
3、正题-Flowable / Subscriber
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
Logger.d("emit 1");
emitter.onNext(1);
Logger.d("emit 2");
emitter.onNext(2);
Logger.d("emit 3");
emitter.onNext(3);
Logger.d("emit complete");
emitter.onComplete();
}
}, BackpressureStrategy.ERROR)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Logger.d("onSubscribe");
mSubscription = s;
}
@Override
public void onNext(Integer integer) {
Logger.d("onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Logger.w("onError: ", t);
}
@Override
public void onComplete() {
Logger.d("onComplete");
}
});
看上面这段代码,我们可以发现其实与Observable/Observer基本一样,区别就是发射器ObservableEmitter替换成了FlowableEmitter,Disposable替换成了Subscription,增加了一个参数背压模式BackpressureStrategy.ERROR(有四种,后面讲)。
运行这段代码,没有报错,但我们只看到了上游发送日志,下游怎么没有接收呢?因为上游发送的事件,先会存放到水缸里(缓存),等待下游接收。那么下游怎么接收呢?
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
Logger.d("emit 1");
emitter.onNext(1);
Logger.d("emit 2");
emitter.onNext(2);
Logger.d("emit 3");
emitter.onNext(3);
Logger.d("emit complete");
emitter.onComplete();
}
}, BackpressureStrategy.ERROR)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Logger.d("onSubscribe");
s.request(1);
mSubscription = s;
}
@Override
public void onNext(Integer integer) {
Logger.d("onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Logger.w("onError: ", t);
}
@Override
public void onComplete() {
Logger.d("onComplete");
}
});
}
private void request(long n) {
mSubscription.request(n); //在外部调用request请求上游
}
通过Subscription.request(n)方法接收数据。onComplete()和OnError()不占用事件个数。
通过上面两个例子,以及之前所讲的,我们可以分析一下Observable和Flowable两种模式的数据传输有哪些不同了。
1、Observable/Oberver,上游发送数据放到缓存区,下游
自动
从缓存获取数据,中间没有开关控制,如果上游发送数据的速率超过了下游接收处理数据的速率,那么缓存区的数据就会不断增大,(缓存大小默认最大缓存),直到OOM。
2、Flowable/Subscriber,上游发送数据放到缓存区,下游不会自动从缓存区获取数据,中间有开关控制,
Subscription.request(n),控制接收数据的个数。如果上游一直发,而下游不接受,那么缓存区会怎么样呢?这就是下面我们要讲的了--背压策略(缓存模式)
4、背压策略(缓存模式)--BackpressureStrategy
/** The default buffer size. */
static final int BUFFER_SIZE;
static {
BUFFER_SIZE = Math.max(16, Integer.getInteger("rx2.buffer-size", 128));
}
从Flowable的源码中我们可以找到,默认缓存大小为128。
/**
* Represents the options for applying backpressure to a source sequence.
*/
public enum BackpressureStrategy {
/**
* OnNext events are written without any buffering or dropping.
* Downstream has to deal with any overflow.
* <p>Useful when one applies one of the custom-parameter onBackpressureXXX operators.
*/
MISSING,
/**
* Signals a MissingBackpressureException in case the downstream can't keep up.
*/
ERROR,
/**
* Buffers <em>all</em> onNext values until the downstream consumes it.
*/
BUFFER,
/**
* Drops the most recent onNext value if the downstream can't keep up.
*/
DROP,
/**
* Keeps only the latest onNext value, overwriting any previous value if the
* downstream can't keep up.
*/
LATEST
}
共有5种模式:
- MISSING 当缓存区大小超过128的时候,会自动发送一个onError事件,即:MissingBackpressureException: Queue is full?!。因为onError事件并不占用上游发送事件个数,也就是不会经过缓存区,而是直接被下游接收,在前面我们有提到,下游接收onError或onComplete事件后就不会再接收后续事件,所以在缓存队列的事件,下游就无法接收了。
- ERROR 和MISSING相似,(在使用效果上),也会自动发送一个onError事件,不同的是错误信息,MissingBackpressureException: create: could not emit value due to lack of requests。为何会这样我还没搞清楚,有兴趣的同学去看源码吧!网上基本是找不到的。
- BUFFER 缓存大小没有控制,也就是上游发送的事件,如果下游没接收,会一直在缓存区里,直到缓存溢出即OOM,所这个我们一定要小心控制。
- DROP 当缓存区大小超过128的时候,后续发送的事件就会被丢弃,直到缓存区的数据被下游接收,上游发送的数据才能放入缓存区内。
- LATEST 和DROP类似,区别在于它总能获取到最新的一条事件
看下面的例子:
private void request(long n) {
mSubscription.request(n); //在外部调用request请求上游
}
private void backpressureStrategy(BackpressureStrategy strategy) {
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
for (int i = 0; i < 1000; i++) {
if (emitter.isCancelled()){
break;
}
Logger.d("emit: " + i);
emitter.onNext(i);
}
}
}, strategy)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Logger.d("onSubscribe");
mSubscription = s;
}
@Override
public void onNext(Integer integer) {
Logger.d("onNext: " + integer);
}
@Override
public void onError(Throwable t) {
if (mSubscription != null) {
mSubscription.cancel();
}
Logger.w("onError: ", t);
}
@Override
public void onComplete() {
Logger.d("onComplete");
}
});
}
传入不同模式的BackpressureStrategy,通过打印日志看它们的不同点,大家可以自行测试。
这里大家看到mSubscription.cancel(),这是干啥的?想必大家一眼都能看出来,就是告诉上游不要在发送数据了。但只是这样其实是没啥用的,必须还要在上游加emitter.isCancelled()这个判断,它们是成对出现的。这就是和Observable/Oberver
不同了,那个是直接调用dispose()就可以了。
结合上面讲的,我们会发现,Observable都是单方面发出指令就可以实现发送和取消发送;而Flowable必须要上下游都发出指令才行。差不多就是这个意思吧,虽然不是很准确.....
讲了这么多,这些都是我们自建的FLowable,那么框架提供的操作符,怎么添加背压策略呢?
- onBackpressureBuffer()
- onBackpressureDrop()
- onBackpressureLatest()
Flowable.interval(1, TimeUnit.MICROSECONDS)
.onBackpressureDrop() //加上背压策略
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Long>() {
@Override
public void onSubscribe(Subscription s) {
Logger.d("onSubscribe");
mSubscription = s;
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Long aLong) {
Logger.d("onNext: " + aLong);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable t) {
Logger.w("onError: ", t);
}
@Override
public void onComplete() {
Logger.d("onComplete");
}
});
是不是很简单,其它操作符都一样,就不再演示了。
5、重点来了
上面我们讲了很多,但上游怎么知道下游接收速度呢?我们讲的背压策略不是说:被观察者发送事件速度远快于观察者的处理速度的情况下,一种告诉上游的被观察者降低发送速度的策略。
public interface FlowableEmitter<T> extends Emitter<T> {
/**
* Sets a Disposable on this emitter; any previous Disposable
* or Cancellation will be disposed/cancelled.
* @param s the disposable, null is allowed
*/
void setDisposable(@Nullable Disposable s);
/**
* Sets a Cancellable on this emitter; any previous Disposable
* or Cancellation will be disposed/cancelled.
* @param c the cancellable resource, null is allowed
*/
void setCancellable(@Nullable Cancellable c);
/**
* The current outstanding request amount.
* <p>This method is thread-safe.
* @return the current outstanding request amount
*/
long requested();
/**
* Returns true if the downstream cancelled the sequence.
* <p>This method is thread-safe.
* @return true if the downstream cancelled the sequence
*/
boolean isCancelled();
/**
* Ensures that calls to onNext, onError and onComplete are properly serialized.
* @return the serialized FlowableEmitter
*/
@NonNull
FlowableEmitter<T> serialize();
}
long requested();//这个就是缓存队列剩余可发送事件数量
在上游通过判断这个方法返回的数量是否为0,来控制上游发送事件的速度。
Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
Logger.d("First requested = " + emitter.requested());
boolean flag;
for (int i = 0; i < 500; i++) {
flag = false;
//这个循环作用是当下游没有及时处理事件的时候(也就是没有缓存空间了),暂停发送事件;
while (emitter.requested() == 0) {
if (!flag) {
Logger.e("不能发了!");
flag = true;
}
}
emitter.onNext(i);
Logger.d("emit: " + i + " requested: " + emitter.requested());
}
}
}, BackpressureStrategy.ERROR)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Logger.d("onSubscribe");
mSubscription = s;
}
@Override
public void onNext(Integer integer) {
Logger.d("onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Logger.w("onError: ", t);
}
@Override
public void onComplete() {
Logger.d("onComplete");
}
});
private void request() {
mSubscription.request(96); //96有特殊意义
}
通过这个实例,其实我们会发现,下游request(n)所设定的值,和上游requested()返回的值是不同的,默认一直是128,上游并没有收到下游控制啊?我们看下面一张图,可能就理解了。
因为这是在两个线程内,每个线程里都有一个request(n),上游的是系统自动调用的,默认大小是128。那么它是怎么触发的呢?答案就是下游的request(n),这里的n的值是触发的关键点,只有大于96的时候才会触发。大家是试一下。
好了,关于Rxjava2的使用就写到这里。后面会学习一下源码。