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 就是建立在这种基本交互之上的,而且它非常常见。

在本教程中,你将学习如何使用 Spring Boot 作为服务器,终端应用程序作为客户端,通过 RSocket 进行请求-响应。

请求-响应只是 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 的适用于 Linux 的 Windows 子系统。Microsoft 关于如何执行此操作的说明在此

现在,将下载的项目文件夹设为终端中的当前目录

cd spring-rsocket-demo

接下来,将由 Toshiaki Maki 编写的优秀 RSocket Client CLI 下载到你克隆或解压的代码的 rsocket-server 文件夹中。还有一个官方的 RSocket CLI 在其他地方,但 Toshiaki 的 CLI 更易于使用。在终端中,按如下方式下载 JAR 文件:

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

你稍后将使用此客户端与 RSocket 服务器通信,但现在,通过调用帮助命令来测试它是否正常工作,如下所示:

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") 注解。这个注解声明了任何包含 RSocket 路由 request-response 元数据的消息都应该由这个方法处理。你将在稍后从客户端发送请求消息时使用这个路由。

你是否注意到这与 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://: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://: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 社区所有即将举行的活动。

查看所有