Spring WebFlux 要革了誰的命?

託夢


Java國王昨晚做了一個夢。

夢中有個白鬍子老頭兒,頗有仙風道骨, 告訴他說:“你們Java啊,實在是太弱了,連一個基本的功能都實現不了!”

國王大為驚奇:“什麼功能是我堂堂大Java搞不定的?”

老頭兒展示了兩行代碼:

float salary = 1000;
float tax = salary * 0.1;


國王說:“這不很正常嗎,薪水(salary)是1000, 稅(tax)等於100,我國小學生都能算出來。”

老頭兒說:“我要是現在讓salary=2000,那tax等於多少啊?”

“還是100! 因為tax沒有被重新計算!”

“薪水變了,為什麼稅不變呢? ”

“這個......”

看到國王不說話了,老頭兒繼續說:“你們得建立變量tax和變量salary之間的關聯

,讓他們像Excel一樣,一個單元格的值變了,Excel的公式自然會更新另外一個單元格,讓它隨之變化。”

float salary = 1000;
//假設這個命令建立了tax和salary之間的關聯
float tax <= salary *0.1
salary = 2000 ;
assertEquals(200,tax); //現在tax自動變成了200


國王說道:“你這麼做有什麼用啊,再說這也不是Java的問題,所有的語言都有這個問題啊......”

仙風道骨的老頭兒沒有回答, 慢慢消失了。

發佈-訂閱模式


第二天早朝,國王給大臣們講了自己這個奇怪的夢,看看誰能幫自己解一解, 沒想到大臣們七嘴八舌,說自己也做了同樣的夢。

這就奇怪了,難道有神靈故意給大家託夢嗎? 在夢中,仙人老頭兒給大家舉的例子都是一模一樣的。

集合大臣小心翼翼地說道:“難道他是在暗示我國的稅收太高了嗎, 有10%,要降低一點?”

國王瞪了他一眼:“胡說些什麼?我們的稅一點都不高,起徵點提高到了5000,除了五險一金扣除,我們還有房貸減免,租房減免,獨生子女減免,贍養老人減免等一系列政策, 怎麼能叫高呢?”

集合大臣趕緊噤聲。

IO大臣繼續說:“陛下不用煩惱,老頭兒說的問題,我們Java早已經提供了對應的解決方案,那就是發佈者-訂閱者模式啊。如果把Salary當成一個數據源的發佈者, 把Tax當成一個訂閱者,註冊到Salary當中, 每當Salary有變化,就發送一個事件給Tax,Tax收到後,做相應的計算就可以了。”

Spring WebFlux 要革了誰的命?


Spring WebFlux 要革了誰的命?


“這不就是一種把數據持續推送給觀察者或者訂閱者的一種模式嘛,這種小兒科的東西,有什麼用?” 老對頭線程大臣說道。

“在這個場景下確實沒啥用處,但是這種事件流的方式,如果處理好了,也許能解決大問題。” IO大臣雖然不太服氣,但是也想不出什麼好的應用場景出來。

高併發


國王看到兩個老傢伙又要幹起來,馬上轉移話題:“聽說我們Java在高併發方面遇到了一點兒問題?”

IO大臣立刻興奮起來,順杆就爬:“沒錯,這二十來年,我們Java一直使用Tomcat那種線程池的方式,現在在越來越差勁了,難以應對高併發了。”

這一下子把Tomcat大臣,線程大臣,甚至Servlet大臣都給拉了進來,國王暗自後悔。

IO大臣繼續侃侃而談:“現在的模型每來一個請求都會有一個線程來處理,如果這個請求涉及到IO或者網絡操作,這個線程不得不阻塞等待,沒法幹別的事情。”


Spring WebFlux 要革了誰的命?


Spring WebFlux 要革了誰的命?



“如果用戶的請求太多,那線程池中的線程很快就會被用光。這時候就沒辦法對外提供服務了。”

Spring WebFlux 要革了誰的命?



Spring WebFlux 要革了誰的命?



這確實是實情,基於Servlet的線程模型,就是這麼工作的。

國王問道:“那個線程調用RPC服務的時候,為什麼要等待呢? 讓它去幹別的事情,比如處理另外一個請求不就可以了?”

“陛下聖明,這就是關鍵所在,要充分地利用線程,一點出現IO調用,立刻走開,去幹別的事情,等到IO調用結束了,就可以通知線程去處理,這樣我們用少量的線程,就可以實現大量的併發了。”

國王說:“愛卿言之有理,你已經可以實現這種NIO的方式了,對吧?”

“沒錯,現在我們要做的就是要改造Tomcat,改造甚至替換Servlet !”

回調地獄


Servlet大臣一聽就急了,自己就是傳統的工作方式,一個請求一個線程,要是這麼搞,自己位置不保,他把目光投向了忠實的盟友線程大臣。

線程大臣心領神會:“IO大臣的辦法其實就是把同步的阻塞調用改成異步的非阻塞調用,不是那麼容易啊,別的不說,這異步編程,對我們臣民來說就很不容易。”

“你不是有什麼Future,Callable之類的東西嗎? 可以讓臣民們去用啊!” IO大臣不依不饒。

線程大臣笑了:“你是隻知其一,不知其二,當你調用那個future.get()的時候,如果線程的工作(例如數據庫查詢)還沒有做完,當前線程還得等待,還是阻塞的。

IO大臣有點兒後悔,自己怎麼忽略了這一層呢?

線程大臣趁勝追擊:“還有啊,即使是按你所說,所有的操作都是異步的,都是事件驅動的,那回調會大量存在,這代碼中回調地獄問題,你考慮了嗎?”

fun1(param,new Callback(){
void onSuccess(...){
......執行業務邏輯......
fun2(param,new Callback(){
void onSuccess(...){
......執行業務邏輯......
fun3(param,new Callback(){
void onSuccess(...){
......執行業務邏輯......
fun4(param,new Callback(){
void onSuccess(...){
......執行業務邏輯......
}
void onError(...){
}
});
}
void onError(...){
}
});
}
void onError(...){
}
});
}
void onError(...){
}
});


IO大臣看到這如同亂麻般的代碼,頭嗡的一聲就大了,這異步操作居然這麼變態!

國王看到IO大臣神色有異,不再說話,趕緊宣佈退朝。

事件流


在朝堂上很鬱悶的IO大臣怒氣衝衝地回到了家,下人送上的茶水也被他打翻在地。

幕僚已經瞭解今日朝堂發生之事,走上前來:“大人息怒,小人聽說民間有個叫做Reactor的東西,用什麼事件流和函數式編程中的高階函數,就能解決這個回調地獄問題。”

事件流? IO大臣突然被點醒了,我怎麼沒想起這茬兒, 昨天仙人託夢不就是引導我用事件流嗎?

他趕緊問道:“具體是怎麼做的?”

“我們用圖來表示,一個事件流是這樣的, 在這個時間線上,還有Error事件和Complete事件,分別用來表示出錯和完成,我就不畫了。”


Spring WebFlux 要革了誰的命?


Spring WebFlux 要革了誰的命?



“瞭解,可是這又有什麼用呢?” IO大臣問道。

“可以使用函數式編程對這個事件流做變換,例如map,把事件從‘圓圈’ 變成了‘三角’”


Spring WebFlux 要革了誰的命?



Spring WebFlux 要革了誰的命?


“還可以用filter對事件流做過濾”


Spring WebFlux 要革了誰的命?


Spring WebFlux 要革了誰的命?



“嗯, 看起來很清楚,我想到一個場景, 先調用函數1,產生了事件流,然後對事件流中的每個元素,又要調用函數B,又產生了新的事件流,該怎麼辦? ” IO大臣問道。

“大人真是厲害,抽象思維能力極高! ” 幕僚適時地拍了一下馬屁,“這時候可以用flatmap,把新的事件流給平鋪了。”


Spring WebFlux 要革了誰的命?



Spring WebFlux 要革了誰的命?


“map, filter, flatmap 僅僅是最基本的操作,還有switch , take, merge,zip等很多運算符,你想要的功能都能滿足!”

“不錯,不錯,” IO大臣興奮地直搓手,他已經把握住了其中的關鍵思想,回調地獄可以被解決了。

比如原來的需求是先異步調用fun1, 根據fun1的結果調用fun2, 只能這麼寫:

fun1(param,new Callback(){
void onSuccess(...){
fun2(param,new Callback(){
void onSuccess(...){
......
}
void onError(...){
}
});
}
void onError(...){
}
});


現在假設fun1 返回的是數據流,fun2返回的也是數據流,用這種新的方式,可以寫成這樣:

fun1(param)
.flatMap( e -> func2(e))
.subscribe(r -> showResult(r),
error -> handleError(error));


相當於把這一系列的回調給壓平了!

IO大臣問道:“你剛才說的民間的那個軟件叫什麼來著? ”

“民間很多的,有RxJava, Reactor,要不要我把他們的負責人叫來聊聊?”

“慢著,光是這個Reactor, 用處不大,你把Spring大臣也請來,我們需要讓Spring去使用Reactor,拋棄Servlet, 把所有的請求和處理都變成異步處理!”

新框架


三個月後,IO大臣喜氣洋洋地向國王彙報:“陛下,臣已經解開了仙人所託的夢,那其實是讓我Java帝國實現反應式編程(Reactive Programming)!”

“反應式編程? 這名字有點古怪!”

“對,這種方式是基於事件流和函數式編程的, 可以讓我們用非阻塞的、異步的方式來處理請求,還能解決回調地獄的問題。”

IO大臣把Reactor給國王講了一遍。

“那這個Reactor該如何使用? ”

“陛下還記得我們Java的高併發問題吧,就是由於沒法有效地管理異步和回調地獄導致的, 現在好了,臣和Spring攜手做了一個叫做Spring WebFlux的東西,獻給陛下,它不用Servlet,可以實現非阻塞的IO,可以有效地應對高併發。 ” IO大臣展示了一幅圖。


Spring WebFlux 要革了誰的命?



Spring WebFlux 要革了誰的命?


Servlet大臣一看,臉都綠了:我的位置在哪兒?

Tomcat大臣也覺得不爽,原來自己一家獨大,現在被Netty給擠走了。

只有JDBC大臣還不慌不忙:“用異步非阻塞處理所有東西? 你省省吧,我這裡訪問數據庫還是阻塞的呢!”

IO大臣心中暗叫不妙,怎麼忘了JDBC這麼重要的東西,既然想實現異步、非阻塞,那一定是端到端的,全鏈路的實現,某個點的阻塞調用都會導致整體出問題。

可他還是保持了鎮靜:“不用擔心,民間的開源社區很快就會搞出來非阻塞的JDBC驅動的。”

國王看到這個新的Spring WebFlux簡直是要革了好幾個大員的命,也只好安撫一下Tomcat, Servlet:“這樣吧,新事物總得有個漸進的採納過程,我們讓Spring MVC和Spring WebFlux 並存一段時間,讓臣民們按照自己的實際情況來選擇吧!”

Spring WebFlux 要革了誰的命?


想要更多Java 有趣的小故事可以私信我哈

私信我:“資料”,還可免費領取更多學習資料哦

Spring WebFlux 要革了誰的命?

Spring WebFlux 要革了誰的命?


分享到:


相關文章: