领先一步
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 的更容易使用一些。在终端中,按如下方式下载 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 控制器,使用
/hello
这样的 URL 路径映射将 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://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 客户端。