閱讀 Subscriber 的實現中關于 backpressure 的部分

rxjava 中最具有挑戰性的設計就是 backpresure 。例如 zip 操作符,合并兩個 Observable A 和 B 。如果 B 的產生速度比 A 快,那么就需要不停的緩存 B 多余生成出來的數據,這樣內存就無限增長了。 backpressure 的機制就是讓 B 生成慢一點。

目前為止,我看到 rxjava 的設計是很丑陋的。這種機制是沒有強制性的。更糟糕的是, rxjava 暴露了 Observable.create(OnSubscribe<?> onSubscribe) 這個函數,如果不了解這個機制,上來"想當然" 的實現一個 OnSubscribe ,而不管 backpressure 機制,很容易產生 MissingBackpressureException

“想當然” 不是使用者的錯,而是庫的設計者的錯誤。可惜的是,太多用戶重度使用這個 Observable.create(OnSubscribe<?> onSubscribe) 函數,為了保證現有程序能夠繼續運行,就不能隱藏這個函數。于是,我們在注釋中,可以看到下面一段話

<strong>This method requires advanced knowledge about building operators and data sources; please consider other standard methods first; </strong>

本文試圖得到 “advanced knowledge” 。

下面是 backpressure 的協議是如何建立的。

someObservable.subscribe(new Subscriber<T>() {
    @Override
    public void onStart() {
      request(1);
    }

    @Override
    public void onCompleted() {
      // gracefully handle sequence-complete
    }

    @Override
    public void onError(Throwable e) {
      // gracefully handle error
    }

    @Override
    public void onNext(T n) {
      // do something with the emitted item "n"
      // request another item:
      request(1);
    }
});

可見底層 subscriber 在剛剛啟動的時候,發起流控請求 onStart , request(1) 。告訴樓上的,哥們,別整太多,一個數據就夠了,多了處理不了。
onNext 中,先處理數據,處理完了,告訴樓上的,接著往下放數據,別多,就一個。

這里需要注意的是,不能再 request(n) 函數里面產生數據,否則遞歸調用 onNext ,可能導致爆棧了。

我們看看 Subscriber 是如何實現這個協議的。

public abstract class Subscriber<T> implements Observer<T>, Subscription {
// represents requested not set yet
private static final long NOT_SET = Long.MIN_VALUE;
private final SubscriptionList subscriptions;
private final Subscriber<?> subscriber;
private Producer producer;
private long requested = NOT_SET; // default to not set
}

本文重點關注 backpressure ,只看和這個相關的變量

  • NOT_SET 表示無效的請求數據量。或者說,還 Subscriber 沒有提供請求的數據量時的狀態。
  • subscriber ,如果這個值不為 null,那么把 backpressure 相關的處理,交給這個 subscriber 處理。有大多數很多操作符,自己并不能很好的處理這種過載,需要一層層向上傳遞,一直到數據源,只有產生數據的地方,才能比較好的處理,因為在那里,可以很容易的少產生一些數據。
  • producer 如果本 subscriber 可以處理,那么代理給 producer 來處理。
  • requested ,計數器,記錄樓下的請求多少數據。
    • 如果是 NOT_SET ,就是說樓下還不知道請求多少。
    • 如果是 MAX_LONG ,就是說樓下來者不拒,不怕 overload
    • 如果是其他值,就是說樓下的最多能處理多少數據。
 protected final void request(long n) {
       // if producer is set then we will request from it
       // otherwise we increase the requested count by n
       if (producer != null) {
            producer.request(n);
       } else {
            requested = requested + n;
       }  
}

這個函數被我簡化了,去掉了關于線程安全的部分。這樣代碼的可讀性好多了。

  • 就是說如果有 producer ,那么計數的功能就交給 producer 了。
  • 如果沒有,那么 requested 用來計數。

這里簡化了代碼,去掉了 requested 溢出的處理,就是說當 requested + nLONG_MAX 還要大的時候,會防止其變成負數。

public void setProducer(Producer p) {
    boolean passToSubscriber = subscriber != null && requested == NOT_SET;
    producer = p;
    if (passToSubscriber) {
        subscriber.setProducer(producer);
    } else {
        if (requested == NOT_SET) {
            producer.request(Long.MAX_VALUE);
        } else {
            producer.request(requested);
        }
    }
}

同樣,這里去掉了關于線程安全的代碼。

個人認為,setProducer 這個函數名字起的不好,因為這個函數除了設置 producer 成員變量之外,還會調用 produce.request 函數。

再來分析一下這個 setProducer 函數

  • 底層是否掉用過本層的 request(n)
    • 如果調用過,requested != NOT_SET,意味著底層出發了流控請求。
    • 如果沒有調用過,requested == NOT_SET,意味著底層沒有出發了流控請求。
  • producer 是真正處理流控的邏輯。subscriber 把流控邏輯交給 producer處理。如果沒有 producer , subscriber 也就只能簡單的計數,根本處理不了流控。
  • 如果在觸發流控請求之前,setProducer 函數被調用,那么要看本層是否愿意處理這個流控請求。
    • 如果成員變量 subscriber 不是空,那么表示本層 Subscriber 不愿意,或者不能夠處理好這個 backpressure ,交個上層處理 subscriber.setProducer(producer)
    • 上層如果不產生數據,本層的 OnNext 也不會觸發。從而達到了流控的目的。這樣一層一層往上傳,一直要交給數據源那一層才好處理。換句話說,如果你需要創建了一個 Observable,例如你寫了一個新的 operator ,但是不能很好地處理 backpressure ,那么最好往上傳遞。在 OnSubscribe 的時候,把本層 subscriber和上層 subscriber 串起來。
    • 如果本層愿意處理 backpressure 請求,那么就調用 procuder.request 處理請求。
  • 如果是在觸發流控請求之后, setProducer 被調用,那么無論本層是否愿意,都要處理這個請求。

代碼雖短,這個邏輯太復雜了。

小結

這里剛剛是一個皮毛,真正的 producer 處理流控邏輯還沒有提到。下次有時間,專門分析一個真正的流控邏輯。

同時,我們也看到,最好不要自己寫 operator 和 OnSubscribe ,而是調用現成的 from 系列函數, createSync 之類的提供流控的工廠方法,構造 Observable。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容