Rxjava的背压策略

同步订阅和异步订阅

1. 同步订阅

  • 定义:被观察者与观察者处于同一个线程里面。
  • 被观察者发送事件的特点:被观察者每发送一个事件,必须等到观察者接收并处理后,才能继续发送下一个事件。

2. 异步订阅

  • 定义:被观察者和观察者处于不同的线程中。
  • 被观察者发送事件的特点:被观察者不用等到观察者接收或者处理事件,而是不断发送,直到事件发送完毕。但此时的事件并不会直接发送给观察者,而是存在于缓存区,等待观察者从中取出事件。

异步订阅存在的问题(为什么引入背压策略)

在异步订阅时(比如网络请求),如果被观察者发送事件的速度太快,观察者来不及接收所有的事件,从而缓存区中的事件越积越多,最终就会导致缓存区溢出,时间丢失并OOM(Out Of Memory)。

比如:

Observable.create(new ObservableOnSubscribe<Integer>() {
            @Override
            public void subscribe(ObservableEmitter<Integer> e) throws Exception {
                int i = 0;
                while (true) {
                    i++;
                    e.onNext(i);
                }
            }
        }).subscribeOn(Schedulers.newThread()).observeOn(Schedulers.newThread()).subscribe(new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) throws Exception {
                Thread.sleep(5000);
                System.out.println(integer);
            }
        });

背压策略的概述

1. 背压的定义

  • Backpressure,也称为Reactive Pull,就是下游需要多少(具体是通过下游的request请求指定需要多少),上游就发送多少。

2. 背压的作用

  • 在异步场景中,被观察者发送事件的速度远快于观察者处理的速度的情况下,一种告诉上游的被观察者降低发送速度的策略。

3. 背压策略的原理

  • 对于观察者:响应式拉取,即观察者根据自己的实际需求接收事件。
  • 对于被观察者:反馈控制,即被观察者根据观察者的接收能力,从而控制事件发送的速度。
  • 对于缓存区:对超出缓存区大小的事件进行丢弃,保留,报错。

背压的实现Flowable

1. Flowable的介绍

在Rxjava2.0中,Flowable是被观察者(Observable)的一种新实现,但和Observable不同之处,在于Flowable实现了非阻塞式背压策略。

2. Flowable的特点

  1. 对应的观察者变成Subscriber
  2. 所有操作符强制支持背压
  3. 默认的缓存区大小为:128
  4. 缓存区使用队列存放事件

3. Flowable与Observable的区别:

  • 具体如下图

img

示意图

  • 那么,为什么要采用新实现Flowable实现背压,而不采用旧的Observable呢?
  • 主要原因:旧实现Observable无法很好解决背压问题。

img

3. Flowable的基本使用

Flowable.create(new FlowableOnSubscribe<Integer>() {
                            @Override
                            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                                Log.d(TAG, "发送事件 1");
                                emitter.onNext(1);
                                Log.d(TAG, "发送事件 2");
                                emitter.onNext(2);
                                Log.d(TAG, "发送事件 3");
                                emitter.onNext(3);
                                Log.d(TAG, "发送完成");
                                emitter.onComplete();
                            }
                        },
                BackpressureStrategy.ERROR

        ).subscribe(new Subscriber<Integer>() {
            @Override
            public void onSubscribe(Subscription s) {
                Log.d(TAG, "onSubscribe");
                s.request(3);
            }

            @Override
            public void onNext(Integer integer) {
                Log.d(TAG, "接收到了事件" + integer);
            }

            @Override
            public void onError(Throwable t) {
                Log.w(TAG, "onError: ", t);
            }

            @Override
            public void onComplete() {
                Log.d(TAG, "onComplete");
            }
        });

在这里插入图片描述

背压的使用

1. 控制观察者接受事件的速度

异步订阅情况

  • 简介

img

  • 具体原理图

img

  • 具体使用:
/ 1. 创建被观察者Flowable
        Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                // 一共发送4个事件
                Log.d(TAG, "发送事件 1");
                emitter.onNext(1);
                Log.d(TAG, "发送事件 2");
                emitter.onNext(2);
                Log.d(TAG, "发送事件 3");
                emitter.onNext(3);
                Log.d(TAG, "发送事件 4");
                emitter.onNext(4);
                Log.d(TAG, "发送完成");
                emitter.onComplete();
            }
        }, BackpressureStrategy.ERROR).subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
                .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        s.request(3);
                        // 作用:决定观察者能够接收多少个事件
                        // 如设置了s.request(3),这就说明观察者能够接收3个事件(多出的事件存放在缓存区)
                        // 官方默认推荐使用Long.MAX_VALUE,即s.request(Long.MAX_VALUE);
                    }

                    @Override
                    public void onNext(Integer integer) {
                        Log.d(TAG, "接收到了事件" + integer);
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "onComplete");
                    }
                });
  • 有两个注意的点:

img

代码演示:观察者不接收事件的情况下,被观察者继续发送事件 & 存放到缓存区;再按需求取出:

Button button = findViewById(R.id.bt);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSubscription.request(2);
            }
        });
        Flowable.create(new FlowableOnSubscribe<Integer>() {
                            @Override
                            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                                Log.d(TAG, "发送事件 1");
                                emitter.onNext(1);
                                Log.d(TAG, "发送事件 2");
                                emitter.onNext(2);
                                Log.d(TAG, "发送事件 3");
                                emitter.onNext(3);
                                Log.d(TAG, "发送事件 4");
                                emitter.onNext(4);
                                Log.d(TAG, "发送完成");
                                emitter.onComplete();
                            }
                        },
                BackpressureStrategy.ERROR
        ).subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
                .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                    }
                    @Override
                    public void onNext(Integer integer) {
                        Log.d(TAG, "接收到了事件" + integer);
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "onComplete");
                    }
                });

img

代码演示2:观察者不接收事件的情况下,被观察者继续发送事件至超出缓存区大小:

Button button = findViewById(R.id.bt);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSubscription.request(2);
            }
        });
        Flowable.create(new FlowableOnSubscribe<Integer>() {
                            @Override
                            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                                for (int i = 0;i< 129; i++) {
                                    Log.d(TAG, "发送了事件" + i);
                                    emitter.onNext(i);
                                }
                                emitter.onComplete();
                            }
                        },
                BackpressureStrategy.ERROR
        ).subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
                .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        // 默认不设置可接收事件大小
                    }
                    @Override
                    public void onNext(Integer integer) {
                        Log.d(TAG, "接收到了事件" + integer);
                    }
                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }
                    @Override
                    public void onComplete() {
                        Log.d(TAG, "onComplete");
                    }
                });

img

同步订阅情况

同步订阅 & 异步订阅 的区别在于:

  • 同步订阅中,被观察者 & 观察者工作于同一线程
  • 同步订阅关系中没有缓存区

同步订阅与异步订阅的示意图:

img

  • 也就是说,被观察者在发送一个事件之后,必须等到观察者接收后,才能发送下一个事件。换言之就是不会出现发送事件的速度>接收事件的速度,但是会出现发送事件的数目>接收事件的数目这种情况,这种情况依旧会报错。

代码示例:正常操作

Flowable.create(new FlowableOnSubscribe<Integer>() {
                            @Override
                            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                                for (int i = 0; i < 3; i++) {
                                    Log.d(TAG, "发送了事件" + i);
                                    emitter.onNext(i);
                                }
                                emitter.onComplete();
                            }
                        },
                BackpressureStrategy.ERROR
        ).subscribe(new Subscriber<Integer>() {
            @Override
            public void onSubscribe(Subscription s) {
                Log.d(TAG, "onSubscribe");
                // 默认不设置可接收事件大小
                s.request(3);
            }
            @Override
            public void onNext(Integer integer) {
                Log.d(TAG, "接收到了事件" + integer);
            }
            @Override
            public void onError(Throwable t) {
                Log.w(TAG, "onError: ", t);
            }

            @Override
            public void onComplete() {
                Log.d(TAG, "onComplete");
            }
        });

在这里插入图片描述

代码示例:发送事件大于接收事件

Flowable.create(new FlowableOnSubscribe<Integer>() {
                            @Override
                            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                                for (int i = 0; i < 4; i++) {
                                    Log.d(TAG, "发送了事件" + i);
                                    emitter.onNext(i);
                                }
                                emitter.onComplete();
                            }
                        },
                BackpressureStrategy.ERROR
        ).subscribe(new Subscriber<Integer>() {
            @Override
            public void onSubscribe(Subscription s) {
                Log.d(TAG, "onSubscribe");
                // 默认不设置可接收事件大小
                s.request(3);
            }
            @Override
            public void onNext(Integer integer) {
                Log.d(TAG, "接收到了事件" + integer);
            }
            @Override
            public void onError(Throwable t) {
                Log.w(TAG, "onError: ", t);
            }
            @Override
            public void onComplete() {
                Log.d(TAG, "onComplete");
            }
        });

在这里插入图片描述

  • 注意:

img

2. 控制被观察者发送事件的速度

  • 简介:

img

也就是说通过被观察者的FolwableEmitter类的requested()方法来控制发送事件的流速。

  • FlowableEmitter类的requested()介绍
public interface FlowableEmitter<T> extends Emitter<T> {
// FlowableEmitter = 1个接口,继承自Emitter
// Emitter接口方法包括:onNext(),onComplete() & onError

    
    long requested();
    // 作用:返回当前线程中request(a)中的a值
    // 该request(a)则是措施1中讲解的方法,作用  = 设置

}

原理图:

img

为了便于理解,我们先列举同步订阅情况的例子:

同步订阅情况:

即在同步订阅情况中,被观察者 通过 FlowableEmitter.requested()获得了观察者自身接收事件能力,从而根据该信息控制事件发送速度,从而达到了观察者反向控制被观察者的效果

具体使用
下面的例子 = 被观察者根据观察者自身接收事件能力(3个事件),从而仅发送3个事件

Flowable.create(new FlowableOnSubscribe<Integer>() {
                            @Override
                            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                                // 调用emitter.requested()获取当前观察者需要接收的事件数量
                                long n = emitter.requested();

                                Log.d(TAG, "subscribe: " + n);

                                for (int i = 0; i < n; i++) {
                                    Log.d(TAG, "发送了事件" + i);
                                    emitter.onNext(i);
                                }
                                emitter.onComplete();
                            }
                        },
                BackpressureStrategy.ERROR
        ).subscribe(new Subscriber<Integer>() {
            @Override
            public void onSubscribe(Subscription s) {
                Log.d(TAG, "onSubscribe");
                // 默认不设置可接收事件大小
                s.request(3);
            }
            @Override
            public void onNext(Integer integer) {
                Log.d(TAG, "接收到了事件" + integer);
            }
            @Override
            public void onError(Throwable t) {
                Log.w(TAG, "onError: ", t);
            }
            @Override
            public void onComplete() {
                Log.d(TAG, "onComplete");
            }
        });

  • 特别注意:在同步订阅情况中使用FlowableEmitter.request()方法时,有以下几个使用特性需要注意:
  1. 可叠加性:观察者可连续要求接收事件,被观察者会进行叠加并一起发送。
  2. 实时更新性:每次发送事件后,FlowableEmitter.request()的返回值会实时更新观察者能接收的事件。
  3. 异常:
    • 当FlowableEmitter.request()的返回值=0时,代表观察者已经不可能接收事件。此时若被观察者继续发送事件,则会抛出MissingBackpressureException异常。
    • 若观察者没有设置可接收事件数量,则无调用Subscription.request()。那么被关这这默认观察者可接收事件数量=0,即FlowableEmitter.request()的返回值=0;
    • 同步订阅关系中不存在缓存区。

异步订阅情况

从最开始的原理可以看出,由于二者处于不同线程,所以被观察者 无法通过 FlowableEmitter.requested()知道观察者自身接收事件能力,即 被观察者不能根据 观察者自身接收事件的能力 控制发送事件的速度。具体请看下面例子:

 Flowable.create(new FlowableOnSubscribe<Integer>() {
                            @Override
                            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                                // 调用emitter.requested()获取当前观察者需要接收的事件数量
                                Log.d(TAG, "观察者可接收事件数量 = " + emitter.requested());
                            }
                        },
                BackpressureStrategy.ERROR

        ).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        // 默认不设置可接收事件大小
                        s.request(150);
                }

                    @Override
                    public void onNext(Integer integer) {
                        Log.d(TAG, "接收到了事件" + integer);
                    }
                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }
                    @Override
                    public void onComplete() {
                        Log.d(TAG, "onComplete");
                    }
                });

在这里插入图片描述

从上面可以看出,在异步订阅的关系中,反向控制的原理是:通过Rxjava内部固定调用被观察者线程中的request(n)从而反向控制被观察者的发送事件速度。

  • 具体使用

关于RxJava内部调用request(n)(n = 128、96、0)的逻辑如下:

img

至于为什么是调用request(128) & request(96) & request(0),感兴趣的读者可自己阅读 Flowable的源码

代码演示:

Button button = findViewById(R.id.bt);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mSubscription.request(48);
            }
        });
        Flowable.create(new FlowableOnSubscribe<Integer>() {
                            @Override
                            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                                // 调用emitter.requested()获取当前观察者需要接收的事件数量
                                Log.d(TAG, "观察者可接收事件数量 = " + emitter.requested());
                                boolean flag; //设置标记位控制

                                // 被观察者一共需要发送500个事件
                                for (int i = 0; i < 500; i++) {
                                    flag = false;
                                    // 若requested() == 0则不发送
                                    while (emitter.requested() == 0) {
                                        if (!flag) {
                                            Log.d(TAG, "不再发送");
                                            flag = true;
                                        }
                                    }

                                    // requested() ≠ 0 才发送
                                    Log.d(TAG, "发送了事件" + i + ",观察者可接收事件数量 = " + emitter.requested());
                                    emitter.onNext(i);
                                }
                            }
                        },
                BackpressureStrategy.ERROR

        ).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                    }

                    @Override
                    public void onNext(Integer integer) {
                        Log.d(TAG, "接收到了事件" + integer);
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "onComplete");
                    }
                });

在这里插入图片描述

点击两次按钮后:

在这里插入图片描述

整个流程 & 测试结果 请看下图:

img

采用背压模式策略:BackpressureStrategy

背压模式介绍:

在Flowable的使用中,会被要求传入背压模式参数

背压模式类型:

关于背压模式需要解决的问题:

  • 流速不匹配,即发送事件速度>接收事件速度
  • 当缓存区大小存满,被观察者继续发送下一个事件时

背压策略模式解决方案:

  • BackpressureStrategy.ERROR:直接抛出异常MissingBackpressureException
  • BackpressureStratery.MISSING:友好提示:缓存区满了
  • BackpressureStratery.BUFFER:将缓存区大小设置成无限大,即被观察者可无限发送事件,实际上是放在缓存区。
  • BackpressureStratery.DROP:超过缓存区大小(128)的事件丢弃
  • BackpressureStratery.LATEST:只保存最新的最后的事件,超过缓存区大小(128)的事件丢弃。

下面我会对每一个模式都逐一说明:

模式1:BackpressureStratery.ERROR

处理方式:直接抛出异常MissingBackpressureException

// 创建被观察者Flowable
        Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {

                // 发送 129个事件
                for (int i = 0;i< 129; i++) {
                    Log.d(TAG, "发送了事件" + i);
                    emitter.onNext(i);
                }
                emitter.onComplete();
            }
        }, BackpressureStrategy.ERROR) // 设置背压模式 = BackpressureStrategy.ERROR
                .subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
                .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                    }

                    @Override
                    public void onNext(Integer integer) {
                        Log.d(TAG, "接收到了事件" + integer);
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "onComplete");
                    }
                });

在这里插入图片描述

模式2:BackpressureStrategy.MISSING

处理方式:友好提示缓存区满了

在这里插入图片描述

模式3:BackpressureStrategy.BUFFER

处理方式:将缓存区大小设置成无限大

在这里插入图片描述

模式4: BackpressureStrategy.DROP

处理方式:超过缓存区大小(128)的事件丢弃,发送了150个事件,仅保存第1 - 第128个事件,第129 -第150事件将被丢弃

Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                // 发送150个事件
                for (int i = 0;i< 150; i++) {
                    Log.d(TAG, "发送了事件" + i);
                    emitter.onNext(i);
                }
                emitter.onComplete();
            }
        }, BackpressureStrategy.DROP)      // 设置背压模式 = BackpressureStrategy.DROP
                .subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
                .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                        // 通过按钮进行接收事件
                    }

                    @Override
                    public void onNext(Integer integer) {
                        Log.d(TAG, "接收到了事件" + integer);
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "onComplete");
                    }
                });


btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mSubscription.request(128);
                // 每次接收128个事件
            }

        });

img

模式5:BackpressureStrategy.LATEST

处理方式:只保存最新(最后)事件,超过缓存区大小(128)的事件丢弃,如果发送了150个事件,缓存区里会保存129个事件(第1-第128 + 第150事件)。

Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
                for (int i = 0;i< 150; i++) {
                    Log.d(TAG, "发送了事件" + i);
                    emitter.onNext(i);
                }
                emitter.onComplete();
            }
        }, BackpressureStrategy.LATEST) // // 设置背压模式 = BackpressureStrategy.LATEST
                 .subscribeOn(Schedulers.io()) // 设置被观察者在io线程中进行
                .observeOn(AndroidSchedulers.mainThread()) // 设置观察者在主线程中进行
                .subscribe(new Subscriber<Integer>() {
                    @Override
                    public void onSubscribe(Subscription s) {
                        Log.d(TAG, "onSubscribe");
                        mSubscription = s;
                        // 通过按钮进行接收事件
                    }

                    @Override
                    public void onNext(Integer integer) {
                        Log.d(TAG, "接收到了事件" + integer);
                    }

                    @Override
                    public void onError(Throwable t) {
                        Log.w(TAG, "onError: ", t);
                    }

                    @Override
                    public void onComplete() {
                        Log.d(TAG, "onComplete");
                    }
                });

btn = (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mSubscription.request(128);
                // 每次接收128个事件
            }

        });

img

小结:

img

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值