RSocket 入門指南:Spring Boot 伺服器

工程 | Ben Wilcock | 2020 年 3 月 2 日 | ...

時間:約 15 分鐘。

在多樣化的微服務世界中,HTTP 是代理程式對代理程式通訊中無可爭議的領導者。它成熟、完善且無處不在。但在某些情況下,HTTP 請求-回應可能會很麻煩。如果您需要超越傳統請求-回應的通訊模式,例如單向發送或串流處理,該怎麼辦?如果您想要在任一方向發送訊息,又該怎麼辦?

使用 HTTP,有一些方法可以實現這一點,但這並不是該協定設計的目的。許多解決方案都帶有額外的權衡或缺點。此外,沒有規定說「你應該總是使用 HTTP」,像 AMQP 這樣的訊息協定已經證明了這一點。因此,了解您的選擇是好的,並且偶爾在您的列表中添加一些新技術也是健康的。這篇文章是關於其中一種替代方案——RSocket。

RSocket 是一種新的訊息協定,旨在解決一些常見的微服務通訊挑戰。使用 RSocket,您可以獲得一個靈活的協定,可在 TCP 或 WebSockets 上運作。這表示您可以進行二進位訊息傳輸而無需轉換。您可以使用多工處理、背壓、恢復和路由等現代控制功能,並且可以使用多種訊息傳輸模式,包括單向發送、請求-回應和串流處理。RSocket 也完全是反應式的,因此非常適合您的高吞吐量微服務應用程式。早期的採用者包括 Netflix、Pivotal、阿里巴巴和 Facebook——它們都是交付可擴展的網際網路服務的專家。

在這篇文章(本系列的第一篇)中,您將學習如何開始使用 RSocket。您將熟悉它的運作方式,並體驗它的一些強大功能。在本系列結束時,您將把 RSocket 添加到您的技能組合中,以便下次您在考慮選項時,您可以多一個協定可供選擇。

開始吧...

由於請求-回應是大多數 Web 開發人員熟悉的領域,我們將從這種模式開始我們的 RSocket 之旅。請求-回應的語意非常簡單,您發送一個請求,您會收到一個回應。HTTP 是建立在這種基本互動之上的,而且非常常見。

在本教學課程中,您將了解如何使用 RSocket 和 Spring Boot 作為您的伺服器,以及終端機應用程式作為您的用戶端來執行請求-回應。

請求-回應只是 Spring 和 RSocket 支援的四種互動模型之一。我們將在未來的文章中介紹其他模型。

當您按照以下步驟操作時,您會注意到使用 Spring Boot 建立 RSocket 伺服器所需的程式碼量非常少。程式碼已在此處提供給您,但如果您願意,您也可以在幾分鐘內從頭開始編寫程式碼。

步驟 1:設定您的環境

首先,檢查您是否已安裝以下先決條件

  1. 版本 8 或以上的 Java SDK(若要檢查,請在終端機中使用 java -version)
  2. 一個可運作的 Java IDE(我使用的是 IntelliJ IDEA)
  3. 一個包含已複製或解壓縮的示範程式碼範例的資料夾。
  4. Linux Bash/ZSH shell(如果您是 Windows 使用者,請查看下面的注意事項)

如果您是 Windows 使用者,請切換到 Microsoft 的 Windows Subsystem for Linux。Microsoft 關於如何執行此操作的說明請見此處

現在,將下載的專案資料夾設為終端機中的目前目錄

cd spring-rsocket-demo

接下來,將 Toshiaki Maki 開發的優秀 RSocket Client CLI 下載到您複製或解壓縮的程式碼的 rsocket-server 資料夾中。還有一個官方的 RSocket CLI 在其他地方,但 Toshiaki 的版本稍微容易使用一些。在終端機中,如下所示下載 JAR 檔案

cd rsocket-server
wget -O rsc.jar https://github.com/making/rsc/releases/download/0.4.2/rsc-0.4.2.jar

您稍後將使用此用戶端與 RSocket 伺服器通訊,但現在,請呼叫 help 命令來測試它是否正常運作,如下所示

java -jar rsc.jar --help

您應該會看到類似於以下的輸出(我已截斷),說明命令的用法和選項。

usage: rsc Uri [Options]

Non-option arguments:
[String: Uri]

Option                              Description
------                              -----------
--channel                           Shortcut of --im REQUEST_CHANNEL
-d, --data [String]                 Data. Use '-' to read data from

...

保持此終端機視窗開啟,您稍後會需要它。

步驟 2:檢查伺服器程式碼

在您的 IDE 中開啟 rsocket-server 專案並檢查程式碼。如您所見,在 Spring Boot 中啟動 RSocket 伺服器所需的程式碼非常少。以下是一些重點

專案檔案

在專案的 pom.xml 檔案中,您可以看到 Spring Boot RSocket 伺服器所需的 <dependencies>。使用 Spring Boot 版本 2.2.5.RELEASE 是因為在撰寫本文時,此版本具有最適合生產環境的 RSocket 功能。該專案還依賴 lombokspring-boot-starter-rsocket 程式庫。Lombok 為 Java 資料類別添加了建構函式、getter、setter 和 equals 方法,並且簡化了對記錄等內容的存取。適用於 RSocket 的 Spring Boot Starter 將 RSocket 與 Spring Boot 整合,並在執行階段自動為您配置一些 RSocket 基礎架構。

應用程式屬性

application.properties 檔案中,RSocket 伺服器的 TCP 連接埠設定為 7000,並且開啟了 Spring Boot 的延遲初始化功能。

spring.rsocket.server.port=7000
spring.main.lazy-initialization=true

訊息類別

第一個要更詳細查看的類別稱為 Message.java。這個 Lombok @Data 類別用於對用戶端和伺服器(或如果您願意,也可以稱為「請求者」和「回應者」)之間的請求和回應訊息進行建模。該類別如下所示…

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Message {
    private String origin;
    private String interaction;
    private long index;
    private long created = Instant.now().getEpochSecond();

    public Message(String origin, String interaction) {
        this.origin = origin;
        this.interaction = interaction;
        this.index = 0;
    }

    public Message(String origin, String interaction, long index) {
        this.origin = origin;
        this.interaction = interaction;
        this.index = index;
    }
}

使用此類別,您可以說明訊息來自何處(其 origin)、它打算使用的訊息傳輸樣式(interaction),以及訊息在訊息序列中的序號(其 index)。Lombok 透過提供建構函式、getter、setter、toString 和 hashcode 實作來簡化程式碼。

控制器類別

RSocket 伺服器控制器程式碼可以在 RSocketController.java 檔案中找到。這個類別被註解為 Spring @Controller,這基本上意味著它宣告了服務端點——在本例中是 RSocket 端點。

@Controller
public class RSocketController {

    @MessageMapping("request-response")
    Message requestResponse(Message request) {
            log.info("Received request-response request: {}", request);
            // create a single Message and return it
            return new Message(SERVER, RESPONSE);
    }
}

在這個類別中,有一個名為 requestResponse() 的方法,它接受單個 Message 物件(請求)並傳回單個 Message 物件(回應)。

您會注意到這個 requestResponse() 方法使用 @MessageMapping("request-response") 註解進行裝飾。此註解宣告,任何具有包含 request-response 的 RSocket 路由的中繼資料的訊息都應由此方法處理。您稍後將在從用戶端發送請求訊息時使用此路由。

您是否注意到這與 Spring 的 REST 控制器略有不同?對於 REST 控制器,URL 路徑對應(如 /hello)用於將 HTTP 呼叫與其處理常式方法相關聯。

程式碼就到此為止。讓我們試試看。

步驟 3:啟動 Spring Boot RSocket 伺服器

保持您現有的終端機視窗開啟,在第二個終端機視窗中,將 rsocket-server 資料夾設為您的目前目錄。然後使用以下命令建置並執行 RSocket 伺服器

./mvnw clean package spring-boot:run -DskipTests=true

或者,如果您願意,可以使用 Java IDE 中的「建置」和「執行」命令。

步驟 4:使用 RSocket CLI 將命令傳送到伺服器

接下來,您將使用您在步驟 1 中下載和測試的 RSocket 用戶端 rsc.jar 將訊息傳送到正在執行的伺服器。回到您顯示 --help 文字的原始終端機視窗,然後發出以下命令

java -jar rsc.jar --debug --request --data "{\"origin\":\"Client\",\"interaction\":\"Request\"}" --route request-response tcp://127.0.0.1:7000

您會注意到該命令已宣告一個 RSocket 訊息路由(這是透過新增 --route 選項並為路由指定名稱來實現的)。在本例中,路由為 request-response,它與 RSocketController.java 中請求-回應處理常式方法中宣告的 @MessageMapping 相符。

當命令執行時,您將在終端機視窗中看到一些偵錯資訊,說明在請求-回應互動期間發生的情況。它看起來像這樣

2020-02-27 11:20:21.806 DEBUG --- [actor-tcp-nio-1] i.r.FrameLogger : sending ->
Frame => Stream ID: 1 Type: REQUEST_RESPONSE Flags: 0b100000000 Length: 69
Metadata:
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 10 72 65 71 75 65 73 74 2d 72 65 73 70 6f 6e 73 |.request-respons|
|00000010| 65                                              |e               |
+--------+-------------------------------------------------+----------------+
Data:
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 7b 22 6f 72 69 67 69 6e 22 3a 22 43 6c 69 65 6e |{"origin":"Clien|
|00000010| 74 22 2c 22 69 6e 74 65 72 61 63 74 69 6f 6e 22 |t","interaction"|
|00000020| 3a 22 52 65 71 75 65 73 74 22 7d                |:"Request"}     |
+--------+-------------------------------------------------+----------------+
2020-02-27 11:20:21.927 DEBUG --- [actor-tcp-nio-1] i.r.FrameLogger : receiving ->
Frame => Stream ID: 1 Type: NEXT_COMPLETE Flags: 0b1100000 Length: 81
Data:
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 7b 22 6f 72 69 67 69 6e 22 3a 22 53 65 72 76 65 |{"origin":"Serve|
|00000010| 72 22 2c 22 69 6e 74 65 72 61 63 74 69 6f 6e 22 |r","interaction"|
|00000020| 3a 22 52 65 73 70 6f 6e 73 65 22 2c 22 69 6e 64 |:"Response","ind|
|00000030| 65 78 22 3a 30 2c 22 63 72 65 61 74 65 64 22 3a |ex":0,"created":|
|00000040| 31 35 38 32 38 30 32 34 32 31 7d                |1582802421}     |
+--------+-------------------------------------------------+----------------+
{"origin":"Server","interaction":"Response","index":0,"created":1582802421}

您看到的偵錯輸出分為三個「訊息框架」。第一個訊息框架標記為 Metadata。在本例中,它顯示了傳送到伺服器的路由中繼資料 (request-response)。第二個框架顯示了用戶端正在傳送到伺服器的 Data 訊息(JSON 字串)。第三個框架顯示了伺服器傳回給用戶端的回應訊息(也是 JSON 字串)。

在最後一行,您可以看到伺服器傳回的 JSON 格式回應以隔離方式列印出來,確認我們的命令訊息已成功被伺服器接收和確認

{"origin":"Server","interaction":"Response","index":0,"created":1582802421}

恭喜!您已完成。您剛剛使用 RSocket 發送了一個請求-回應訊息。您現在可以透過在終端機視窗中按下 Ctrl-C 或關閉它來停止 RSocket 伺服器。如果您使用 IDE 執行 RSocket 伺服器,則可以透過一般方式在 IDE 中停止該程序。

運作方式

您下載的 RSocket rsc 用戶端使用 RSocket 訊息協定將請求訊息傳送到 RSocketController。訊息透過 TCP 傳送到伺服器正在等待的位址 tcp://127.0.0.1:7000

訊息路由指示在第一個訊息框架中發送。此路由指示是使用 CLI 用戶端的 --route 選項設定的,並設定為 request-response。Spring 使用此路由資訊來選取要呼叫的正確 @MessageMapping 端點,在本例中為 requestResponse(Message request) 方法。然後,該方法會使用自己的訊息回應。CLI 用戶端將整個互動作為一系列訊息框架列印在終端機視窗中。

總結

如果您一路跟隨,您就會看到使用 Spring Boot 編寫一個簡單的 RSocket 伺服器是多麼容易。您檢查了所需的 Java 程式碼,然後在本機啟動了 Spring Boot 伺服器。然後,您向 RSocket 伺服器發送了一條訊息並觀察了回應。您還學習了如何使用 RSocket 訊息路由功能在 Spring 中路由您的 RSocket 訊息。在下一篇文章中,您將學習如何開始使用 Spring Boot 建置您自己的 RSocket 用戶端

取得 Spring 電子報

保持與 Spring 電子報的連線

訂閱

領先一步

VMware 提供訓練和認證,以加速您的進展。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中為 OpenJDK™、Spring 和 Apache Tomcat® 提供支援和二進位檔案。

了解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

檢視全部