RxJava2.0│背压

868 阅读36分钟
原文链接: mp.weixin.qq.com

☝点击上方蓝字,关注我们!

 RxJava2的背压

背压的观察者模式:Flowable和Subscriber。默认队列大小为128,所有的操作符强制支持背压。

背压模式的流程同Observable一样,也是分成对象创建、逆向订阅和任务执行这三部分。

我们假设一种场景:上游发送无限多的数据,要求下游处理无限多的数据,并保证数据不丢失,同时避免OOM。

为了实现以上需求,我们先了解Flowable的五种处理策略。

1. 五种策略:

策略

描述

应用场景

MISSING

没有指定背压策略,需要下游通过背压操作符(onBackpressureBuffer()/onBackpressureDrop()/onBackpressureLatest())指定背压策略。

ERROR

如果放入Flowable的异步缓存池中的数据超限了,则会抛出MissingBackpressureException异常。

对数据丢失零容忍,宁愿抛异常也要尽可能处理所有已发送的数据。

BUFFER

Flowable的异步缓存池没有固定大小,可以无限制添加数据,不会抛出MissingBackpressureException异常,但有可能导致OOM。

对数据丢失零容忍,确定上游发送数据速度大于下游处理速度。

DROP

如果Flowable的异步缓存池满了,则会丢掉将要放入缓存池中的数据。

不在乎中间数据丢失和最后发送的数据值,多用于耗时任务进度更新和完成提示。

LATEST

如果Flowable的异步缓存池满了,则会丢掉将要放入缓存池中的数据,但会将最后一条数据强行放入缓存池中。

不在乎中间数据丢失,多用于耗时任务进度更新并给出最后发射数据的处理结果。

我们做个实验,每隔100毫秒由上游发送一条数据,每隔300毫秒由下游处理一条数据,总共发送500条数据,由此看一下各个策略下的表现:

1public void flowableTest(final String tag, BackpressureStrategy strategy) { 2    Flowable 3            .create(new FlowableOnSubscribe<Integer>() { 4                @Override 5                public void subscribe(FlowableEmitter<Integer> e) throws Exception { 6                    String threadName = Thread.currentThread().getName(); 7                    Log.d(tag, threadName + "开始发射数据" + System.currentTimeMillis()); 8                    for (int i = 1; i <= 500; i++) { 9                        Log.d(tag, threadName + "发射---->" + i);10                        e.onNext(i);11                        try {12                            Thread.sleep(100);//每隔100毫秒发射一次数据13                        } catch (Exception ex) {14                            e.onError(ex);15                        }16                    }17                    Log.d(tag, threadName + "发射数据结束" + System.currentTimeMillis());18                    e.onComplete();19                }20            }, strategy)21            .subscribeOn(Schedulers.newThread())22            .observeOn(Schedulers.newThread())23            .subscribe(new Subscriber<Integer>() {24                @Override25                public void onSubscribe(Subscription s) {26                    s.request(Long.MAX_VALUE);            //注意此处,暂时先这么设置27                }2829                @Override30                public void onNext(Integer integer) {31                    Log.d(tag, Thread.currentThread().getName() + "接收---------->" + integer);32                    try {33                        Thread.sleep(300);//每隔300毫秒接收一次数据34                    } catch (InterruptedException ignore) {35                    }36                }3738                @Override39                public void onError(Throwable t) {40                    t.printStackTrace();41                }4243                @Override44                public void onComplete() {45                    Log.d(tag, Thread.currentThread().getName() + "接收----> 完成");46                }47            });48}

在起始阶段,所有的策略都是一致的,每隔100ms发送一条数据,每隔300ms接收一条数据。

1.1 MISSING

在MISSING策略下,且不指定下游的背压操作符,我们会发现,下游每接收一条数据,上游的缓存池就会清一条数据,当上游发射到第196条数据时,缓存池就溢出了(196-67=129),这时rx会抛出io.reactivex.exceptions.UndeliverableException的异常,程序终止。

1.2 ERROR

在ERROR策略下,我们发现上游在发送第129个数据时停止运行,而下游已经处理了45个。疑问来了,既然下游已经处理了45个数据,为什么第129个数据没有放进缓存池呢?这是因为在ERROR策略下,缓存池并不是接收一条,清理一条,而是累积一段时间后再清理,所以即使下游已经处理了45条数据,但缓存池依然是满的,抛出了MissingBackpressureException异常。

1.3 BUFFER

BUFFER策略下,我们发现,所有数据都顺序的发送/接收成功了,但是这并不代表可以随意使用BUFFER策略。BUFFER的缓存池没有固定大小,不会抛出MissingBackpressureException异常,但有可能导致OOM,慎用。

1.4 DROP

DROP策略下,我们会发现下游接收了128条数据后,再次接收数据便是第284条了,原因同ERROR策略:下游处理数据后,缓存池并不会及时清理,而是等到接收到第97条数据才进行的清理,之后上游发送的数据才得以进入到缓存池,而最终接收完成时,处理的最后一条数据是379。

1.5 LATEST

LATEST流程大致与DROP相同,不同的是,不管缓存池的状态如何,都会将最后一条数据强行插入到缓存池中(总数还是128),来保证下游能够收到最新发送的一条数据,所以最终下游可以处理第500条数据。

到此我们了解了Flowable针对背压的各种策略,由此可见,这五种策略并不能满足我们的需求,要想合理的使用Flowable,还需了解Subscription和FlowableEmitter。

2. Subscription:

Subscription与Disposable均是观察者与被观察对象建立订阅关系后回调回来的参数,如同通过Disposable的dispose()方法可以取消Observer与Oberverable的订阅关系一样,通过Subscription的cancel()方法也可以取消Subscriber与Flowable的订阅关系,

不同的是接口Subscription中多了一个方法request(long n)。

运行如下代码:

1public void flowableTest1() { 2    Flowable 3            .create(new FlowableOnSubscribe<Integer>() { 4                @Override 5                public void subscribe(FlowableEmitter<Integer> e) throws Exception { 6                    System.out.println("发射----> 1"); 7                    e.onNext(1); 8                    System.out.println("发射----> 2"); 9                    e.onNext(2);10                    System.out.println("发射----> 3");11                    e.onNext(3);12                    System.out.println("发射----> 完成");13                    e.onComplete();14                }15            }, BackpressureStrategy.BUFFER)16            .subscribeOn(Schedulers.newThread())17            .observeOn(Schedulers.newThread())18            .subscribe(new Subscriber<Integer>() {19                @Override20                public void onSubscribe(Subscription s) {21                    //去掉代码s.request(Long.MAX_VALUE);22                }2324                @Override25                public void onNext(Integer integer) {26                    System.out.println("接收----> " + integer);27                }2829                @Override30                public void onError(Throwable t) {31                }3233                @Override34                public void onComplete() {35                    System.out.println("接收----> 完成");36                }37            });38}

运行结果如下:

我们发现Flowable照常发送数据,而Subsriber不再接收数据,因为Flowable在设计的时候,采用了一种新的思路——响应式拉取方式,来设置下游对数据的请求数量,上游可以根据下游的需求量,按需发送数据。如果不显示调用request则默认下游的需求量为零,所以运行上面的代码后,上游Flowable发射的数据不会交给下游Subscriber处理。

运行如下代码:

1public void flowableTest2() { 2    Flowable 3            .create(new FlowableOnSubscribe<Integer>() { 4                @Override 5                public void subscribe(FlowableEmitter<Integer> e) throws Exception { 6                    System.out.println("发射----> 1"); 7                    e.onNext(1); 8                    System.out.println("发射----> 2"); 9                    e.onNext(2);10                    System.out.println("发射----> 3");11                    e.onNext(3);12                    System.out.println("发射----> 完成");13                    e.onComplete();14                }15            }, BackpressureStrategy.BUFFER) 16            .subscribeOn(Schedulers.newThread())17            .observeOn(Schedulers.newThread())18            .subscribe(new Subscriber<Integer>() {19                @Override20                public void onSubscribe(Subscription s) {21                    s.request(2);//设置Subscriber的消费能力为222                }2324                @Override25                public void onNext(Integer integer) {26                    System.out.println("接收----> " + integer);27                }2829                @Override30                public void onError(Throwable t) {31                }3233                @Override34                public void onComplete() {35                    System.out.println("接收----> 完成");36                }37            });38}

运行结果如下:

我们发现通过s.request(2)设置Subscriber的数据请求量为2条,超出其请求范围之外的数据则没有接收。

看到这儿疑问又来了,看上去s.request(2)只是设置了下游数据接收的限制,并不像上面提到的“响应式拉取方式”。的确,在此情况下,无论下游request()如何设置,上游依旧发射数据到缓存池,在超出阈值时,会根据策略进行处理,相关代码实例就不在此贴上了,所以虽然通过request()可以设置下游的请求数量,但是上游并没有获取到这个数量,如何获取呢?这便需要用到Flowable特有的发射器FlowableEmitter:

3. FlowableEmitter:

我们先看一下FlowableEmitter和ObservableEmitter的区别:

1public interface FlowableEmitter<T> extends Emitter<T> { 2 3    void setDisposable(@Nullable Disposable s); 4 5    void setCancellable(@Nullable Cancellable c); 6 7    long requested(); 8 9    boolean isCancelled();1011    @NonNull12    FlowableEmitter<T> serialize();1314    @Experimental15    boolean tryOnError(@NonNull Throwable t);16}

1public interface ObservableEmitter<T> extends Emitter<T> { 2 3    void setDisposable(@Nullable Disposable d); 4 5    void setCancellable(@Nullable Cancellable c); 6 7    boolean isDisposed(); 8 9    @NonNull10    ObservableEmitter<T> serialize();1112    @Experimental13    boolean tryOnError(@NonNull Throwable t);14}

其中FlowableEmitter多了一个long requested()方法,我们可以通过这个方法来获取当前未完成的请求数量。

我们来看下面的代码:

1public void flowableEmitTest(final int emitCount, final int requestCount) { 2    Flowable 3            .create(new FlowableOnSubscribe<Integer>() { 4                @Override 5                public void subscribe(FlowableEmitter<Integer> e) throws Exception { 6                    String threadName = Thread.currentThread().getName(); 7                    for (int i = 1; i <= emitCount; i++) { 8                        Log.d(TAG, "当前未完成的请求数量-->" + e.requested()); 9                        Log.d(TAG, threadName + "发射---->" + i);10                        e.onNext(i);11                    }12                    Log.d(TAG, threadName + "发射数据结束" + System.currentTimeMillis());13                    e.onComplete();14                }15            }, BackpressureStrategy.BUFFER)16            .subscribeOn(Schedulers.newThread())//添加两行代码,为上下游分配独立的线程17            .observeOn(Schedulers.newThread())18            .subscribe(new Subscriber<Integer>() {19                @Override20                public void onSubscribe(Subscription s) {21                    s.request(requestCount);22                }2324                @Override25                public void onNext(Integer integer) {26                    Log.d(TAG, Thread.currentThread().getName() + "接收---------->" + integer);27                }2829                @Override30                public void onError(Throwable t) {31                    t.printStackTrace();32                }3334                @Override35                public void onComplete() {36                    Log.d(TAG, Thread.currentThread().getName() + "接收----> 完成");37                }38            });39}

执行下面的代码:

1flowableEmitTest(5, 3);

如上代码运行时,由于下游request()的值为3,我们会认为,上游requested()的返回值应该依次是3、2、1、0,但实际上并非如此:

虽然我们指定了下游的数据请求量为3,但是我们在上游获取未完成请求数量的时候,并不是3,而是128,难道上游有个最小未完成请求数量?只要下游设置的数据请求量小于128,上游获取到的都是128?

带着这个疑问,我们执行下面的代码:

1flowableEmitTest(5, 500);

结果果真如此:

其实不论下游通过s.request()设置多少请求量,我们在上游获取到的初始未完成请求数量都是128。

这是因为Flowable有一个异步缓存池,上游发射的数据,先放到异步缓存池中,再由异步缓存池交给下游,所以上游在发射数据时,首先需要考虑的不是下游的数据请求量,而是缓存池中能不能放得下,否则在缓存池满的情况下依然会导致数据遗失或者背压异常。如果缓存池可以放得下,那就发送,至于是否超出了下游的数据需求量,可以在缓存池向下游传递数据时,再作判断,如果未超出,则将缓存池中的数据传递给下游,如果超出了,则不传递。

如果下游对数据的需求量超过缓存池的大小,而上游能获取到的最大需求量是128,上游对超出128的需求量是怎么获取到的呢?

 

带着这个疑问,我们执行如下代码:

1flowableEmitTest(150, 150);

结果如下:

在此我们发现,在发射了第96个数据到缓存池中后, requested()的返回值为32,然后上游发射97,下游接收96,97。再次requested(),返回值就变为了127。这是因为,缓存池中的数据并不是下游接收一条便清理一条,而是等待时机统一清理,但为什么缓存池清理后,requested()的返回值不是128呢?原因是在处理了96之后,会对缓存池已处理过的数据进行清理,此时97还没有被下游处理,所以缓存池的空闲大小为127。

4. Flowable的最终方案:

我们回到最初的需求:上游发送无限多的数据,要求下游处理无限多的数据,并保证数据不丢失,同时避免OOM。

最终,使用Flowable的正确操作如下:

1public void flowableSolution() { 2    Flowable 3            .create(new FlowableOnSubscribe<Integer>() { 4                @Override 5                public void subscribe(FlowableEmitter<Integer> e) throws Exception { 6                    int i = 0; 7                    while (true) { 8                        Log.d(TAG, "当前未完成的请求数量-->" + e.requested()); 9                        if (e.requested() == 0) continue;//此处添加代码,让flowable按需发送10                        Log.d(TAG, "发射---->" + i);11                        i++;12                        e.onNext(i);13                    }14                }15            }, BackpressureStrategy.MISSING)16            .subscribeOn(Schedulers.newThread())17            .observeOn(Schedulers.newThread())18            .subscribe(new Subscriber<Integer>() {19                private Subscription mSubscription;2021                @Override22                public void onSubscribe(Subscription s) {23                    s.request(1);            //设置初始请求数据量为124                    mSubscription = s;25                }2627                @Override28                public void onNext(Integer integer) {29                    try {30                        Thread.sleep(50);31                        Log.d(TAG, "接收------>" + integer);32                        mSubscription.request(1);//每接收到一条数据增加一条请求量33                    } catch (InterruptedException ignore) {34                    }35                }3637                @Override38                public void onError(Throwable t) {39                }4041                @Override42                public void onComplete() {43                }44            });45}

我们在下游onNext(Integer integer) 方法中,每接收一条数据增加一条请求量:

1mSubscription.request(1);

在上游添加代码:

1if (e.requested() == 0) continue;//此处添加代码,让flowable按需发送数据

这样,上游严格按照下游的需求量发送数据,不会产生MissingBackpressureException异常,或者丢失数据。同时不会产生OOM。

5. 源码解析

看完Flowable的使用方式,我们有了一些疑问:首先,这个默认的128缓存池是怎么来的?另外,在Flowable的一些策略中都提到过下游接收数据后,缓存池不会立即清理,而是到处理了第96个数据时清理,这又是怎么回事儿呢?所谓的响应式拉取又是如何实现的呢?带着这几个问题,我们看一下Flowable这五种策略的相关源码:

5.1 对象创建

以上面代码为例,对象创建这个阶段同Observable类似,但有两处不同:

  1. 在create时多了一个记录背压策略的变量,这块我们直接跳过;

  2. observeOn的Flowable初始化阶段会传入bufferSize,这部分我们看一下源码:

observeOn():

1@CheckReturnValue2@BackpressureSupport(BackpressureKind.FULL)3@SchedulerSupport(SchedulerSupport.CUSTOM)4public final Flowable<T> observeOn(Scheduler scheduler) {5    return observeOn(scheduler, false, bufferSize());6}

这里有个bufferSize(),就是我们想要的缓存池大小:

1public static int bufferSize() {2    return BUFFER_SIZE;3}45static final int BUFFER_SIZE;6static {7    BUFFER_SIZE = Math.max(1, Integer.getInteger("rx2.buffer-size", 128));8}

我们继续看observeOn方法:

1@CheckReturnValue2@BackpressureSupport(BackpressureKind.FULL)3@SchedulerSupport(SchedulerSupport.CUSTOM)4public final Flowable<T> observeOn(Scheduler scheduler, boolean delayError, int bufferSize) {5    ObjectHelper.requireNonNull(scheduler, "scheduler is null");6    ObjectHelper.verifyPositive(bufferSize, "bufferSize");7    return RxJavaPlugins.onAssembly(new FlowableObserveOn<T>(this, scheduler, delayError, bufferSize));8}

1public FlowableObserveOn( 2        Flowable<T> source, 3        Scheduler scheduler, 4        boolean delayError, 5        int prefetch) { 6    super(source); 7    this.scheduler = scheduler; 8    this.delayError = delayError; 9    this.prefetch = prefetch;10}

在FlowableObserveOn的构造方法里,这个prefetch变量就是传入的bufferSize。

5.2 逆向订阅

在逆向订阅的阶段,从下至上,我们首先还是先关注一下observeOn()的订阅过程,即FlowableObserveOn的subscribeActual()方法:

1@Override 2public void subscribeActual(Subscriber<? super T> s) { 3    Worker worker = scheduler.createWorker(); 4 5    if (s instanceof ConditionalSubscriber) { 6        source.subscribe(new ObserveOnConditionalSubscriber<T>( 7                (ConditionalSubscriber<? super T>) s, worker, delayError, prefetch)); 8    } else { 9        source.subscribe(new ObserveOnSubscriber<T>(s, worker, delayError, prefetch));10    }11}

跳过ConditionalSubscriber,我们直接看ObserveOnSubscriber:

1ObserveOnSubscriber(2        Subscriber<? super T> actual,3        Worker worker,4        boolean delayError,5        int prefetch) {6    super(worker, delayError, prefetch);7    this.actual = actual;8}

这里,prefetch即是bufferSize,我们继续看super方法:

1BaseObserveOnSubscriber( 2        Worker worker, 3        boolean delayError, 4        int prefetch) { 5    this.worker = worker; 6    this.delayError = delayError; 7    this.prefetch = prefetch; 8    this.requested = new AtomicLong(); 9    this.limit = prefetch - (prefetch >> 2);10}

我们看到这里有个limit变量,这个limit就是下游接收数据后,缓存池清理时的那个阈值,默认情况下,这个limit就是96。这也解释了上面示例代码中,在下游处理完第96个数据,接收到第97个数据时,上游的缓存池会被清理。

在create的订阅过程,我们可以看到不同策略的初始化过程,所有的Emitter都继承自BaseEmitter,BaseEmitte继承自AtomicLong,因为背压问题会涉及到不平衡,为了解决这种问题,通常会设置一个缓冲队列,缓冲队列不可能无限大吧?应该是有限制的。

1@Override 2public void subscribeActual(Subscriber<? super T> t) { 3    BaseEmitter<T> emitter; 4 5    switch (backpressure) { 6    case MISSING: { 7        emitter = new MissingEmitter<T>(t); 8        break; 9    }10    case ERROR: {11        emitter = new ErrorAsyncEmitter<T>(t);12        break;13    }14    case DROP: {15        emitter = new DropAsyncEmitter<T>(t);16        break;17    }18    case LATEST: {19        emitter = new LatestAsyncEmitter<T>(t);20        break;21    }22    default: {23        emitter = new BufferAsyncEmitter<T>(t, bufferSize());24        break;25    }26    }2728    t.onSubscribe(emitter);29    try {30        source.subscribe(emitter);31    } catch (Throwable ex) {32        Exceptions.throwIfFatal(ex);33        emitter.onError(ex);34    }35}

在ObserveOnSubscriber订阅之后,会执行以下代码:

1@Override 2public void onSubscribe(Subscription s) { 3    if (SubscriptionHelper.validate(this.s, s)) { 4        this.s = s; 5 6        if (s instanceof QueueSubscription) { 7            @SuppressWarnings("unchecked") 8            QueueSubscription<T> f = (QueueSubscription<T>) s; 910            int m = f.requestFusion(ANY | BOUNDARY);1112            if (m == SYNC) {13                sourceMode = SYNC;14                queue = f;15                done = true;1617                actual.onSubscribe(this);18                return;19            } else20            if (m == ASYNC) {21                sourceMode = ASYNC;22                queue = f;2324                actual.onSubscribe(this);2526                s.request(prefetch);2728                return;29            }30        }3132        queue = new SpscArrayQueue<T>(prefetch);3334        actual.onSubscribe(this);3536        s.request(prefetch);37    }38}

这里,我们看到了一个SpscArrayQueue变量,这是一个固定长度的缓存队列,用来缓存上游发射的数据。s.request()的这个s,实际是Emitter,我们看一下BaseEmitter的request()方法:

1@Override2public final void request(long n) {3    if (SubscriptionHelper.validate(n)) {4        BackpressureHelper.add(this, n);5        onRequested();6    }7}

这里调用了BackPressureHelper的add()方法:

1public static long add(AtomicLong requested, long n) { 2    for (;;) { 3        long r = requested.get(); 4        if (r == Long.MAX_VALUE) { 5            return Long.MAX_VALUE; 6        } 7        long u = addCap(r, n); 8        if (requested.compareAndSet(r, u)) { 9            return r;10        }11    }12}

其实就是更新BaseEmitter的父类AtomicLong的值,也就是缓存池的大小。

5.3 任务执行

我们直接看这段代码的subscribe():

1Flowable 2        .create(new FlowableOnSubscribe<Integer>() { 3            @Override 4            public void subscribe(FlowableEmitter<Integer> e) throws Exception { 5                int i = 0; 6                while (true) { 7                    Log.d(TAG, "当前未完成的请求数量-->" + e.requested()); 8                    if (e.requested() == 0) continue;//此处添加代码,让flowable按需发送数据 9                    Log.d(TAG, "发射---->" + i);10                    i++;11                    e.onNext(i);12                }13            }14        }, BackpressureStrategy.DROP)

在DROP策略下,onNext会走到DropAsyncEmitter的父类,NoOverflowBaseAsyncEmitter的onNext():

1@Override 2public final void onNext(T t) { 3    if (isCancelled()) { 4        return; 5    } 6 7    if (t == null) { 8        onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources.")); 9        return;10    }1112    if (get() != 0) {13        actual.onNext(t);14        BackpressureHelper.produced(this, 1);15    } else {16        onOverflow();17    }18}

跳过前面的检查,走到get()那句判断,如果AtomicLong的值不是0,即缓存池未满,走到Subscriber的onNext之后,回调用BackPressureHelper的produced()方法:

1public static long produced(AtomicLong requested, long n) { 2    for (;;) { 3        long current = requested.get(); 4        if (current == Long.MAX_VALUE) { 5            return Long.MAX_VALUE; 6        } 7        long update = current - n; 8        if (update < 0L) { 9            RxJavaPlugins.onError(new IllegalStateException("More produced than requested: " + update));10            update = 0L;11        }12        if (requested.compareAndSet(current, update)) {13            return update;14        }15    }16}

这个方法很简单,就是缓存池大小减1,重新给AtomicLong对象赋值。如果get()那句判断,AtomicLong的值是0,即缓存池已满,则调用onOverFlow()方法,这个方法是针对不同策略的处理方式,在此不做展开。

 

接下来我们走到observeOn的onNext()方法中去:

1@Override 2public final void onNext(T t) { 3    if (done) { 4        return; 5    } 6    if (sourceMode == ASYNC) { 7        trySchedule(); 8        return; 9    }10    if (!queue.offer(t)) {11        s.cancel();1213        error = new MissingBackpressureException("Queue is full?!");14        done = true;15    }16    trySchedule();17}

trySchedule()其实就是将线程调度交给Worker处理,最终Runnable对象执行run,过程同第3节的线程调度流程:

1final void trySchedule() { 2    if (getAndIncrement() != 0) { 3        return; 4    } 5    worker.schedule(this); 6} 7 8@Override 9public final void run() {10    if (outputFused) {11        runBackfused();12    } else if (sourceMode == SYNC) {13        runSync();14    } else {15        runAsync();16    }17}

这里我们来分析异步的执行代码:

1@Override   2void runAsync() {   3                 …    4    for (;;) {   5        long r = requested.get();   6        while (e != r) {   7            boolean d = done;   8            T v;   9            try {  10                v = q.poll();  11            } catch (Throwable ex) {  12                 …  13                return;  14            }  1516                 …  17            a.onNext(v);  18                 …  19        }  20                 …  21    }  22}  

很多逻辑判断代码我们都跳过,看到v=q.poll()的执行,这个poll()方法被重写了:

1@Nullable 2@Override 3public T poll() throws Exception { 4    T v = queue.poll(); 5    if (v != null && sourceMode != SYNC) { 6        long p = produced + 1; 7        if (p == limit) { 8            produced = 0; 9            s.request(p);10        } else {11            produced = p;12        }13    }14    return v;15}

这里,我们就看到了在从队列里取出值消费后,消费数+1,若此时下游的消费数已经到了缓存池需要清理的那个阈值,便重新向上游request。

到此为止,基本的Flowable的步骤就说完了。


狐友技术团队其他精彩文章

Swift之Codable实战技巧

不了解GIF的加载原理?看我就够了!

安卓系统权限,你真的了解吗?

AspectJ在Android中的应用


加入搜狐技术作者天团

千元稿费等你来!

戳这里!☛