作者簡介
劉誠,攜程酒店研發性能架構師。2014年加入攜程,緻力于通過架構的演進,控制企業硬體成本。
一、初識RSocket
在QCon2019北京大會上第一次得知RSocket。印象深刻的是Netifi公司通過他們研發的RSocket幫助企業實作微服務,在40,000RPS的場景下,Istio需要每月3495美金,而Netifi每月隻要388美金,同時性能提升10倍,這無疑對任何企業都是極具吸引力的。
Netiffi的創始人在會上也談到,使用Netiffi的Broker,得益于RSocket協定,無需獨立部署監控、服務發現、健康檢查、負載均衡等等中間件。如果是跨雲部署,例如谷歌雲與亞馬遜雲之間,或者亞馬遜雲與企業本地資料中心,都隻要通過Netiffi的Broker即可無縫溝通,無需處理複雜的适配問題。
回來查詢了不算豐富的資料後發現,Istio的技術專家發文稱RSocket Broker的service mesh比Istio有将近10倍的速度提升。考慮到Istio專家的觀點還有一定說服力的,那麼RSocket真的有那麼厲害?
二、RSocket生産實踐
我們決定到生産上面去實踐RSocket,看看性能到底如何。現在已經支援RSocket的service架構有Spring Flux:

Dubbo3.0 snapshot
壓測對比的是Dubbo2.7。Dubbo2.7的樣例代碼如下:
Dubbo3的樣例代碼如下:
SpringFlux的樣例代碼如下:
分别壓測QPS200,400和800的情況,結果顯示CPU、記憶體和響應時間基本一緻。是不是壓測方式有問題,為什麼性能一點也沒有提高?
三、RSocket的定義
在解答上面的問題之前,我們先來看看RSocket到底是什麼?
官方定義:RSocket是基于reactive stream flow control的雙向的、多路的、基于消息的、二進制通訊協定。它提供了4種互動模式:
- request - response:一個請求,一個響應。現在的Restful服務既是如此;
- fire-and-forget:對于那些不關心結果的請求,直接傳回;
- request – stream:一個請求,多次結果傳回;
- channel:伺服器可以發多個請求給用戶端,用戶端也可以發多個結果給伺服器;
幾個特點:
- 可取消:請求和響應都可取消,能夠高效的清理系統資源;
- 可中斷後繼續:如果被調用方卡住了,請求方可斷開後,過一會再過來重試;
- 可租賃:響應者可根據自己的實際情況來控制調用方的頻率,其實就是響應式程式設計中的背壓的實作;
上面隻是定義了RSocket協定,在具體的實作上面是非常靈活的。
進一步查詢資料後發現,現有JAVA的RSocket實作一般都是基于TCP長連接配接。熟悉Dubbo的朋友,立刻就會想到Dubbo不就是基于TCP長連接配接進行服務調用的麼。是的,但是不同之處在于RSocket是一系列的協定規範,原先的Dubbo雖然也是基于TCP長連接配接實作的,但是并沒有完全按照RSocket的規範來進行實作。
更加确切的來說,那個時候應該還沒有RSocket。這個也就幫助我們了解為什麼Dubbo3開始接入RSocket,以及阿裡為什麼也是RSocket的擁護者之一。至此就能了解為什麼性能沒提高了,在我們實踐的場景中,隻是把原來基于HTTP的請求方式變成了基于TCP的實作。就生産結果而言,并沒有性能大幅提升,更别提10倍的提升。
那RSocket隻有TCP長連接配接的優勢?
四、RSocket協定的業務開發優勢
作為一名一線業務開發者,可能更關心的是使用RSocket協定寫業務代碼時的優劣勢。就我個人而言,感覺還是很棒的。例如下面這個傳入參數為Mono,傳回也為Mono類型的接口定義方式。
熟悉響應式程式設計的同學應該知道Mono是Pivotal Reactor Core中的一種類型。是一種特殊的釋出者,最多隻釋出一次。
如果應用本身就是以非堵塞的方式寫的,那這裡就可以直接使用reactor core的所有API。當然也有同學會說,即使不傳回Mono,例如Dubbo2.7中的傳回CompletableFuture,我們隻要自己内部轉換一下即可。然後應用裡面照樣可以使用reactor core或者rxjava等響應式程式設計的架構。
的确如此,但是如果是Flux呢?可以多次、不斷地往流裡面寫入結果的呢?CompletableFuture還能支援麼?顯然不行。因為flux本身就定義為最多可以釋出n次。
那為什麼要用flux?
flux是響應式程式設計中的一種常用對象,其實就是request -> stream一種實作。一個請求發起後,結果可以分批寫回。好處是什麼?例如:A服務調用B服務,B服務調用C和D服務,但是D服務很慢,如果是request -> response模式,那必須要等到C和D完成後,才能傳回結果給A。如果是Request -> stream模式,則可以先把C的結果傳回給A,然後等D的結果拿到了,在傳回給A。這樣就可以高效的利用系統資源,減少等待。
熟悉Dubbo的同學可能會說,request -> stream這種模式也不是RSocket獨有的吧,例如Dubbo就可以使用下面的方式來實作:
看完上面的代碼,然後我們可以思考一下如何用上面提供的API去實作下面的功能。你就會發現flux的這種Reactive Functional Programming的程式設計方式大大降低了程式設計的難度與代碼量,提升了代碼的可讀性:
但是這還不是全部的好處,下面我們來看看RSocket的另外一種使用場景。
五、RSocket的展望
響應式程式設計中有一個比較有名的功能叫背壓。例如:當上遊服務調用下遊服務,而下遊服務來不及處理的時候,可以選擇性的限制上遊服務的調用。
而我們日常在刷手機的時候,經常會由于手機卡頓,無論是APP導緻的還是網絡導緻的,重複點選或者重新整理頁面的情況。而HTTP本身是無狀态的,是以隻要有請求,無論是有效的還是無效的,伺服器都會進行處理直到完成。
但是如果有背壓,那我們就可以一定程度上減少APP的無效和重複的請求。
例如:使用者檢視訂單清單,如果一下子過來10個請求,其實隻要傳回最後一個即可,前面9個都可以忽略。如果實作了,伺服器就會減少流量,對硬體成本的控制有着非常積極的作用。InfoQ的文章中就提到:Facebook的工程師現在就是這樣實作APP與伺服器之間的通訊。
之是以稱之為展望,因為這個也是我們下一個實踐的目标。
參考資料
- https://qconsp.com/system/files/presentation-slides/rsocket_and_spring_cloud_gateway-spencer.gibb_.pdf
- https://projectreactor.io/docs/core/release/reference/
- https://www.infoq.com/news/2018/10/rsocket-facebook/