一、前言

RSocket是一种二进制协议,可用于TCP、WebSocket和Aeron等字节流传输的应用协议,具有以下交互模型:

  • Request-Response:    发送一条信息,接收一条信息。
  • Request-Stream:       发送一条消息并接收返回的消息流。
  • Channel:                双向发送消息流。
  • Fire-and-Forget:     发送单向消息。

一旦建立了初始连接,“客户端”与“服务器”之间的区别就消失了,因为两端变得对称,每一端都可以发起上述交互之一。这就是为什么在协议中称参与端的为“请求者”和“响应者”,而上述交互称为“请求流”或简单地称为“请求”。

RSocket 协议的主要特点和优势:

  • 跨网络边界的响应式流语义——对于流请求,如请求流和通道,背压信号在请求者和响应者之间传递,允许请求者在源端减慢响应者的速度,从而减少对网络层拥塞控制的依赖,以及在网络层或任何层缓冲的需要。
  • 请求节流(Request throttling)——这个特性被命名为“租赁”(LEASE),以从两端发送的租赁帧命名,用于限制给定时间内另一端允许的请求总数。租约是定期更新的。
  • 会话恢复——这是为连接丢失而设计的,需要维护一些状态。状态管理对应用程序是透明的,并与背压相结合,可以在可能的情况下停止生产者并减少所需的状态量。
  • 大消息的碎片化和重组。
  • 保活(心跳)。

应用场景:

  1. 游戏开发:游戏需要实时、低延迟的通信,以提供更好的用户体验。RSocket提供了一种可靠、高效的通信方式,可以在不同的游戏组件之间进行通信,例如服务器和客户端之间的通信,或者客户端与客户端之间的通信。
  2. 实时流媒体:实时流媒体应用需要将大量的数据从服务器传输到客户端,并且需要保证数据的实时性和稳定性。RSocket提供了一种可靠的数据传输方式,可以保证数据的有序性和完整性。
  3. 物联网(IoT):物联网设备需要通过网络进行通信,以实现设备的远程控制和数据采集。RSocket可以用于建立可靠、高效的连接,使得设备可以快速地传输和接收数据。
  4. 分布式系统:分布式系统需要将不同的组件连接在一起,以实现协同工作。RSocket提供了一种可靠、高效的通信方式,可以用于在不同的组件之间进行通信。
  5. 实时数据分析:实时数据分析需要对大量的数据进行实时处理和分析。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

SpringBoot整合RSocket实现实时数据通信_Rsocket

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.启动两个项目进行项目测试

客户端发送消息

SpringBoot整合RSocket实现实时数据通信_SpringBoot_02

服务端接收到客户端发送的数据

SpringBoot整合RSocket实现实时数据通信_SpringBoot_03

客户端收到服务端收到信息的反馈

SpringBoot整合RSocket实现实时数据通信_SpringBoot_04