领先一步
VMware 提供培训和认证,助您加速进步。
了解更多时间:约 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 服务器所需的代码量非常小。代码已为你提供在此,但如果你愿意,也可以在几分钟内从头开始编写代码。
首先,检查你是否安装了以下必备条件:
如果你是 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
...
请保持此终端窗口打开,稍后会用到。
在你的 IDE 中打开 rsocket-server 项目并检查代码。如你所见,在 Spring Boot 中启动 RSocket 服务器所需的代码量非常少。以下是一些亮点:
在项目的 pom.xml 文件中,你可以看到 Spring Boot RSocket 服务器所需的 <dependencies>。使用的是 Spring Boot 2.2.5.RELEASE 版本,因为在撰写本文时,此版本具有最成熟的 RSocket 功能。该项目还依赖于 lombok 和 spring-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 调用与其处理方法关联起来。
代码部分就到这里。我们来试试看。
保持现有终端窗口打开,在第二个终端窗口中,将 rsocket-server 文件夹设置为当前目录。然后使用以下命令构建并运行 RSocket 服务器:
./mvnw clean package spring-boot:run -DskipTests=true
或者,如果你愿意,也可以使用 Java IDE 中的“构建”和“运行”命令。
接下来,你将使用在步骤 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 客户端。