使用Rx系列框架也有2年多了, 真的感覺它在項目就像是萬金油, 哈哈.
項目裏經常有併發請求外部系統(然後合併結果, 這個今天先不考慮)的情況.
這裏給出一個場景: 需要調用N次外部系統, 調用無返回值, 只需要確保調用結束即可, 可以不處理異常情況, 但需要確保全部調用都完成程序才能繼續執行下去.
如果自己去寫代碼實現這裏就不說了, 大概就是: 拿到一個線程池, 提交多個任務, 根據情況block所有future等待結果.
我知道Rx裏有2種方式可以實現併發操作: parallel操作符 和 flatMap+subscribeOn組合操作符.
Parallel使用起來比較簡單, 假設併發度是2, 它將N個請求輪流分到2個隊列裏, 然後每個隊列用一個線程去消費. 這樣有一個缺點就是不支持工作竊取, 比如剛好1個隊列積壓, 另一各隊列已經空閒, 也不會將積壓隊列的工作量分一些到另一個隊列的.
使用 flatMap+subscribeOn 的話就沒有這個問題了, 可以用concurrency來控制flatMap的併發度. subscribeOn是爲了讓內部的Mono的阻塞任務跑在IO線程上, 否則內部的Mono會阻塞在subscribe階段, 因爲我們在fromCallable做了阻塞操作sleep.
/**
* parallel不支持工作竊取, 雖然代碼短一些, 看起來似乎沒flatMap好. 但parallel有其他用處
*
* @author xzchaoo
* @since 2020-02-27
*/
public class ParallelTest {
/**
* parallel不支持工作竊取
*/
@Test
public void test_parallel_notSupportStealing() {
Flux.just(9, 1, 1, 1)
.parallel(2)
.runOn(Schedulers.elastic())
.doOnNext(e -> {
try {
System.out.println("在線程 " + Thread.currentThread() + " 收到參數 " + e);
TimeUnit.SECONDS.sleep(e);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
})
.then()
.block();
}
/**
* 使用如下的flatMap可以支持工作竊取
*/
@Test
public void test_flatMap_supportStealing() {
Flux.just(9, 1, 1, 1)
.flatMap(e -> {
return Mono.fromCallable(() -> {
System.out.println("在線程 " + Thread.currentThread() + " 收到參數 " + e);
TimeUnit.SECONDS.sleep(e);
return null;
})
.subscribeOn(Schedulers.elastic());
}, 2)
.ignoreElements()
.block();
}
}