一、前言
RSocket是一种二进制协议,可用于TCP、WebSocket和Aeron等字节流传输的应用协议,具有以下交互模型:
- Request-Response: 发送一条信息,接收一条信息。
- Request-Stream: 发送一条消息并接收返回的消息流。
- Channel: 双向发送消息流。
- Fire-and-Forget: 发送单向消息。
一旦建立了初始连接,“客户端”与“服务器”之间的区别就消失了,因为两端变得对称,每一端都可以发起上述交互之一。这就是为什么在协议中称参与端的为“请求者”和“响应者”,而上述交互称为“请求流”或简单地称为“请求”。
RSocket 协议的主要特点和优势:
- 跨网络边界的响应式流语义——对于流请求,如请求流和通道,背压信号在请求者和响应者之间传递,允许请求者在源端减慢响应者的速度,从而减少对网络层拥塞控制的依赖,以及在网络层或任何层缓冲的需要。
- 请求节流(Request throttling)——这个特性被命名为“租赁”(LEASE),以从两端发送的租赁帧命名,用于限制给定时间内另一端允许的请求总数。租约是定期更新的。
- 会话恢复——这是为连接丢失而设计的,需要维护一些状态。状态管理对应用程序是透明的,并与背压相结合,可以在可能的情况下停止生产者并减少所需的状态量。
- 大消息的碎片化和重组。
- 保活(心跳)。
应用场景:
- 游戏开发:游戏需要实时、低延迟的通信,以提供更好的用户体验。RSocket提供了一种可靠、高效的通信方式,可以在不同的游戏组件之间进行通信,例如服务器和客户端之间的通信,或者客户端与客户端之间的通信。
- 实时流媒体:实时流媒体应用需要将大量的数据从服务器传输到客户端,并且需要保证数据的实时性和稳定性。RSocket提供了一种可靠的数据传输方式,可以保证数据的有序性和完整性。
- 物联网(IoT):物联网设备需要通过网络进行通信,以实现设备的远程控制和数据采集。RSocket可以用于建立可靠、高效的连接,使得设备可以快速地传输和接收数据。
- 分布式系统:分布式系统需要将不同的组件连接在一起,以实现协同工作。RSocket提供了一种可靠、高效的通信方式,可以用于在不同的组件之间进行通信。
- 实时数据分析:实时数据分析需要对大量的数据进行实时处理和分析。RSocket可以用于将数据从数据源传输到处理节点,并保证数据的实时性和完整性。
二、协议简介
Connecting
最初,客户端通过 TCP 或 WebSocket 等低级流传输方式连接服务器,并向服务器发送 SETUP 帧,以设置连接参数。服务器可能会拒绝 SETUP 帧,但一般情况下,在发送(客户端)和接收(服务器)SETUP 帧后,双方都可以开始发出请求,除非 SETUP 表示使用租用语义来限制请求数量,在这种情况下,双方都必须等待另一端发出 LEASE 帧才能允许发出请求。
发出请求(Making Requests)
建立连接后,双方可通过 REQUEST_RESPONSE、REQUEST_STREAM、REQUEST_CHANNEL 或 REQUEST_FNF 帧之一发起请求。每个帧都会从请求者向响应者发送一条信息。然后,响应者可以返回带有响应信息的 PAYLOAD 帧,而在 REQUEST_CHANNEL 的情况下,请求者也可以发送带有更多请求信息的 PAYLOAD 帧。
当一个请求涉及到诸如request - stream和Channel这样的消息流时,响应者必须遵守来自请求者的需求信号。需求表示为若干消息。初始需求在REQUEST_STREAM和REQUEST_CHANNEL帧中指定。后续的请求通过REQUEST_N帧发出。
每一端也可以通过METADATA_PUSH帧发送元数据通知,这些元数据通知不属于任何单独的请求,而是属于整个连接。
消息格式
RSocket 消息包含数据和元数据。元数据可用于发送路由、安全令牌等。数据和元数据可以有不同的格式。每种格式的 MIME 类型都在设置框架中声明,并适用于给定连接上的所有请求。
虽然所有报文都可以包含元数据,但路由等元数据通常是按请求提供的,因此只包含在请求的第一条报文中,即 REQUEST_RESPONSE、REQUEST_STREAM、REQUEST_CHANNEL 或 REQUEST_FNF 框架之一。
三、代码实战
1.添加依赖
注:两个项目都需要添加这个依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2.创建两个项目 一个服务端rscoket-server 一个客户端rsocket-client
3.服务端配置和客户端配置
服务端:
server.port=8081
spring.rsocket.server.port=8899
spring.rsocket.server.transport=tcp
客户端:
server.port=8080
4.客户端消息处理模板
package com.example.rsocketclient.service;
import org.springframework.messaging.rsocket.RSocketRequester;
import org.springframework.stereotype.Service;
/**
* @author qx
* @date 2024/7/10
* @des
*/
@Service
public class RsocketService {
private final RSocketRequester rSocketRequester;
public RsocketService(RSocketRequester.Builder builder) {
this.rSocketRequester = builder.tcp("localhost", 8899);
}
/**
* 发送消息
*
* @param content
*/
public void sendMessage(String content) {
this.rSocketRequester.route("message")
.data(content)
.retrieveMono(String.class)
.subscribe(System.out::println);
}
}
5.客户端消息发送的控制层
package com.example.rsocketclient.controller;
import com.example.rsocketclient.service.RsocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author qx
* @date 2024/7/10
* @des
*/
@RestController
public class RsocketController {
@Autowired
private RsocketService rsocketService;
@GetMapping("/message")
public String test() {
rsocketService.sendMessage("测试发送数据");
return "success";
}
}
6.服务端接收数据
package com.example.rsocketserver.server;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
/**
* @author qx
* @date 2024/7/11
* @des
*/
@Service
public class RsServer {
@MessageMapping("message")
public Mono<String> handleMessage(Mono<String> message) {
return message.doOnNext(msg -> {
System.out.printf("接收到消息:%s%n", msg);
}).map(msg -> "服务器成功收到了你的消息!!!");
}
}
7.启动两个项目进行项目测试
客户端发送消息
服务端接收到客户端发送的数据
客户端收到服务端收到信息的反馈