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

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

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

第一篇文章咱們將經過Servlet和RSocket的對比,快速瞭解RSocket的一些基本知識。要說明的是其實RSocket與Servlet並非同類的產品,可是你們對Servlet都很熟悉,功能對比相對方便一些。node

閱讀本系列文章,須要你們對Java有了解,其中可能會涉及到Kotlin,有少部分C++和Python(不作要求),若是瞭解Spring Boot則最好。程序員

什麼是 Servlet ?

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

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

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; } 

展開闡述一下:websocket

四個模型:

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也沒有關係。

固然還有一項細小差異,這些就不作介紹了。鑑於我的能力,可能我理解的不夠完全,漏掉了重大的區別,你們理解和使用後,歡迎反饋一下。咱們再經過圖例來對比下二者的不一樣:
ce359da7368a27faa67bd531492c6bc4

RSocket Demo

這裏咱們將RSocket的Demo介紹一下。因爲沒有client -> server這種通信模型,因此咱們用requester和responder來講明,可是角色也是互換的,requester能夠爲responder,在實際的編碼過程當中,其實就將requester默認調整爲responder。

Responder代碼:

RSocketFactory.receive()
            .acceptor(new SocketAcceptor() { @Override public Mono<RSocket> accept(ConnectionSetupPayload setup, RSocket sendingSocket) { 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 public RSocket apply(RSocket peerRsocket) { 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已經開發快完成了,因此不用擔憂代碼的複雜性。

 


原文連接本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索