本文参考:https://www.jianshu.com/p/a75ecf461e02
3.9 背压的策略
我们在上一篇文章中说到了背压的策略问题,总共有五个策略,在这里列个表格展示一下吧。
策略 | 含义 |
---|---|
BackpressureStrategy.MISSING | 如果流的速度无法保持同步,可能会抛出MissingBackpressureException或IllegalStateException |
BackpressureStrategy.ERROR | 会在下游跟不上速度时抛出MissingBackpressureException |
BackpressureStrategy.BUFFER | 上游不断的发出onNext请求,直到下游处理完,也就是和Observable一样了,缓存池无限大,最后直到程序崩溃 |
BackpressureStrategy.DROP | 会在下游跟不上速度时把onNext的值丢弃 |
BackpressureStrategy.LATEST | 会一直保留最新的onNext的值,直到被下游消费掉 |
我们先看BackpressureStrategy.BUFFER。
Flowable<Integer> flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
for (int i = 0; ; i++) {
Log.i(TAG, "subscribe: " + i);
emitter.onNext(i);
}
}
}, BackpressureStrategy.BUFFER);
Subscriber<Integer> subscriber = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
mSubscription = s;
Log.i(TAG, "onSubscribe: ");
}
@Override
public void onNext(Integer integer) {
Log.i(TAG, "onNext: ");
}
@Override
public void onError(Throwable t) {
Log.i(TAG, "onError: ");
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
};
flowable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);
BUFFER策略使得Flowable与Observable相似,它会改变Flowable缓存区的大小,将数据不停的放到这里。在实验的时候你可能会感觉Flowable内存占用率的上升时间比较慢,这是因为Flowable在响应式拉取方面做了更多的操作,所以性能方面有所损失也是在所难免的。
从这个角度看,不能因为使用了Flowable就放松代码的要求,一味的滥用肯定会造成OOM或者别的问题。
然后是DROP。
private void test25() {
Flowable<Integer> flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
for (int i = 0; i < 1000; i++) {
emitter.onNext(i);
}
}
}, BackpressureStrategy.DROP);
Subscriber<Integer> subscriber = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
Log.i(TAG, "onSubscribe: ");
mSubscription = s;
}
@Override
public void onNext(Integer integer) {
Log.i(TAG, "onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Log.i(TAG, "onError: ");
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
};
flowable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);
}
public void requestClick(View view) {
//点击一次按钮 请求一次数据
mSubscription.request(128);
}
下游处理速度跟不上时Drop会把值丢弃掉。Flowable的缓存区是128,所以上游会先将0~127存储到缓存区,然后到了128,Flowable会直接将128丢掉,剩下的值也类似。当我们requset(128)时,上游将缓存区中的128个数字发送到下游,下游然后打印出来。但是当我们再此点击时,却不会打印数据了,因为for循环跑完了,没有数据存储到缓存区,所以没有打印。
然后是LATEST。
private void test26() {
Flowable<Integer> flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
for (int i = 0; i < 1000; i++) {
emitter.onNext(i);
}
}
}, BackpressureStrategy.LATEST);
Subscriber<Integer> subscriber = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
mSubscription = s;
Log.i(TAG, "onSubscribe: ");
}
@Override
public void onNext(Integer integer) {
Log.i(TAG, "onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Log.i(TAG, "onError: " + t);
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
};
flowable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);
}
public void requestClick(View view) {
//点击一次按钮 请求一次数据
mSubscription.request(128);
}
从Log中可以看到,与DROP不同的是,LATEST总是可以获取最后一个onNext数值。
MISSING就比较好理解了,下游无法处理上游数据时,便会抛出错误,与ERROR相似。抛出的Log如下所示:
I/RxJavaTestHaHa: onError: io.reactivex.exceptions.MissingBackpressureException: Queue is full?!
至此,背压的策略我们就过了一遍,
然后是一个小例子,如果是别人的Flowable我们怎么选择背压策略。
onBackpressureBuffer()
onBackpressureDrop()
onBackpressureLatest()
Flowable.interval(1, TimeUnit.MICROSECONDS)
.onBackpressureDrop() //加上背压策略
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Long>() {
@Override
public void onSubscribe(Subscription s) {
Log.d(TAG, "onSubscribe");
mSubscription = s;
s.request(Long.MAX_VALUE);
}
@Override
public void onNext(Long aLong) {
Log.d(TAG, "onNext: " + aLong);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onError(Throwable t) {
Log.w(TAG, "onError: ", t);
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
});
3.10 响应式请求
在前文中受到了Flowable是响应式请求,既然下游能request,那在上游肯定可以得到这个数值,这个值就是FlowableEmitter中的requested。
现在同步的DEMO中看一下是如何请求的。
Flowable<Integer> flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
Log.i(TAG, "subscribe: " + emitter.requested());
emitter.onNext(3);
Log.i(TAG, "subscribe: " + emitter.requested());
emitter.onNext(2);
Log.i(TAG, "subscribe: " + emitter.requested());
emitter.onNext(1);
Log.i(TAG, "subscribe: " + emitter.requested());
emitter.onComplete();
Log.i(TAG, "subscribe: " + emitter.requested());
}
}, BackpressureStrategy.ERROR);
Subscriber<Integer> subscriber = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
mSubscription = s;
mSubscription.request(100);
mSubscription.request(30);
Log.i(TAG, "onSubscribe: ");
}
@Override
public void onNext(Integer integer) {
Log.i(TAG, "onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Log.i(TAG, "onError: " + t);
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
};
flowable.subscribe(subscriber);
I/RxJavaTestHaHa: onSubscribe:
I/RxJavaTestHaHa: subscribe: 130
I/RxJavaTestHaHa: onNext: 3
I/RxJavaTestHaHa: subscribe: 129
I/RxJavaTestHaHa: onNext: 2
I/RxJavaTestHaHa: subscribe: 128
I/RxJavaTestHaHa: onNext: 1
I/RxJavaTestHaHa: subscribe: 127
I/RxJavaTestHaHa: onComplete:
I/RxJavaTestHaHa: subscribe: 127
从Log中可以直观的看到,多次request时是会做加法,加在一起,100+30=130,发送一个onNext事件时是会做减法,而onComplete事件不会减少requested的值,那在异步中是什么样的。
Flowable<Integer> flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
Log.i(TAG, "subscribe: " + emitter.requested());
emitter.onNext(3);
Log.i(TAG, "subscribe: " + emitter.requested());
emitter.onNext(2);
Log.i(TAG, "subscribe: " + emitter.requested());
emitter.onNext(1);
Log.i(TAG, "subscribe: " + emitter.requested());
emitter.onComplete();
Log.i(TAG, "subscribe: " + emitter.requested());
}
}, BackpressureStrategy.ERROR);
Subscriber<Integer> subscriber = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
mSubscription = s;
mSubscription.request(100);
mSubscription.request(30);
Log.i(TAG, "onSubscribe: ");
}
@Override
public void onNext(Integer integer) {
Log.i(TAG, "onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Log.i(TAG, "onError: " + t);
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
};
flowable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);
I/RxJavaTestHaHa: onSubscribe:
I/RxJavaTestHaHa: subscribe: 128
I/RxJavaTestHaHa: subscribe: 127
I/RxJavaTestHaHa: subscribe: 126
I/RxJavaTestHaHa: subscribe: 125
I/RxJavaTestHaHa: onNext: 3
I/RxJavaTestHaHa: onNext: 2
I/RxJavaTestHaHa: onNext: 1
I/RxJavaTestHaHa: onComplete:
从log中可以看出这个值变成了128,为什么不是130?
当上下游工作在不同的线程里时,每一个线程里都有一个requested,而我们调用request(130)时,实际上改变的是下游主线程中的requested,而上游中的requested的值是由RxJava内部调用request(n)去设置的,这个调用会在合适的时候自动触发。
简单来说就是在异步中,下游无法设置上游的requested,得靠系统自己选择一个默认值。但是在同步中,因为在同一个线程中,下游可以设置上游的requested。
private void test30(){
Flowable<Integer> flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
boolean sign;
for (int i = 0; i<181; i++) {
sign = false;
while (emitter.requested() == 0) {
if (!sign) {
Log.i(TAG, "subscribe: I can't give you anymore");
}
sign = true;
}
Log.i(TAG, "subscribe: " + i + " " + emitter.requested());
emitter.onNext(i);
}
}
}, BackpressureStrategy.ERROR);
Subscriber<Integer> subscriber = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
mSubscription = s;
Log.i(TAG, "onSubscribe: ");
}
@Override
public void onNext(Integer integer) {
Log.i(TAG, "onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Log.i(TAG, "onError: " + t);
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
};
flowable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);
}
public void requestClick(View view) {
//点击一次按钮 请求一次数据
mSubscription.request(90);
}
这个的Log太长了,我就不粘了,但是如果大家仔细看到话,会发现一个奇怪的数字96。对,不是128,是96。128我们能理解,是Flowable缓存区水缸的大小,那96是什么数字?96也是默认的数字,前面不是说过在异步中,下游无法给上游设置requested,上游的requested刚开始默认是128,那下次请求时requested会是多少,是96。如果你观察Log仔细的话会发现,点击一次按钮,只调用了下游的onNext方法,因为上游存储的128个数据满足上游的使用。然后再调用一次的话,剩下的数据不够了,才会改变requested,而这个值改为了96。
下面的这个例子更能说明我的意思
Flowable<Integer> flowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
boolean sign;
for (int i = 0; i<1801; i++) {
sign = false;
while (emitter.requested() == 0) {
if (!sign) {
Log.i(TAG, "subscribe: I can't give you anymore");
}
sign = true;
}
Log.i(TAG, "subscribe: " + i + " " + emitter.requested());
emitter.onNext(i);
}
}
}, BackpressureStrategy.ERROR);
Subscriber<Integer> subscriber = new Subscriber<Integer>() {
@Override
public void onSubscribe(Subscription s) {
mSubscription = s;
Log.i(TAG, "onSubscribe: ");
}
@Override
public void onNext(Integer integer) {
Log.i(TAG, "onNext: " + integer);
}
@Override
public void onError(Throwable t) {
Log.i(TAG, "onError: " + t);
}
@Override
public void onComplete() {
Log.i(TAG, "onComplete: ");
}
};
flowable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(subscriber);
}
public void requestClick(View view) {
//点击一次按钮 请求一次数据
mSubscription.request(129);
}