之前曾寫過一篇介紹 Spring 提出的 Reactive Streams 實現(xiàn)方案 —— Project Reactor 的文章:《Reactor 入門與實踐》,其中介紹了 Spring Reactor API 的用法。相信很多人都能發(fā)現(xiàn),Spring Reactor 的 API 看上去和 Java 8 Stream 很像:
Project Reactor:
Flux.fromIterable(Character.captainAmerica3())
.filter(Character::isPlay)
.map(Character::getName)
.subscribe(System.out::println);
Java 8 Stream:
Character.captainAmerica3().stream()
.filter(Character::isPlay)
.map(Character::getName)
.forEach(System.out::println);
上面兩段代碼分別由 Spring Reactor 和 Java 8 Stream 實現(xiàn),功能是打印《美隊3》參演的角色名稱秕铛。代碼的形式和大部分方法名稱都是相同的棒假。
那 Spring Reactor 或者說 Reactive Streams 和 Java 8 Stream 的差別是什么呢条辟?
簡單來說,這兩者最大的差別是前者是 Push-based胳施,后者是 Pull-based溯祸。
Push 與 Pull
Reactive Streams 與 Java 8 Stream 最大的不同在于,前者是 Push 模式舞肆,后者是 Pull 模式焦辅。可能大部分人對推與拉的概念不是很了解胆绊。先做一個形象的介紹,常見的操作集合類的方式就是 Pull 模式:
List<User> users = userRpcClient.findAllUsers();
for (String user : users) {
// do something
}
為什么說這樣的代碼是 Pull 模式呢欧募?因為這樣的代碼压状,客戶端主動向服務端請求數(shù)據(jù),然后在操作數(shù)據(jù)跟继。好比客戶端主動從服務端拉取數(shù)據(jù)种冬,故稱為拉模式。
那 Push 模式是什么樣呢舔糖?
userRpcPublisher.subscribe(new UserSubscriber() {
public void onNext(User user) {
// do something
}
});
簡單來說就是 Callback 風格的代碼通常都是 Push 模式的娱两。
Pull 模式的代碼的問題在于,如果
userRpcClient.findAllUsers()
表示的操作是一個很耗時的操作金吗,那 Pull-based 的代碼的并發(fā)能力將很成問題十兢。這就是 Push 模式出現(xiàn)的原因。
當然摇庙,上面兩個例子只是為了給大家一個直觀的介紹旱物。并不是說傳統(tǒng)形式的代碼就一定是 Pull 模式,Callback 風格的代碼就一定是 Pull 模式卫袒。
換言之宵呛,代碼形式并不是 Pull 與 Push 的本質(zhì)。從更深的層面說夕凝,Pull 模式對應的是同步的宝穗、命令式的程序,Push 模式對應的是異步的码秉、非阻塞的逮矛、反應式的程序。
因此转砖,雖然在代碼形式上說 Java 8 Stream 和 Reactive Streams 的代碼有些像橱鹏,但從本質(zhì)上來說,同步、阻塞的 Java 8 Stream 與異步莉兰、非阻塞的 Reactive Streams 有著很大的差別挑围。
因此 Reactive Streams 不僅在形的層面,以接口定義的形式對反應式編程做出了規(guī)范糖荒,更在實的層面定義了 TCK杉辙,用來保證相關實現(xiàn)確實滿足了異步、非阻塞等等的要求捶朵。