Rxjava2 简析Flowable背压(4)

  • Rxjava2 基本用法(1)
  • Rxjava2 操作符原理(2)
  • Rxjava2 线程切换(3)
  • Rxjava2 简析Flowable背压(4)

一、简介

前面几章都是介绍Observable,而Observable类是实现无背压方式的。而有背压的方式就不能使用Observable而是Flowable。其实这两个类没有太大的区别,尤其是操作符的处理这块。

关于Flowable和Observable有几个相似作用的类。

  • Flowable对应Observable
  • FlowableEmitter对应ObservableEmitter
  • Subscriber对应Observer
  • Subscription对于Disposable

关于这几个相同概念的类,背压方式的增加了一些额外的功能。

先来看下背压方式的简单实现:

// 1、创建Flowable对象
Flowable flowable = Flowable
                .create(new FlowableOnSubscribe() {
                    @Override
                    public void subscribe(FlowableEmitter emitter) throws Exception {
                        Log.e(TAG, "subscribe: " + emitter.requested());

                        for (int i = 0; i < 128; i++) {
                            Log.e(TAG, "subscribe: "+i );
                            emitter.onNext(i);
                        }
                    }
                }, BackpressureStrategy.DROP);
        // 2、创建Subscriber观察者对象
        Subscriber subscriber = new Subscriber() {
            Subscription mSubscription = null;

            @Override
            public void onSubscribe(Subscription s) {
                Log.e(TAG, "onSubscribe");
                mSubscription = s;
                s.request(1);
            }

            @Override
            public void onNext(Integer integer) {
                Log.e(TAG, "onNext: " + integer);
            }

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

            @Override
            public void onComplete() {
                Log.e(TAG, "onComplete");
            }
        };
        // 3、发生订阅关系
        flowable.subscribe(subscriber);

从使用角度来说,Flowable的整体框架和Observable没有区别,仅仅是换了不同的类,但是实现的功能大体一致。

  • 1、创建Flowable对象
  • 2、创建Subscriber观察者对象
  • 3、发生订阅关系

关于出现了压力后,也有不同的策略处理。Flowable提供了以下几种策略:

public enum BackpressureStrategy {
    /**
     * OnNext events are written without any buffering or dropping.
     * Downstream has to deal with any overflow.
     * 

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 all 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 }

  • BUFFER 当发送的事件有来不及处理的时候,会放在缓冲区里面,这个缓冲区会无限的增加,直到发生OOM
  • ERROR 当FlowableEmitter发射器在emitter.requested() == 0的时候发送就会抛出异常
  • DROP Rxjava默认的缓冲区为128,如果有来不及处理的事件,就会放到缓冲区,128个放满后,接下来的事件就会抛弃。
  • LATEST 与DROP策略类似,他会抛弃最开始的数据,缓冲最后的数据。

二、基本类的介绍

1)、Subscription

/**
 * A {@link Subscription} represents a one-to-one lifecycle of a {@link Subscriber} subscribing to a {@link Publisher}.
 * 

* It can only be used once by a single {@link Subscriber}. *

* It is used to both signal desire for data and cancel demand (and allow resource cleanup). * */ public interface Subscription { public void request(long n); public void cancel(); }

对于Subscription的解释是,它是和Subscriber对象是一对一的关系的,以及他是个控制类,控制事件流的流向。用户可以用该对象去拉取相应的数据。

Subscription类是对应Disposable类的,Disposable类原来的作用就是取消事件流的,Subscription保留了该方法。但同时增加了拉取方法request。该对象会在调用观察者Subscriber的时候传入。

2)、Subscriber

Subscriber是观察者,对应着Observable中的Observer类。

public interface Subscriber {
    /**
     * Invoked after calling {@link Publisher#subscribe(Subscriber)}.
     * 

* No data will start flowing until {@link Subscription#request(long)} is invoked. *

* It is the responsibility of this {@link Subscriber} instance to call {@link Subscription#request(long)} whenever more data is wanted. *

* The {@link Publisher} will send notifications only in response to {@link Subscription#request(long)}. * * @param s * {@link Subscription} that allows requesting data via {@link Subscription#request(long)} */ public void onSubscribe(Subscription s); public void onNext(T t); public void onError(Throwable t); public void onComplete(); }

Subscriber相比较Observer类,在onSubscribe传入的不是Disposable对象,而是Subscription对象,使用Subscription对象控制事件流。

3)、FlowableEmitter

FlowableEmitter类是发射器类,对应ObservableEmitter类。

public interface FlowableEmitter extends Emitter {

    /**
     * Sets a Disposable on this emitter; any previous Disposable
     * or Cancellation will be unsubscribed/cancelled.
     * @param s the disposable, null is allowed
     */
    void setDisposable(Disposable s);

    /**
     * Sets a Cancellable on this emitter; any previous Disposable
     * or Cancellation will be unsubscribed/cancelled.
     * @param c the cancellable resource, null is allowed
     */
    void setCancellable(Cancellable c);

    /**
     * The current outstanding request amount.
     * 

This method is thread-safe. * @return the current outstanding request amount */ long requested(); /** * Returns true if the downstream cancelled the sequence. *

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 */ FlowableEmitter serialize(); }

FlowableEmitter类有他独特的方法,主要的方法是long requested();可以用该方法来感知当前的下游的情况,可以使下游和上游产生联系。

FlowableEmitter本身是继承Emitter的,它具有发射的功能。在发射的之前可以通过requestd方法判断下游还可以处理多少,这样就完成了响应式拉取的核心东西。

3)、Flowable被观察者

Flowable是整个观察者模式中的被观察者概念。作为被观察者它是有个订阅功能。

Flowable是继承自Publisher类,该类的作用就是定义一个订阅方法。其中Subscriber是观察者。

public interface Publisher {
    public void subscribe(Subscriber s);
}

Flowable的结构和Observable是差不多的,现在就来看下一份精简的代码。

public abstract class Flowable implements Publisher {

    @BackpressureSupport(BackpressureKind.SPECIAL)
    @SchedulerSupport(SchedulerSupport.NONE)
    public static  Flowable create(FlowableOnSubscribe source, BackpressureStrategy mode) {
        ObjectHelper.requireNonNull(source, "source is null");
        ObjectHelper.requireNonNull(mode, "mode is null");
        return RxJavaPlugins.onAssembly(new FlowableCreate(source, mode));
    }

    @BackpressureSupport(BackpressureKind.SPECIAL)
    @SchedulerSupport(SchedulerSupport.NONE)
    @Override
    public final void subscribe(Subscriber s) {
        ObjectHelper.requireNonNull(s, "s is null");
        try {
            s = RxJavaPlugins.onSubscribe(this, s);

            ObjectHelper.requireNonNull(s, "Plugin returned null Subscriber");

            subscribeActual(s);
        } catch (NullPointerException e) { // NOPMD
            throw e;
        } catch (Throwable e) {
            Exceptions.throwIfFatal(e);
            // can't call onError because no way to know if a Subscription has been set or not
            // can't call onSubscribe because the call might have set a Subscription already
            RxJavaPlugins.onError(e);

            NullPointerException npe = new NullPointerException("Actually not, but can't throw other exceptions due to RS");
            npe.initCause(e);
            throw npe;
        }
    }

    protected abstract void subscribeActual(Subscriber s);

}

源码一看和Observable没什么区别,通过Create或者其他类似方式创建一个Flowable对象。在Create中和其他操作符中保存上一个的Observable对象,在subscribe中用上一个Observable对象调用下游传入的Subscriber,这样就形成了一个链式结构。

三、响应式拉去原理

背压策略

白话描述响应式拉取原理:观察者中的onSubscribe方法被调用的时候,会传入一个Subscription对象,该对象的request(int n)方法就是发送一个命令,下游可以处理n个数据。保存的变量就会增加n。上游的发送器中也可以访问到这个保存的变量,通过判断这个变量是否0就知道下游现在的情况了,从而触发onNext()方法继续发送事件。

现在就开始从源码角度看了:

  • 1、注册的时候走到FlowableCreate中的subscribeActual方法
  @Override
    public void subscribeActual(Subscriber t) {
        BaseEmitter emitter;

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

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

可以看出这里传入不同的策略,会生成对应的BaseEmitter策略子类。

  • 第一步还是执行相应的 t.onSubscribe(emitter);传入的Emitter就是Subscription的子类。

  • 第二步还是source.subscribe(emitter);通知用户开始发射事件流。

  • 2、第二步触发事件流

public void subscribe(FlowableEmitter emitter) throws Exception {
      if (emitter.requested()>0){
            emitter.onNext(1);
      }
}

source.subscribe(emitter)执行中,通常会用emitter去触发emitter.onNext(int),这样会触发事件流,也就到了DropAsyncEmitter中的onNext方法。

DropAsyncEmitter#onNext源码:

        @Override
        public final void onNext(T t) {
            if (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;
            }

            if (get() != 0) {
                actual.onNext(t);
                BackpressureHelper.produced(this, 1);
            } else {
                onOverflow();
            }
        }

前面都是检测合法性,重要的是下面的判断,如果get()!=0,说明下游可以处理数据,那就发送一个数据,否则则丢弃不管了(该事件到这就停止了),也就是Drop策略。get()!=0则处理actual.onNext(t);并将记录的处理数据减一。

再看下生产者这边,也就是在BaseEmitter中的request(int)方法:

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

先判断传入的n是否大于0是否合法,合法就更改可以处理的保存的记录数,生产者到这就完成了。

这里逻辑就基本清晰了,生产者(request(int))和消费者(onNext()),他们操作的是同一个内存数据。

生产者消费者模型

  • 触发onNext()是消费者,代表消费一个数据,前提是有数据可以消费。
  • request(int)是生产者,生产数据。

由于生产者和消费者可以在不同的线程操作,可能会带来线程不安全,所以采用了AtomicLong线程安全的Long来保存可消费的数据。

关于对数据内存的操作,被封装成了单独的类,下面是精简版本

public final class BackpressureHelper {
    /** Utility class. */
    private BackpressureHelper() {
        throw new IllegalStateException("No instances!");
    }

     // 判断是否超出了范围
    public static long addCap(long a, long b) {
        long u = a + b;
        if (u < 0L) {
            return Long.MAX_VALUE;
        }
        return u;
    }

    // 生产数据
    public static long add(AtomicLong requested, long n) {
        for (;;) {
            long r = requested.get();
            if (r == Long.MAX_VALUE) {
                return Long.MAX_VALUE;
            }
            long u = addCap(r, n);
            if (requested.compareAndSet(r, u)) {
                return r;
            }
        }
    }


    // 消费数据
    public static long produced(AtomicLong requested, long n) {
        for (;;) {
            long current = requested.get();
            if (current == Long.MAX_VALUE) {
                return Long.MAX_VALUE;
            }
            long update = current - n;
            if (update < 0L) {
                RxJavaPlugins.onError(new IllegalStateException("More produced than requested: " + update));
                update = 0L;
            }
            if (requested.compareAndSet(current, update)) {
                return update;
            }
        }
    }
}

以add(AtomicLong requested, long n)生产者为例。传入的参数是requested代表原来的数据的索引,n为需要再生产个数。本质上就是将request+n,但这样是线程不安全的。

首先使用for (;;) 循环,这里没有采用锁机制,而是采用自旋锁,将生产的数据和理论的数据进行对比,如果不是则重新操作,一致就说明这些操作是线程安全的,采用原理的比较设置为最新的数即可。

同理消费者也是。

四、小结

关于响应式拉取,本质上就是生产者和消费者模型。AtomicLong代表着数据源,是个被操作的对象;FlowableEmitter为发射器是消费者;Subscription为控制器,内部有request(int)方法,为生产者。

而BaseEmitter则继承了AtomicLong、FlowableEmitter和Subscription这三个类,也就是说明在BaseEmitter是个集大成者,将生产者消费者模型中的元素都放在了一起,产生了联系,这样就可以操作了。

其中BackpressureHelper是个代理处理数据源的类,他采用自旋锁机制提高了CPU的利用率,也保证了安全。因为这里的线程冲突场景不可能特别多,加锁的话会占用上千的CPU时钟周期,而自旋锁一般状态也就消耗几个时钟周期。

你可能感兴趣的:(Rxjava2 简析Flowable背压(4))