1. 程式人生 > >雲原生實踐之 RSocket 從入門到落地:Servlet vs RSocket

雲原生實踐之 RSocket 從入門到落地:Servlet vs RSocket

實踐 連接 1.0 英文 mvc 調用 star 只需要 class

技術實踐的作用在於:除了用於構建業務,也是為了驗證某項技術或框架是否值得大規模推廣。

本期開始,我們推出《RSocket 從入門到落地》系列文章,通過實例和對比來介紹RSocket。主要圍繞RSocket如何實現Polyglot RPC、Service Registry、 Service Discovery、 IoT聯結等維度,為讀者們揭開RSocket的面紗,希望對大家在Java API規範的技術選型過程中有所借鑒。

第一篇文章我們將通過Servlet和RSocket的對比,快速了解RSocket的一些基本知識。要說明的是其實RSocket與Servlet並不是同類的產品,但是大家對Servlet都很熟悉,功能對比相對方便一些。

閱讀本系列文章,需要大家對Java有了解,其中可能會涉及到Kotlin,有少部分C++和Python(不做要求),如果了解Spring Boot則最好。

什麽是 Servlet ?
維基百科上的解釋是"Servlet,全稱Java Servlet,是用Java編寫的服務器端程序。 其主要功能在於交互式地瀏覽和修改數據,生成動態Web內容”。

對於Java程序員來說,解釋這個概念直接上代碼,這樣才能方便理解,如下:

public abstract class HttpServlet extends Servlet {
protected abstract void doGet(HttpServletRequest req,HttpServletResponse resp)

throws ServletException, IOException;

protected abstract void doPost(HttpServletRequest req,HttpServletResponse resp)
   throws ServletException, IOException;

}
所以,Servlet就是提供HTTP Request,處理後,最終調用HTTP Response完成輸出。沒錯,就是這個,大家可別小瞧這個class,幾乎所有符合Servlet規範的web框架的第一個Java類都是從這裏開始的,包括Struts、Spring MVC和阿裏巴巴內部用到的WebX。很多開發者根據這個class寫了Web Framework,來解決不同的問題。

什麽是 RSocket
rsocket.io給出的解釋是"RSocket是一個二進制的協議,以異步消息的方式提供4種對等的交互模型,以字節流的方式運行在TCP, WebSockets, Aeron等傳輸層之上”。

通過這個定義,大家可以有一個基本理解:二進制協議、異步消息、七層協議和運行在TCP、WebSocket以及Aeron之上。同樣的,我們通過代碼來解釋這個概念,如下:

public interface RSocket extends Availability, Closeable {

Mono<Payload> requestResponse(Payload payload);

Mono<Void> fireAndForget(Payload payload);

Flux<Payload> requestStream(Payload payload);

Flux<Payload> requestChannel(Publisher<Payload> payloads);

Mono<Void> metadataPush(Payload payload);

default double availability() {
return isDisposed() ? 0.0 : 1.0;
}
展開闡述一下:

四個模型:

requestResponse、fireAndForget、requestStream和requestChannel,它們和doGet、doPost沒有區別。

參數:

Payload,前面說到基於消息通訊,那就是拿到消息返回消息,Got!等一下,為何不叫Message?請原諒我們的英文水平,暫時可以理解為同義詞吧。對於一個消息來說,由兩部分組成,原信息(metadata)和數據(data)。原信息是指路由信息等,例如要調用那個服務,你的數據的mime type是什麽,數據則是指調用的參數值和返回的結果。

metadataPush:

這個是什麽?推送元信息的,可以告訴對方的一些元信息,至於是什麽,可以自己定義。我理解為:如果是一個集群,我可以將集群的信息給你,然後讓你和各個work node連接;我要下線啦,大家做好準備等等。

availability:

為何要這個? 這個可以理解問健康度檢查,如果為0,則表示不可用,這在load balance的情況下非常實用。Servlet缺少這個,所以我們要自行加入Health URL等,如/ok.jsp :) 那為何不是布爾值,true或者false?僅是個人理解:double值可以作為權重,如1.0表示處理能力非常好,0.8一般,這個就看你如何處理了。

Mono和Flux:

這是Reactive編程要求,通過異步的方式來提升系統的處理能力。RSocket定義中有一個異步關鍵字,Mono和Flux就是來處理異步的。

Servlet 和 RSocket的區別
其實兩者的共同點非常明顯:Servlet是一套Java的API規範,基於HTTP協議之上;RSocket也是一套API規範(支持多種語言),基於自定義的二進制協議之上。 可以不用關心協議的細節,直接實現接口寫代碼就可以,然後功能就會Ready。 這裏我們還是想列舉一下它們兩者之間的重大區別:

協議層:

Servlet是基於HTTP協議的,RSocket則是自定義協議。 標準化方面,HTTP尚不用說。 但是RSocket的自定義二進制協議性能非常好,解析方便。如果覺得HTTP非常簡單,那是1.1,2.0版本開始是有點復雜的。這裏我們可以理解為:RSocket定位高性能通訊,比HTTP高非常多(號稱10倍)。這裏要註意的是:RSocket並不是天然的極致高性能,要實現極致高性能需要根據自己業務場景優化才行。

指令和通訊模式:

HTTP的指令不只是get和post,其他還有head、put、delete和options等。Servlet2.0添加了流式的支持,但是這些指令都是為瀏覽器設計的,並非為服務通訊設計的,而且它們都是request/response模式,所以也叫做 request command。其他例如流式推送、fireAndForget和雙向通訊,Servlet2.0都不支持。基本上,我們可以將HTTP定位為request/response這一種通訊模式。這個說法也許有爭議,因為HTTP也有polling和websocket等,但是這些設計都是為了hack和高效通訊的改造,而不是內置的通訊模式。

message:

HTTP1.1是基於文本的通訊,2.0是基於message的。 message的好處是什麽呢?基於message的好處是異步化。message都必須有一個ID,這個消息發送出去後,就不用等立即返回,可以繼續發其他message,收到message後,再根據返回的message ID和之前的發出去的message ID進行匹配。如果不是message,內容發出去後,就要等著返回的結果進行匹配,然後才能發下一個message,這也是為何很多人抱怨www是World Wide Wait。

Reactive編程模型:

RSocket要求基於Reactive編程模型,對Java來說,主要是Reactor和RxJava,由於Spring在RSocket上貢獻頗多,外加RSocket Java SDK還要基於Netty-Reactor,所以默認的接口就是Reactor API。異步化對編程確實比較有挑戰,如callback、Future和Promise等,對比傳統不是那麽友好,所以Reactive在傳統和異步化上推出了Reactive編程模型,算是兼顧,這個看大家如何理解,如果對Functional Programming也能接受的話,那Reactive就沒有問題。

對等通訊:

我們傳統的理解是Client -> Server模式,例如寫一個Servlet運行在服務端的,然後再用JS寫一個Servlet運行在瀏覽器端,這樣服務端可以反向調用瀏覽器,例如訂單狀態變更時,需要將詳情區域刷新一下。但是RSocket沒有這個概念,大家的地位是對等的,都可以在server端,我調用你的服務,你也可以調用我的服務。後續我們會有詳細的Demo來介紹這個使用場景,如無監聽端口對外提供服務,從互聯網反向訪問內部服務。RSocket Broker就是基於這種對等通訊來實現的。

Singleton & Prototype scope:

這裏我們套用Spring的Singleton scope和Prototype scope來看Servlet和RSocket的不同。 Singleton scope表示JVM唯一,而Prototype scope是每次調用都需要創建。類比而言,Servlet的class基本都是singleton的,但是RSocket確未必,主要原因是前面說到的對等通訊,如果要給連接的另一方發送請求,就需要hold住連接的另一方(peer RSocket),所以這個handler就不能singleton的,如果只是單方通訊,不用在乎setup payload,那麽RSocket的handler為Singleton也沒有關系。

當然還有一項細小差別,這些就不做介紹了。鑒於個人能力,可能我理解的不夠徹底,漏掉了重大的區別,大家理解和使用後,歡迎反饋一下。我們再通過圖例來對比下兩者的不同:
技術分享圖片

RSocket Demo
這裏我們將RSocket的Demo介紹一下。由於沒有client -> server這種通訊模型,所以我們用requester和responder來說明,但是角色也是互換的,requester可以為responder,在實際的編碼過程中,其實就將requester默認調整為responder。

Responder代碼:

RSocketFactory.receive()
.acceptor(new SocketAcceptor() {@Override
br/>@Override
return Mono.just(new RSocketHandlerImpl());
}
})
.transport(TcpServerTransport.create("0.0.0.0", 42252))
.start()
.subscribe();
Responder主要是RSocketFactory.receive(),接收外部來的連接。接下來你只需要一個RSocket的接口實現給acceptor就可以了。 這裏說明一下SocketAcceptor接口。對於Responder來說,它需要驗證requester能否可以連接到自己,這個非常有用,如初始鑒權等,一旦鑒權通過,連接建立好後,後續就不需要驗證了。這裏的ConnectionSetupPayload是requester發給responder的創建連接的數據。這個是Servlet中沒有的,後續我們還會提供更多的實踐,第一篇文章裏僅驗證是否可以連接。

Requester代碼:

RSocketFactory.connect()
.acceptor(new Function<RSocket, RSocket>() {@Override
br/>@Override
return Mono.just(new RSocketHandlerImpl()) ;
}
})
.transport(TcpClientTransport.create("localhost", 42252))
.start()
.block();
RSocketFactory.connect() 是表示要連接到目標的responder上,然後也有RSocket實現給acceptor表示接收從對方過來的調用請求。 最好的block()表示采用同步方式等待responder返回,這個是需要的,如目標服務宕機或者不存在等,應用可以快速自我發現。 但是在load balance的情況下,我們未采用block這種方式,而是使用Mono方式,這樣可以實現自動重連,新地址推送等。

實際上,如果使用Spring Boot,可能就需要1-2個Bean,SocketAcceptor和RSocket Bean,其他都是通過註入方式完成,不需要寫很多重復代碼。 目前rsocket-spring-boot-starter已經開發快完成了,所以不用擔心代碼的復雜性。

雲原生實踐之 RSocket 從入門到落地:Servlet vs RSocket