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. Java SDK 版本 8 或更高(要检查,请在终端中使用 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 的更容易使用一些。在终端中,按如下方式下载 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 控制器,使用/hello这样的 URL 路径映射将 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://localhost: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://localhost:7000,服务器正在该地址等待。

第一个消息帧中发送了消息路由指令。此路由指令使用 CLI 客户端的--route选项设置,并设置为request-response。Spring 使用此路由信息来选择要调用的正确@MessageMapping端点,在本例中是requestResponse(Message request)方法。该方法然后返回自己的消息作为响应。CLI 客户端将整个交互过程以消息帧序列的形式打印在终端窗口中。

总结

如果您跟着操作,您会看到使用 Spring Boot 编写一个简单的 RSocket 服务器是多么容易。您检查了所需的 Java 代码,然后在本地启动了 Spring Boot 服务器。然后您向 RSocket 服务器发送了一条消息并观察了响应。您还学习了如何在 Spring 中使用 RSocket 消息路由功能来路由您的 RSocket 消息。在下一篇文章中,您将学习如何使用 Spring Boot 开始构建您自己的 RSocket 客户端

获取 Spring 邮件列表

订阅 Spring 邮件列表,保持联系

订阅

领先一步

VMware 提供培训和认证,助您快速进步。

了解更多

获取支持

Tanzu Spring 通过一个简单的订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举办的活动

查看 Spring 社区所有即将举办的活动。

查看全部