徹底搞懂 RSocket 協(xié)議
我們知道,常見(jiàn)的 RESTful Web 服務(wù)采用的都是基于 HTTP 協(xié)議實(shí)現(xiàn)的請(qǐng)求 - 響應(yīng)式交互方式。這種交互方案很簡(jiǎn)單,但是因?yàn)橹恢С謫我坏慕换シ绞剑簿蜔o(wú)法應(yīng)對(duì)所有日常開(kāi)發(fā)需求場(chǎng)景,例如服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù)。
請(qǐng)求 - 響應(yīng)模式
那么,能不能在網(wǎng)絡(luò)協(xié)議層上具備更加豐富的交互方式呢?答案是肯定的,這就是我們今天要討論的 RSocket 協(xié)議。RSocket 協(xié)議提供了多種客戶端和服務(wù)端之間的交互方式,它并不是對(duì) HTTP 協(xié)議的補(bǔ)充,而是一個(gè)基于響應(yīng)式編程技術(shù)的全新的、高性能網(wǎng)絡(luò)通訊協(xié)議。
在引入 RSocket 協(xié)議之前,你必須要了解為什么需要這樣一個(gè)協(xié)議,讓我們從傳統(tǒng)的請(qǐng)求 - 響應(yīng)模式所存在的問(wèn)題開(kāi)始說(shuō)起。
RSocket 協(xié)議解決了什么問(wèn)題?
請(qǐng)求 - 響應(yīng)模式的問(wèn)題
請(qǐng)求 - 響應(yīng)模式的一個(gè)問(wèn)題是,高并發(fā)場(chǎng)景下,性能和響應(yīng)性上存在瓶頸。因?yàn)檎麄€(gè)處理過(guò)程是同步阻塞的,如果某個(gè)請(qǐng)求的響應(yīng)時(shí)間過(guò)長(zhǎng),會(huì)導(dǎo)致其他請(qǐng)求無(wú)法及時(shí)響應(yīng)。這一點(diǎn)我之前在之前的響應(yīng)式編程一課中有詳細(xì)講解。
更重要的是,對(duì)很多應(yīng)用場(chǎng)景來(lái)說(shuō),HTTP 協(xié)議提供的請(qǐng)求 - 響應(yīng)模式是不合適的。典型的例子是消息推送,如果客戶端需要獲取最新的推送消息,就必須使用輪詢,客戶端不停的發(fā)送請(qǐng)求到服務(wù)器來(lái)檢查更新,無(wú)疑這會(huì)造成了大量資源浪費(fèi)。
客戶端輪詢模式
你可能會(huì)說(shuō),我們可以使用服務(wù)器發(fā)送事件(Server-Sent Events,SSE)技術(shù)實(shí)現(xiàn)從服務(wù)端向客戶端推送消息。不過(guò),SSE 也是一個(gè)構(gòu)建在 HTTP 協(xié)議上的處理機(jī)制,一般只用來(lái)傳送文本,提供的功能非常有限。
服務(wù)器發(fā)送事件
幸運(yùn)的是,業(yè)界認(rèn)識(shí)到了異步、多向交互通信的必要性。在 2015 年,RSocket 協(xié)議就在這樣的背景下誕生了。
RSocket 協(xié)議的解決方案
RSocket 是一種語(yǔ)言無(wú)關(guān)的二進(jìn)制網(wǎng)絡(luò)協(xié)議,用來(lái)解決現(xiàn)有網(wǎng)絡(luò)傳輸協(xié)議存在的單一請(qǐng)求 - 響應(yīng)模式以及性能問(wèn)題。那么,它是怎么解決這個(gè)問(wèn)題的呢?
RSocket 以異步消息的方式提供 4 種交互模式,除了請(qǐng)求 - 響應(yīng)(request/response)模式之外,還包括請(qǐng)求 - 響應(yīng)流(request/stream)、即發(fā)即棄(fire-and-forget)和通道(channel)這三種新的交互模式。
RSocket 協(xié)議的四種交互模式
我們來(lái)看這四種交互模式的特點(diǎn):
請(qǐng)求 - 響應(yīng)模式:這是最典型也最常見(jiàn)的模式。發(fā)送方在發(fā)送消息給接收方之后,等待與之對(duì)應(yīng)的響應(yīng)消息
請(qǐng)求 - 響應(yīng)流模式:發(fā)送方的每個(gè)請(qǐng)求消息,都對(duì)應(yīng)接收方的一個(gè)消息流作為響應(yīng)
即發(fā)即忘模式:發(fā)送方的請(qǐng)求消息沒(méi)有與之對(duì)應(yīng)的響應(yīng)
通道模式:在發(fā)送方和接收方之間建立一個(gè)雙向傳輸?shù)耐ǖ?/span>
我們可以從請(qǐng)求與響應(yīng)的數(shù)量對(duì)應(yīng)關(guān)系來(lái)對(duì)上述四種交互模式做一個(gè)總結(jié)。
可以看到,當(dāng)我們選擇具體的交互模式時(shí),請(qǐng)求 - 響應(yīng)、請(qǐng)求 - 響應(yīng)流和即發(fā)即忘這三種交互模式的請(qǐng)求數(shù)量都是 1,并能夠獲取不同數(shù)量的響應(yīng)結(jié)果。我們就可以根據(jù)這一特性來(lái)重構(gòu)現(xiàn)有的請(qǐng)求處理過(guò)程。而通道模式則比較特殊,它的請(qǐng)求數(shù)量和響應(yīng)數(shù)量都是 N,決定了我們可以選擇它來(lái)應(yīng)對(duì)雙向的數(shù)據(jù)流處理場(chǎng)景。
RSocket 協(xié)議專門(mén)設(shè)計(jì)用來(lái)和響應(yīng)式編程技術(shù)風(fēng)格的應(yīng)用程序配合使用,在使用 RSocket 協(xié)議時(shí),響應(yīng)式編程體系中的數(shù)據(jù)流機(jī)制仍然有效。
為了更好的理解 RSocket 協(xié)議,我們對(duì)比它和 HTTP 協(xié)議。在交互模式上,與 HTTP 的請(qǐng)求 - 響應(yīng)這種單向交互模式不同,RSocket 倡導(dǎo)的是對(duì)等通信。這種對(duì)等通信不是傳統(tǒng)單向交互模式的改進(jìn),而是在客戶端和服務(wù)端之間可以自由的相互發(fā)送和處理請(qǐng)求。
RSocket 協(xié)議的交互方式
另一方面,從性能上講,我們知道 HTTP 協(xié)議為了兼容各種應(yīng)用方式,本身有一定的復(fù)雜性和冗余性,性能一般。而 RSocket 采用的是自定義二進(jìn)制協(xié)議,本身的定位就是高性能通訊協(xié)議,性能上比 HTTP 高出一個(gè)數(shù)量級(jí)。
如何正確使用 RSocket 協(xié)議?
到這里,我們就明白了 RSocket 協(xié)議是要解決什么問(wèn)題,以及是如何解決的了。接下來(lái),我們具體看看如何正確使用 RSocket 協(xié)議。
RSocket 接口
我們先來(lái)看一下 RSocket 協(xié)議中最核心的接口,即 RSocket 接口的定義,如下所示。
public interface RSocket extends Availability, Closeable {
//推送元信息,數(shù)據(jù)可以自定義
Mono<Void> metadataPush(Payload payload);
//請(qǐng)求-響應(yīng)模式,發(fā)送一 個(gè)請(qǐng)求并接收一個(gè)響應(yīng)
Mono<Payload> requestResponse(Payload payload);
//即發(fā)-即忘模式,請(qǐng)求-響應(yīng)的優(yōu)化,在不需要響應(yīng)時(shí)非常有用
Mono<Void> fireAndForget(Payload payload);
//請(qǐng)求-響應(yīng)流模式,類似于返回集合的請(qǐng)求/響應(yīng),集合將以流的方式返回,而不是等到查詢完成
Flux<Payload> requestStream(Payload payload);
//通道模式,允許任意交互模型的雙向消息流
Flux<Payload> requestChannel(Publisher<Payload> payloads);
}
顯然,RSocket 接口通過(guò)四個(gè)方法分別實(shí)現(xiàn)了它所提供的四種交互模式,這里的 Payload 代表的就是一種消息對(duì)象,由兩部分組成,即元信息 metadata 和數(shù)據(jù) data,類似于常見(jiàn)的消息通信中的消息頭和消息體的概念。
同時(shí),你注意到嗎,這里出現(xiàn)了兩個(gè)新的數(shù)據(jù)結(jié)構(gòu) Mono 和 Flux。在響應(yīng)式編程中,Mono 代表只包含 0 個(gè)或 1 個(gè)元素的數(shù)據(jù)流,而對(duì)應(yīng)的 Flux 則是一個(gè)包含 0 到 n 個(gè)元素的數(shù)據(jù)流。
所以 fireAndForget() 方法返回的是一個(gè) Mono 流,符合即發(fā) - 即棄模式的語(yǔ)義。而 requestStream() 作為請(qǐng)求 - 響應(yīng)流模式的實(shí)現(xiàn),與 requestResponse() 的區(qū)別在于它的返回值是一個(gè) Flux 流,而不是一個(gè) Mono 對(duì)象。而且,RSocket 提供的請(qǐng)求 - 響應(yīng)模式也比 HTTP 更具優(yōu)勢(shì),因?yàn)樗钱惒角叶嗦窂?fù)用的。
另一方面,與其他方法不同,requestChannel() 方法返回的并不是一個(gè) Payload 消息對(duì)象,而是一個(gè)代表響應(yīng)式流的 Publisher 對(duì)象,意味著這種模式下的輸入輸出都是響應(yīng)式流,也就是說(shuō)可以實(shí)現(xiàn)客戶端和服務(wù)端之間的雙向交互,這也和通道模式的定義一致。
RSocket 與框架集成
RSocket 接口過(guò)于底層,開(kāi)發(fā)人員需要考慮服務(wù)器端和客戶端的具體構(gòu)建方式以及手工實(shí)現(xiàn)遠(yuǎn)程調(diào)用的細(xì)節(jié)。所以,我通常不建議直接使用 RSocket 接口進(jìn)行應(yīng)用程序的開(kāi)發(fā),而是傾向于借助特定的開(kāi)發(fā)框架。如果你使用的是 Spring Boot 框架,就可以構(gòu)建如下所示一個(gè)簡(jiǎn)單 Controller:
@Controller
public class HelloController {
@MessageMapping("hello")
public Mono<String> hello(String name) {
return Mono.just("Hello: " + name);
}
}
這里引入了一個(gè)新的注解@MessageMapping,類似 Spring MVC 中的@RequestMapping 注解,@MessageMapping 是,Spring 中提供,用來(lái)指定 RSocket 協(xié)議中消息處理的目的地。然后,我們輸入了一個(gè) String 類型的參數(shù)并返回一個(gè) Mono 對(duì)象,符合請(qǐng)求 - 響應(yīng)交互模式的定義。
為了訪問(wèn)這個(gè) RSocket 端點(diǎn),我們需要構(gòu)建一個(gè) RSocketRequester 請(qǐng)求對(duì)象。基于該對(duì)象,我們就可以通過(guò)它的 route() 方法路由到前面通過(guò)@MessageMapping 注解構(gòu)建的"hello"端點(diǎn),如下所示:
Mono<String> response = requester.route("hello")
.data("Geektime")
.retrieveMono(String.class);
我們?cè)賮?lái)看一個(gè)請(qǐng)求 - 響應(yīng)流的示例,如下所示:
@MessageMapping("stream")
Flux<Message> stream(Request request) {
return Flux
.interval(Duration.ofSeconds(1))
.map(index -> new Message(request.getParam, index));
}
這里我們根據(jù)輸入的 Request 對(duì)象,返回一個(gè) Flux 流,每一秒發(fā)送一個(gè)新的 Message 對(duì)象。
如果你想在其他框架中使用 RSocket 協(xié)議,也有很多選擇。Dubbo 在 3.0.0-SNAPSHOT 版本里基于 RSocket 對(duì)響應(yīng)式編程提供了支持,開(kāi)發(fā)人員可以非常方便的使用 RSocket 的 API。而隨著 Spring 框架的持續(xù)升級(jí),5.2 版本中也把 RSocket 作為缺省的網(wǎng)絡(luò)通信協(xié)議。
目前,RSocket 協(xié)議的應(yīng)用已經(jīng)越來(lái)越廣泛。相信隨著這項(xiàng)技術(shù)的不斷成熟,日常開(kāi)發(fā)過(guò)程中也會(huì)出現(xiàn)更多的應(yīng)用場(chǎng)景和解決方案。
總結(jié)
今天我們系統(tǒng)討論了 RSocket 這款新的高性能網(wǎng)絡(luò)通信協(xié)議。與 HTTP 協(xié)議相比,RSocket 提供了四種不同的交互模式來(lái)實(shí)現(xiàn)多樣化的網(wǎng)絡(luò)通信。同時(shí),RSocket 也無(wú)縫集成了響應(yīng)式編程技術(shù),我們可以通過(guò) RSocket 協(xié)議來(lái)實(shí)現(xiàn)異步、非阻塞式的網(wǎng)絡(luò)通信。