Rxjava2 BackpressureStrategy.BUFFER源码分析

109 阅读2分钟

在开发过程中,可能会遇到生产者生产速度过快,消费者来不及消费的情形。我们就可以用Flowable中的 BackpressureStrategy.BUFFER,其作用会缓存生产者生产的数据,消费者可以会根据负载调用request请求数据,参数是告诉生产者消费者需要的数据量。 看一个例子:

Flowable.create(new FlowableOnSubscribe<Integer>() {
            @Override
            public void subscribe(@NotNull FlowableEmitter<Integer> emitter) throws Exception {
                emitter.onNext(1);
                emitter.onNext(2);
                emitter.onNext(3);
                emitter.onNext(4);
                emitter.onComplete();
            }
        }, BackpressureStrategy.BUFFER)
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .subscribe(new FlowableSubscriber<Integer>() {
                    @Override
                    public void onSubscribe(@NotNull Subscription s) {
                        new Thread(() -> {
                            try {
                                Thread.sleep(1000);
                                s.request(1); //给我一个数据
                                Thread.sleep(1000);
                                s.request(1); //再给我一个数据
                                Thread.sleep(1000);
                                s.request(1); //再再给我一个数据
                                Thread.sleep(1000);
                                s.request(1);//再再再给我一个数据
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }).start();
                    }

                    @Override
                    public void onNext(Integer integer) {
                        System.out.println(integer);
                    }

                    @Override
                    public void onError(Throwable throwable) {
                        System.out.println(throwable.getMessage());
                    }

                    @Override
                    public void onComplete() {
                        System.out.println("onComplete");
                    }
                });

例子的作用是每1s请求一次数据。 看看源码:

 public static <T> Flowable<T> create(FlowableOnSubscribe<T> source, BackpressureStrategy mode) {
        ...
        return RxJavaPlugins.onAssembly(new FlowableCreate<T>(source, mode));
    }

FlowableCreate源码中,根据mode适配emitter,这里我们用的是BackpressureStrategy.BUFFER,用的就是BufferAsyncEmitter

public void subscribeActual(Subscriber<? super T> t) {
        BaseEmitter<T> emitter;

        switch (backpressure) {
        case MISSING: {
            emitter = new MissingEmitter<T>(t);
            break;
        }
        case ERROR: {
            emitter = new ErrorAsyncEmitter<T>(t);
            break;
        }
        case DROP: {
            emitter = new DropAsyncEmitter<T>(t);
            break;
        }
        case LATEST: {
            emitter = new LatestAsyncEmitter<T>(t);
            break;
        }
        default: {
            emitter = new BufferAsyncEmitter<T>(t, bufferSize());
            break;
        }
        }

        t.onSubscribe(emitter);
        try {
            source.subscribe(emitter);
        } catch (Throwable ex) {
            Exceptions.throwIfFatal(ex);
            emitter.onError(ex);
        }
    }

在分析BufferAsyncEmitter的源码前,先看看它的父类BaseEmitter,主要分析request方法

BackpressureHelper的作用是加上或者减去我们需要的数据量n,然后调用onRequested方法。

 public final void request(long n) {
            if (SubscriptionHelper.validate(n)) {
                BackpressureHelper.add(this, n);
                onRequested();
            }
        }

好了我们返回BufferAsyncEmitter

生产者生产数据,然后把数据放到队列queue中,然后调用drain方法把队列中的数据出队,这里就会把数据缓存起来。

  public void onNext(T t) {
            if (done || isCancelled()) {
                return;
            }

            if (t == null) {
                onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
                return;
            }
            queue.offer(t);
            drain();
        }

drain方法的作用就是把队列中的数据弹出来发送给消费者,但不是一次性弹出,是根据消费者负载按需弹出的。

       void drain() {
            if (wip.getAndIncrement() != 0) {
                return;
            }

            int missed = 1;
            final Subscriber<? super T> a = downstream;
            final SpscLinkedArrayQueue<T> q = queue;

            for (;;) {
                long r = get(); //获取消费者需要的数据量
                long e = 0L; //已经消费的数据量,等到e和r相等时,说明数据已经发给消费者了

                while (e != r) {
                    if (isCancelled()) {
                        q.clear();
                        return;
                    }

                    boolean d = done;

                    T o = q.poll();//弹出一个数据

                    boolean empty = o == null;

		   //数据发送完毕就可以等着结束了
                    if (d && empty) {
                        Throwable ex = error;
                        if (ex != null) {
                            error(ex);
                        } else {
                            complete();
                        }
                        return;
                    }

                    if (empty) {
                        break;
                    }

                    a.onNext(o);//交给消费者

                    e++; //消费数量自增一次,等到和r一样时,说明这次数据消费完毕。
                }

                if (e == r) {
                    if (isCancelled()) {
                        q.clear();
                        return;
                    }

                    boolean d = done;

                    boolean empty = q.isEmpty();

                    if (d && empty) {
                        Throwable ex = error;
                        if (ex != null) {
                            error(ex);
                        } else {
                            complete();
                        }
                        return;
                    }
                }

                if (e != 0) {
                    //减去消费的数据量,等着调用request再次增加消费量 
                    BackpressureHelper.produced(this, e);
                }

                missed = wip.addAndGet(-missed);
                if (missed == 0) {
                    break;
                }
            }
        }