5章 RxJava背壓策略

Hensen發表於2019-05-13

本篇文章已授權微信公眾號 YYGeeker 獨家釋出轉載請標明出處

CSDN學院課程地址

5. RxJava背壓策略(BackpressureStrategy)

5.1 背壓是什麼

背壓的概念是在平時業務開發時較為常見,大多數是針對高併發的業務,背壓是必須考慮的因素之一。在非同步場景中,由於資料流的發射速度高於資料流的接收速度,就會導致資料不能及時處理,從而導致資料流的阻塞。背壓所要做的事情就是主動控制資料流發射的速度

在RxJava2.0中,推出了Flowable用來支援背壓,去除了Observable對背壓的支援,下面在背壓策略的講解中,我們都使用Flowable作為我們的響應型別。在使用背壓時,只需要在create()方法中第二個引數新增背壓策略即可

  1. 在訂閱的時候如果使用FlowableSubscriber,那麼需要通過s.request(Long.MAX_VALUE)去主動請求上游的資料項。如果遇到背壓報錯的時候,FlowableSubscriber預設已經將錯誤try-catch,並通過onError()進行回撥,程式並不會崩潰
  2. 在訂閱的時候如果使用Consumer,那麼不需要主動去請求上游資料,預設已經呼叫了s.request(Long.MAX_VALUE)。如果遇到背壓報錯、且對Throwable的Consumer沒有new出來,則程式直接崩潰
  3. 背壓策略的上游的預設快取池是128
public abstract class Flowable<T> implements Publisher<T> {
    /** The default buffer size. */
    static final int BUFFER_SIZE;
    static {
        BUFFER_SIZE = Math.max(1, Integer.getInteger("rx2.buffer-size", 128));
    }
}
複製程式碼

5.2 MISSING

MISSING表示OnNext事件沒有任何快取和丟棄,下游要處理任何溢位,可以理解為相當於沒有指定背壓策略。Flowable相當於沒有指定背壓策略可以將下游要處理任何溢位理解為,上游發射的資料未得到處理,就會快取起來,當快取容量達到128時,再增加一個未處理的資料項,就會丟擲MissingBackpressureException,且帶有佇列已經滿了的友好提示。這裡就好比一個大水缸,當水注滿的時候,它就會把蓋子蓋上,不讓你再繼續注水了

這裡我們模擬上游傳送速度高於下游資料流的處理速度,在資料處理的時候加上Thread.sleep(1000)

public void missing() {
    Flowable.create(new FlowableOnSubscribe<Integer>() {
        @Override
        public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
            for (int i = 0; i < 129; i++) {
                emitter.onNext(i);
            }
        }
    }, BackpressureStrategy.MISSING)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new FlowableSubscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {
                    s.request(Long.MAX_VALUE);
                }

                @Override
                public void onNext(Integer integer) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e("TAG", "onNext=" + integer);
                }

                @Override
                public void onError(Throwable t) {
                    t.printStackTrace();
                }

                @Override
                public void onComplete() {

                }
            });
}
複製程式碼

輸出

io.reactivex.exceptions.MissingBackpressureException: Queue is full?!
複製程式碼

5.3 ERROR

ERROR表示在下游無法跟上時,會丟擲MissingBackpressureException。可以將下游無法跟上理解為,上游發射的資料未得到處理,就會快取起來,當快取容量達到128時,再增加一個未處理的資料項,就會丟擲MissingBackpressureException。這裡好比一個大水缸,當水注滿的時候,它會把水缸撐破了,直接破裂

public void error() {
    Flowable.create(new FlowableOnSubscribe<Integer>() {
        @Override
        public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
            for (int i = 0; i < 129; i++) {
                emitter.onNext(i);
            }
        }
    }, BackpressureStrategy.ERROR)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new FlowableSubscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {
                    s.request(Long.MAX_VALUE);
                }

                @Override
                public void onNext(Integer integer) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e("TAG", "onNext=" + integer);
                }

                @Override
                public void onError(Throwable t) {
                    t.printStackTrace();
                }

                @Override
                public void onComplete() {

                }
            });
}
複製程式碼

輸出

io.reactivex.exceptions.MissingBackpressureException: create: could not emit value due to lack of requests
複製程式碼

5.4 BUFFER

上游不斷的發出onNext請求,直到下游處理完,上游發射的資料項的快取池是無限大的,程式也不會丟擲錯誤,但是要注意程式OOM的現象,因為快取越大,佔用的記憶體就越多。例子中發射129個資料項,然而程式並沒有崩潰,只會一直讀取快取池的資料項,直到資料項被處理完。這裡就是一個無限大的水缸

背壓策略除了BUFFER策略的快取池是無限大之外,其他預設的快取池都是128

public void buffer() {
    Flowable.create(new FlowableOnSubscribe<Integer>() {
        @Override
        public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
            for (int i = 0; i < 1000; i++) {
                emitter.onNext(i);
            }
        }
    }, BackpressureStrategy.BUFFER)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new FlowableSubscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {
                    s.request(Long.MAX_VALUE);
                }

                @Override
                public void onNext(Integer integer) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e("TAG", "onNext=" + integer);
                }

                @Override
                public void onError(Throwable t) {
                    t.printStackTrace();
                }

                @Override
                public void onComplete() {

                }
            });
}
複製程式碼

輸出

onNext=0
onNext=1
onNext=2
......
onNext=998
onNext=999
複製程式碼

5.5 DROP

會在下游跟不上速度時,把onNext的值丟棄,簡單的說就是,超過快取區大小(128)的資料項都會被丟棄。例子中通過發射800個資料項,那麼我們只會收到0-127的資料項。如果我們再次呼叫request(),這時候取到的資料就是上一次request()後的128個資料。這裡好比一個大水缸,當水注滿的時候,水還是在繼續的流,一旦有request呼叫的時候,它就會去取出水缸裡的所有水,這時候水缸就是空的,但水一直在流,所以水缸馬上又會被注滿,這個時候就要等request再次取出水缸裡的水

public void drop() {
    Flowable.create(new FlowableOnSubscribe<Integer>() {
        @Override
        public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
            for (int i = 0; i < 1000; i++) {
                emitter.onNext(i);
            }
        }
    }, BackpressureStrategy.DROP)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new FlowableSubscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {
                    s.request(Long.MAX_VALUE);
                }

                @Override
                public void onNext(Integer integer) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e("TAG", "onNext=" + integer);
                }

                @Override
                public void onError(Throwable t) {
                    t.printStackTrace();
                }

                @Override
                public void onComplete() {

                }
            });
}
複製程式碼

輸出

onNext=0
onNext=1
onNext=2
......
onNext=127
複製程式碼

5.6 LATEST

LATEST與Drop策略一樣,如果超過快取池容量大小的資料項都會被丟棄。不同的是,不管快取池的狀態如何,LATEST都會將最後一條資料強行放入快取池中。這裡的水缸容納下了最後一滴水

public void latest() {
    Flowable.create(new FlowableOnSubscribe<Integer>() {
        @Override
        public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
            for (int i = 0; i < 1000; i++) {
                emitter.onNext(i);
            }
        }
    }, BackpressureStrategy.LATEST)
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new FlowableSubscriber<Integer>() {
                @Override
                public void onSubscribe(Subscription s) {
                    s.request(Long.MAX_VALUE);
                }

                @Override
                public void onNext(Integer integer) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.e("TAG", "onNext=" + integer);
                }

                @Override
                public void onError(Throwable t) {
                    t.printStackTrace();
                }

                @Override
                public void onComplete() {

                }
            });
}
複製程式碼

輸出

onNext=0
onNext=1
......
onNext=126
onNext=127
onNext=999
複製程式碼

5.7 小結

  1. MISSING:沒有任何快取和丟棄,下游要處理任何溢位
  2. ERROR:下游的處理速度無法跟上上游的發射速度時報錯
  3. BUFFER:資料項的快取池無限大
  4. DROP:下游的處理速度無法跟上上游的發射速度時丟棄
  5. LATEST:最後一條資料項被強行放入快取池

相關文章