一文帶你徹底瞭解Java異步編程

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"隨着","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"RxJava","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Reactor","attrs":{}}],"attrs":{}},{"type":"text","text":"等異步框架的流行,異步編程受到了越來越多的關注,尤其是在IO密集型的業務場景中,相比傳統的同步開發模式,異步編程的優勢越來越明顯。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那到底什麼是異步編程?異步化真正的好處又是什麼?如何選擇適合自己團隊的異步技術?在實施異步框架落地的過程中有哪些需要注意的地方?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文從以下幾個方面結合真實項目異步改造經驗對異步編程進行分析,希望能給大家一些客觀認識:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"使用RxJava異步改造後的效果","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"什麼是異步編程?異步實現原理","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"異步技術選型參考","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"異步化真正的好處是什麼?","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"異步化落地的難點及解決方案","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"擴展:異步其他解決方案-協程","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"使用RxJava異步改造後的效果","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下圖是我們後端java項目使用RxJava改造成異步前後的RT(響應時長)效果對比:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bb/bbf333e4de4a636b19fad308ff3fc96f.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/81/81b7bed774d0989c8c7efde73221d10f.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"統計數據基於App端的gateway,以75線爲準,還有80、85、90、99線,從圖中可以看出改成異步後接口整體的平均響應時長降低了","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"40%","attrs":{}},{"type":"text","text":"左右。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(響應時間是以發送請求到收到後端接口響應數據的時長,上圖改造的這個後端java接口內部流程比較複雜,因爲公司都是微服務架構,該接口內部又調用了6個其他服務的接口,最後把這些接口的數據彙總在一起返回給前端)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這張圖是同步接口和改造成異步接口前後的CPU負載情況對比","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"改造前cpu load : 35.46","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/19/1906172d5c201de458f88a35c35eae67.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"改造後cpu load : 14.25","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2b/2b6a3c4b3d9b8bfa4221f3f8105900a2.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"改成異步後CPU的負載情況也有明顯下降,但CPU使用率並無影響(一般情況下異步化後cpu的利用率會有所提高,但要看具體的業務場景)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CPU LoadAverage是指:一段時間內處於可運行狀態和不可中斷狀態的進程平均數量。(可運行分爲正在運行進程和正在等待CPU的進程;","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"不可中斷則是它正在做某些工作不能被中斷比如等待磁盤IO、網絡IO等","attrs":{}},{"type":"text","text":")","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而我們的服務業務場景大部分都是IO密集型業務,功能實現很多需要依賴底層接口,會進行頻繁的IO操作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下圖是2019年在全球架構師峯會上","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"阿里","attrs":{}},{"type":"text","text":"分享的異步化改造後的RT和QPS效果:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/f2/f2db125c8dcfe37d8ca812f8fcecbe41.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(圖片來源:淘寶應用架構升級——反應式架構的探索與實踐)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"什麼是異步編程?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"響應式編程 + NIO","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 異步和同步的區別:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先從","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"I/O","attrs":{}},{"type":"text","text":"的角度看下同步模式下接口A調用接口B的交互流程:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下圖是傳統的同步模式下io線程的交互流程,可以看出io是阻塞的,即bio的運行模式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/65/654c6edb88dca21d27e76a89eb6ca895.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"接口A發起調用接口B後,這段時間什麼事情也不能做,主線程阻塞一直等到接口B數據返回,然後才能進行其他操作,可想而知如果接口A調用的接口不止B的話(A->B->C->D->E。。。),那麼等待的時間也是遞增的,而且","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"這期間CPU也要一直佔用着","attrs":{}},{"type":"text","text":",白白浪費資源,也就是上圖看到的 cpu load 高的原因。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而且還有一個隱患就是如果調用的其他服務中的接口比如C超時,或接口C掛掉了,那麼對調用方服務A來說,剩餘的接口比如D、E都會無限等待下去。。。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實大部分情況下我們收到數據後內部的處理邏輯耗時都很短,這個可以通過埋點執行時間統計,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"大部分時間都浪費在了IO等待上","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面這個視頻演示了同步模式下我們線上環境真實的接口調用情況,即接口調用的線程執行和變化情況,(使用的工具是JDK自帶的jvisual來監控線程變化情況)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏先交代下大致背景:服務端api接口A內部一共調用了6個其他服務的接口,大致交互是這樣的:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"A接口(B -> C -> D -> E -> F -> G)返回聚合數據","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"背景:使用Jemter測試工具壓測100個線程併發請求接口,以觀察線程的運行情況(可以全屏觀看):","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"video","attrs":{"videoHTML":""}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"http-nio-8080-exec*","attrs":{}}],"attrs":{}},{"type":"text","text":"開頭的是tomcat線程池中的線程,即前端請求我們後端接口時要通過tomcat服務器接收和轉發的線程,因爲我們後端api接口內部又調用了其他服務的6個接口(B、C、D、E、F、G),同步模式下需要等待上一個接口返回數據才能繼續調用下一個接口,所以可以從視頻中看出,大部分的http線程耗時都在8秒以上(綠色線條代表線程是\"運行中\"狀態,8秒包括等待接口返回的時間和我們內部邏輯處理的總時間,因爲是本地環境測試,受機器和網絡影響較大)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後我們再看下異步模式的交互流程,即nio方式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8b/8bc91d61e94ae8ab4f3fbf7cca5a0f65.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大致流程就是接口A發起調用接口B的請求後就立即返回,而不用阻塞等待接口B響應,這樣的好處是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"http-nio-8080-exec*","attrs":{}}],"attrs":{}},{"type":"text","text":"線程可以","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"馬上得到複用,接着處理下一個前端請求的任務","attrs":{}},{"type":"text","text":",如果接口B處理完返回數據後,會有一個回調線程池處理真正的響應,即這種模式下我們的業務流程是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"http線程只處理請求,回調線程處理接口響應","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個視頻演示了異步模式下接口A的線程執行情況,同樣也是使用Jemter測試工具壓測100個線程併發請求接口,以觀察線程的運行情況(可以全屏觀看):","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"video","attrs":{"videoHTML":""}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"模擬的條件和同步模式一樣,同樣是100個線程併發請求接口,但這次","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"http-nio-8080-exec*","attrs":{}}],"attrs":{}},{"type":"text","text":"開頭的線程只處理請求任務,而不再等待全部的接口返回,所以http的線程運行時間普遍都很短(大部分在1.8秒左右完成),","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AsfThread-executor-*","attrs":{}}],"attrs":{}},{"type":"text","text":"是我們系統封裝的回調線程池,處理底層接口的真正響應數據。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"演示視頻中的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AsfThread-executor-*","attrs":{}}],"attrs":{}},{"type":"text","text":"的回調線程只創建了30多個,而請求的http線程有100個,也就是說這30多個回調線程處理了接口B的100次響應(其實應該是600次,因爲接口B內部又調用了6個其他接口,這6次也都是在異步線程裏處理響應的),因爲每個接口返回的時間不一樣,加上網絡傳輸的時間,所以可以利用這個時間差充分複用線程即cpu資源,視頻中回調線程","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AsfThread-executor-*","attrs":{}}],"attrs":{}},{"type":"text","text":"的綠色運行狀態是多段的,表示複用了多次,也就是少量回調線程處理了全部(600次)的響應,這正是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"IO多路複用","attrs":{}},{"type":"text","text":"的機制。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"nio模式下雖然","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"http-nio-8080-exec*","attrs":{}}],"attrs":{}},{"type":"text","text":"線程和回調線程","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"AsfThread-executor-*","attrs":{}}],"attrs":{}},{"type":"text","text":"的運行時間都很短,但是從http線程開始到asf回調處理完返回給前端結果的時間和bio即同步模式下的時間差異不大(在相同的邏輯流程下),並不是nio模式下服務響應的整體時間就會縮短,而是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"會提升CPU的利用率","attrs":{}},{"type":"text","text":",因爲CPU不再會阻塞等待(不可中斷狀態減少),這樣","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"CPU就能有更多的資源來處理其他的請求任務","attrs":{}},{"type":"text","text":",相同單位時間內能處理更多的任務,所以nio模式帶來的好處是:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"提升QPS(用更少的線程資源實現更高的併發能力)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"降低CPU負荷,提高利用率","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. Nio原理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/03/03f780f4da01b66f282a0b08d84e1b32.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"結合上面的接口交互圖可知,接口B通過網絡返回數據給調用方(接口A)這一過程,對應底層實現就是網卡接收到返回數據後,通過自身的DMA(直接內存訪問)將數據拷貝到內核緩衝區,這一步不需要CPU參與操作,也就是把原先CPU等待的事情交給了底層網卡去處理,這樣","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"CPU就可以專注於我們的應用程序即接口內部的邏輯運算","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. Nio In Java","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8f/8f6bd1646c879dafd7dcefe6ae48059b.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"nio在java裏的實現主要是上圖中的幾個核心組件:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"channel","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"buffer","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"selector","attrs":{}}],"attrs":{}},{"type":"text","text":",這些組件組合起來即實現了上面所講的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"多路複用機制","attrs":{}},{"type":"text","text":",如下圖所示:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/38/3860afa25871631af956c31130a6044e.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"響應式編程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 什麼是響應式編程?它和傳統的編程方式有什麼區別?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"響應式可以簡單的理解爲收到某個事件或通知後採取的一系列動作,如上文中所說的響應操作系統的網絡數據通知,然後以","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"回調的方式","attrs":{}},{"type":"text","text":"處理數據。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"傳統的命令式編程主要由:順序、分支、循環 等控制流來完成不同的行爲","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"響應式編程的特點是:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"以邏輯爲中心轉換爲以數據爲中心","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"從命令式到聲明式的轉換","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. Java.Util.Concurrent.Future","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Java使用nio後無法立即拿到真實的數據,而且先得到一個\"","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"future","attrs":{}}],"attrs":{}},{"type":"text","text":"\",可以理解爲郵戳或快遞單,爲了獲悉真正的數據我們需要不停的通過快遞單號查詢快遞進度,所以 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"J.U.C 中的 Future 是Java對異步編程的第一個解決方案","attrs":{}},{"type":"text","text":",通常和線程池結合使用,僞代碼形式如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"ExecutorService executor = Executors.newCachedThreadPool(); // 線程池\nFuture future = executor.submit(() ->{\n Thread.sleep(200); // 模擬接口調用,耗時200ms\n return \"hello world\";\n});\n// 在輸出下面異步結果時主線程可以不阻塞的做其他事情\n// TODO 其他業務邏輯\n\nSystem.out.println(\"異步結果:\"+future.get()); //主線程獲取異步結果","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Future","attrs":{}}],"attrs":{}},{"type":"text","text":"的缺點很明顯:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無法方便得知任務何時完成","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"無法方便獲得任務結果","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在主線程獲得任務結果會導致主線程阻塞","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. ListenableFuture","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Google併發包下的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"listenableFuture","attrs":{}}],"attrs":{}},{"type":"text","text":"對Java原生的future做了擴展,顧名思義就是使用監聽器模式實現的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"回調機制","attrs":{}},{"type":"text","text":",所以叫可監聽的future。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Futures.addCallback(listenableFuture, new FutureCallback() {\n @Override\n public void onSuccess(String result) {\n System.out.println(\"異步結果:\" + result);\n }\n\n @Override\n public void onFailure(Throwable t) {\n t.printStackTrace();\n }\n}, executor);","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"回調機制的最大問題是:","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Callback Hell(回調地獄)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"試想如果調用的接口多了,而且接口之間有依賴的話,最終寫出來的代碼可能就是下面這個樣子:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/51/510a89d19987429b546ba891a831793a.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼的字面形式和其所表達的業務含義不匹配","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"業務的先後關係在代碼層面變成了包含和被包含的關係","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大量使用 Callback 機制,使應該是先後的業務邏輯在代碼形式上表現爲層層嵌套,這會導致代碼難以理解和維護。","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那麼如何解決 Callback Hell 問題呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"響應式編程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實主要是以下兩種解決方式:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"事件驅動機制","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"鏈式調用(Lambda)","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4. CompletableFuture","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Java8裏的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"CompletableFuture","attrs":{}}],"attrs":{}},{"type":"text","text":"和Java9的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Flow Api","attrs":{}}],"attrs":{}},{"type":"text","text":"勉強算是上面問題的解決方案:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"CompletableFuture f1 = CompletableFuture.supplyAsync(() ->\n \"hello\"\n);\n// f2依賴f1的結果做轉換\nCompletableFuture f2 = f1.thenApplyAsync(t ->\n t + \" world\"\n);\nSystem.out.println(\"異步結果:\" + f2.get());","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"CompletableFuture","attrs":{}}],"attrs":{}},{"type":"text","text":"處理簡單的任務可以使用,但並不是一個完整的反應式編程解決方案,在服務調用複雜的情況下,存在服務編排、上下文傳遞、柔性限流(背壓)方面的不足","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"CompletableFuture","attrs":{}}],"attrs":{}},{"type":"text","text":"面對這些問題可能需要自己額外造一些輪子,Java9的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Flow","attrs":{}}],"attrs":{}},{"type":"text","text":"雖然是基於 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Reactive Streams","attrs":{}},{"type":"text","text":" 規範實現的,但沒有RxJava、Project Reactor這些異步框架豐富和強大和完整的解決方案。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然如果接口邏輯比較簡單,完全可以使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"listenableFuture","attrs":{}}],"attrs":{}},{"type":"text","text":"或","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"CompletableFuture","attrs":{}}],"attrs":{}},{"type":"text","text":",關於他們的詳細用法可參考之前的一篇文章:","attrs":{}},{"type":"link","attrs":{"href":"http://javakk.com/225.html","title":null},"content":[{"type":"text","text":"Java異步編程指南","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5. Reactive Streams","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在網飛推出RxJava1.0並在Android端普及流行開後,響應式編程的規範也呼之欲出:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://javakk.com/redirect/aHR0cHM6Ly93d3cucmVhY3RpdmUtc3RyZWFtcy5vcmcv","title":null},"content":[{"type":"text","text":"https://www.reactive-streams.org/","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"包括後來的RxJava2.0、Project Reactor都是基於Reactive Streams規範實現的。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"關於他們和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"listenableFuture","attrs":{}}],"attrs":{}},{"type":"text","text":"、 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"CompletableFuture","attrs":{}}],"attrs":{}},{"type":"text","text":"的區別通過下面的例子大家應該就會清楚。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如下面的基於回調的代碼示例:獲取用戶的5個收藏列表功能","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7e/7eecd44ef865156f8f592e03f55dfa9f.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圖中標註序號的步驟對應如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"根據uid調用用戶收藏列表接口","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"userService.getFavorites","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"成功的回調邏輯","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"如果用戶收藏列表爲空","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"調用推薦服務","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"suggestionService.getSuggestions","attrs":{}}],"attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"推薦服務成功後的回調邏輯","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"取前5條推薦並展示(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java8 Stream api","attrs":{}}],"attrs":{}},{"type":"text","text":")","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":7,"align":null,"origin":null},"content":[{"type":"text","text":"推薦服務失敗的回調,展示錯誤信息","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":8,"align":null,"origin":null},"content":[{"type":"text","text":"如果用戶收藏列表有數據返回","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":9,"align":null,"origin":null},"content":[{"type":"text","text":"取前5條循環調用詳情接口","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"favoriteService.getDetails","attrs":{}}],"attrs":{}},{"type":"text","text":" 成功回調則展示詳情,失敗回調則展示錯誤信息","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看出主要邏輯都是在回調函數(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"onSuccess()","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"onError()","attrs":{}}],"attrs":{}},{"type":"text","text":")中處理的,在可讀性和後期維護成本上比較大。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於Reactive Streams規範實現的響應式編程解決方案如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c6/c6e8bb138f0d9700aaa7b7f2808a21e5.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"調用用戶收藏列表接口","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"壓平數據流調用詳情接口","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"如果收藏列表爲空調用推薦接口","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"取前5條","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":5,"align":null,"origin":null},"content":[{"type":"text","text":"切換成異步線程處理上述聲明接口返回結果)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":6,"align":null,"origin":null},"content":[{"type":"text","text":"成功則展示正常數據,錯誤展示錯誤信息","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看出因爲這些異步框架提供了豐富的api,所以我們可以把主要精力","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"放在數據的流轉上,而不是原來的邏輯控制上。這也是異步編程帶來的思想上的轉變。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下圖是RxJava的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"operator api","attrs":{}}],"attrs":{}},{"type":"text","text":":","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d1/d1e236cee963b9492fe3bcd80cc9d975.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(如果這些操作符滿足不了你的需求,你也可以自定義操作符)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以說","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"異步最吸引人的地方在於資源的充分利用,不把資源浪費在等待的時間上(nio),代價是增加了程序的複雜度,而Reactive Program封裝了這些複雜性,使其變得簡單。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以我們無論使用哪種異步框架,儘量使用框架提供的api,而不是像上圖那種基於回調業務的代碼,把業務邏輯都寫在onSuccess、onError等回調方法裏,這樣無法發揮異步框架的真正作用:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Codes Like Sync,Works Like Async","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即以","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"同步的方式編碼,達到異步的效果與性能,兼顧可維護性與可伸縮性","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"異步框架技術選型","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/84/84d966142c204c40ee0314c5af4d591d.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(圖片來源:淘寶應用架構升級——反應式架構的探索與實踐)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面這張圖也是阿里在2019年的深圳全球架構師峯會上分享的PPT截圖(文章末尾有鏈接),供大家參考,選型標準主要是基於穩定性、普及性、成本這3點考慮","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果是我個人更願意選擇Project Reactor作爲首選異步框架,(具體差異網上很多分析,大家可以自行百度谷歌),還有一點是因爲Netflix的尿性,推出的開源產品漸漸都不維護了,而且Project Reactor提供了","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"reactor-adapter","attrs":{}}],"attrs":{}},{"type":"text","text":"組件,可以方便的和RxJava的api轉換。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實還有","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Vert.x","attrs":{}},{"type":"text","text":"也算異步框架 (底層使用netty實現nio, 最新版已支持reactive stream規範)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"異步化真正的好處","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"Scalability","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"伸縮性主要體現在以下兩個方面:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"elastic 彈性","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"resilient 容錯性","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(異步化在平時","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"不會明顯降低 RT、提高 QPS","attrs":{}},{"type":"text","text":",文章開頭的數據也是在大促這種流量高峯下的體現出的異步效果)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從架構和應用等更高緯度看待異步帶來的好處則會提升系統的兩大能力:","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"彈性","attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"容錯性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前者反映了系統應對壓力的表現,後者反映了系統應對故障的表現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"1. 容錯性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"像RxJava,Reactor這些異步框架處理回調數據時一般會切換線程上下文,其實就是使用不同的線程池來隔離不同的數據流處理邏輯,下圖說明了這一特性的好處:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/8e/8ea2fd8406685fafccb95eddf5797173.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即利用異步框架支持線程池切換的特性實現","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"服務/接口隔離","attrs":{}},{"type":"text","text":",進而提高系統的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"高可用","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2. 彈性","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ea/ea732289e80167a8f8197954b5ee04b3.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"back-pressure是一種重要的反饋機制,相比於傳統的熔斷限流等方式,是一種更加","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"柔性的自適應限流","attrs":{}},{"type":"text","text":"。使得系統得以優雅地響應負載,而不是在負載下崩潰。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"異步化落地的難點及解決方案","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"還是先看下淘寶總結的異步改造中難點問題:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/04/044aa252693b89978be8bac138567f6f.png","alt":"image","title":"image","style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"(圖片來源:淘寶應用架構升級——反應式架構的探索與實踐)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"中間件全異步牽涉到到公司中臺化戰略或框架部門的支持,包括公司內部常用的中間件比如MQ、redis、dal等,超出了本文討論的範圍,感興趣的可以看下文章末尾的參考資料。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"線程模型統一的背景在上一節異步化好處時有提到過,其實主要還是對線程池的管理,做好服務隔離,線程池設置和注意事項可以參考之前的兩篇文章:","attrs":{}},{"type":"link","attrs":{"href":"http://javakk.com/188.html","title":null},"content":[{"type":"text","text":"Java踩坑記系列之線程池","attrs":{}}]},{"type":"text","text":" 、","attrs":{}},{"type":"link","attrs":{"href":"http://javakk.com/215.html","title":null},"content":[{"type":"text","text":"線程池ForkJoinPool簡介","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏主要說下上下文傳遞和阻塞檢測的問題:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. 上下文傳遞","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"改造成異步服務後,不能再使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal","attrs":{}}],"attrs":{}},{"type":"text","text":"傳遞上下文context,因爲異步框架比如RxJava一般在收到通知後會先調用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"observeOn()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法切換成另外一個線程處理回調,比如我們在請求接口時在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal","attrs":{}}],"attrs":{}},{"type":"text","text":"的context裏設置了一個值,在回調線程裏從context裏取不到這個值的,因爲此時已經不是同一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal","attrs":{}}],"attrs":{}},{"type":"text","text":"了,所以需要我們手動在切換上下文的時候傳遞context從一個線程到另一個線程環境,僞代碼如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Context context = ThreadLocalUtils.get(); // 獲取當前線程的上下文\nsingle.observeOn(scheduler).doOnEvent((data, error) -> ThreadLocalUtils.set(context)); // 切換線程後在doOnEvent裏重新給新的線程賦值context","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"observeOn()","attrs":{}}],"attrs":{}},{"type":"text","text":"方法切換成另外一個線程後調用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"doOnEvent","attrs":{}}],"attrs":{}},{"type":"text","text":"方法將原來的context賦給新的線程","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal","attrs":{}}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"注意","attrs":{}},{"type":"text","text":":這裏的代碼只是提供一種解決思路,實際在使用前和使用後還要考慮清空","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"ThreadLocal","attrs":{}}],"attrs":{}},{"type":"text","text":",因爲線程有可能會回收到線程池下次複用,而不是立即清理,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"這樣就會污染上下文環境","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以將傳遞上下文的方法封裝成公共方法,不需要每次都手動切換。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. 阻塞檢測","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"阻塞檢測主要是要能及時發現我們某個異步任務長時間阻塞的發生,比如異步線程執行時間過長進而影響整個接口的響應,原來同步場景下我們的日誌都是串行記錄到ES或Cat上的,現在改成異步後,每次處理接口數據的邏輯可能在不同的線程中完成,這樣記錄的日誌就需要我們主動去合併(依據具體的業務場景而定),如果日誌無法關聯起來,對我們排查問題會增加很多難度。所幸的是隨着異步的流行,現在很多日誌和監控系統都已支持異步了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Project Reactor 自己也有阻塞檢測功能,可以參考這篇文章:","attrs":{}},{"type":"link","attrs":{"href":"http://javakk.com/redirect/aHR0cHM6Ly9naXRodWIuY29tL3JlYWN0b3IvQmxvY2tIb3VuZA==","title":null},"content":[{"type":"text","text":"BlockHound","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. 其他問題","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了上面提到的兩個問題外,還有一些比如RxJava2.0之後不支持返回null,如果我們原來的代碼或編程習慣所致返回結果有null的情況,可以考慮使用java8的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional.ofNullable()","attrs":{}}],"attrs":{}},{"type":"text","text":"包裝一下,然後返回的RxJava類型是這樣的:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Single","attrs":{}}],"attrs":{}},{"type":"text","text":",其他異步框架如果有類似的問題同理。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"異步其他解決方案:纖程/協程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Quasar","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Kilim","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Kotlin","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Open JDK Loom","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AJDK wisp2","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"協程並不是什麼新技術,它在很多語言中都有實現,比如 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Python","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Lua","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Go","attrs":{}}],"attrs":{}},{"type":"text","text":" 都支持協程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"協程與線程不同之處在於,","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"線程由內核調度,而協程的調度是進程自身完成的","attrs":{}},{"type":"text","text":"。這樣就可以不受操作系統對線程數量的限制,一個線程內部可以創建成千上萬個協程。因爲上文講到的異步技術都是基於線程的操作和封裝,Java中的線程概念對應的就是操作系統的線程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. Quasar、Kilim","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開源的Java輕量級線程(協程)框架,通過利用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java instrument","attrs":{}}],"attrs":{}},{"type":"text","text":"技術對字節碼進行修改,使方法掛起前後可以保存和恢復JVM棧幀,方法內部已執行到的字節碼位置也通過增加狀態機的方式記錄,在下次恢復執行可直接跳轉至最新位置。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. Kotlin","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Kotlin Coroutine 協程庫,因爲 Kotlin 的運行依賴於 JVM,不能對 JVM 進行修改,因此Kotlin不能在底層支持協程。同時Kotlin 是一門編程語言,需要在語言層面支持協程,所以Kotlin 對協程支持最核心的部分是在編譯器中完成,這一點其實和Quasar、Kilim實現原理類似,都是在","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"編譯期通過修改字節碼","attrs":{}},{"type":"text","text":"的方式實現協程","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. Project Loom","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Project Loom 發起的原因是因爲長期以來Java 的線程是與操作系統的線程一一對應的,這限制了 Java 平臺併發能力提升,Project Loom 是","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"從 JVM 層面對多線程技術進行徹底的改變","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"OpenJDK 在2018年創建了 Loom 項目,目標是在JVM上實現輕量級的線程,並解除JVM線程與內核線程的映射。其實 Loom 項目的核心開發人員正是從Quasar項目過來的,目的也很明確,就是要將這項技術集成到底層JVM裏,所以Quasar項目目前已經不維護了。。。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4. AJDK Wisp2","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Alibaba Dragonwell 是阿里巴巴的 Open JDK 發行版,提供長期支持。dragonwell8已開源協程功能(之前的版本是不支持的),開啓jvm命令:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-XX:+UseWisp2","attrs":{}}],"attrs":{}},{"type":"text","text":" 即支持協程。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Future 在異步方面支持有限","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Callback 在編排能力方面有 Callback Hell 的短板","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Project Loom 最新支持的Open JDK版本是16,目前還在測試中","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AJDK wisp2 需要換掉整個JVM,需要考慮改動成本和收益比","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以目前實現異步化比較成熟的方案是 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Reactive Streams","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上是老K對異步編程的理解,如有問題歡迎指正。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外開發人員也不要將自己侷限在某種特定技術上,對各種技術都保持開放的態度是開發人員技能不斷提高的前提。只會簡單說某某語言、某某技術比其它技術更好的人永遠不會成爲出色的產品[狗頭]。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"From Reactive, More Than Reactive","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"文章來源:","attrs":{}},{"type":"link","attrs":{"href":"http://javakk.com/563.html","title":null},"content":[{"type":"text","text":"http://javakk.com/563.html","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"參考資料","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"全球架構師峯會:(第二天-淘寶應用架構升級——反應式架構的探索與實踐)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://javakk.com/redirect/aHR0cHM6Ly9hcmNoc3VtbWl0LmluZm9xLmNuLzIwMTkvc2hlbnpoZW4vc2NoZWR1bGU=","title":null},"content":[{"type":"text","text":"https://archsummit.infoq.cn/2019/shenzhen/schedule","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Project Reactor:","attrs":{}},{"type":"link","attrs":{"href":"http://javakk.com/redirect/aHR0cHM6Ly9wcm9qZWN0cmVhY3Rvci5pby8=","title":null},"content":[{"type":"text","text":"https://projectreactor.io/","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"RxJava:","attrs":{}},{"type":"link","attrs":{"href":"http://javakk.com/redirect/aHR0cDovL3JlYWN0aXZleC5pby8=","title":null},"content":[{"type":"text","text":"http://reactivex.io/","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"openjdk loom:","attrs":{}},{"type":"link","attrs":{"href":"http://javakk.com/redirect/aHR0cDovL2pkay5qYXZhLm5ldC9sb29tLw==","title":null},"content":[{"type":"text","text":"http://jdk.java.net/loom/","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"wiki:","attrs":{}},{"type":"link","attrs":{"href":"http://javakk.com/redirect/aHR0cHM6Ly93aWtpLm9wZW5qZGsuamF2YS5uZXQvZGlzcGxheS9sb29tL01haW4=","title":null},"content":[{"type":"text","text":"https://wiki.openjdk.java.net/display/loom/Main","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"github:","attrs":{}},{"type":"link","attrs":{"href":"http://javakk.com/redirect/aHR0cHM6Ly9naXRodWIuY29tL29wZW5qZGsvbG9vbQ==","title":null},"content":[{"type":"text","text":"https://github.com/openjdk/loom","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"阿里 jdk:","attrs":{}},{"type":"link","attrs":{"href":"http://javakk.com/redirect/aHR0cHM6Ly9naXRodWIuY29tL2FsaWJhYmEvZHJhZ29ud2VsbDg=","title":null},"content":[{"type":"text","text":"https://github.com/alibaba/dragonwell8","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章