RxJava2系列(四) 背压策略

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替换成了FlowableEmitterDisposable替换成了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的使用就写到这里。后面会学习一下源码。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值