随着现代互联网应用的不断发展,实时通信已经成为许多应用程序不可或缺的功能。无论是社交网络、在线游戏还是数据监控系统,实时通信都能提供快速、无缝的信息交换。而实现实时通信的两种主要协议是 RSocket 和 WebSocket。
RSocket 是一种新的、先进的应用层协议,旨在提供高效的网络通信。与传统的请求/响应模式不同,RSocket 支持请求-响应、请求-流、流-流等多种模式,从而在微服务和流式数据传输中表现得更加灵活和高效。RSocket 的优势在于它可以在 TCP、WebSocket 等多种传输协议上运行,支持背压机制和多路复用,从而避免了资源的浪费,并保证了消息传递的可靠性。
WebSocket 是一种标准协议,允许客户端和服务器之间建立持久连接,客户端和服务器都可以主动发送消息。相较于传统的 HTTP 请求-响应模型,WebSocket 是全双工通信,即服务器可以实时向客户端推送数据,而不需要等待客户端发起请求,尤其适合实时数据更新场景。WebSocket 的使用场景广泛,涵盖了即时通讯、实时数据展示和多人在线游戏等。
运行效果:
图片
若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。
本文将结合 Spring Boot 3.3,详细讲解如何使用 RSocket 和 WebSocket 实现实时通信。我们将通过一个完整的示例,展示前后端交互、消息传递和双向通信的实际应用。文章还将结合具体的代码示例,演示如何从前端向后端发送消息,并在点击按钮时与服务器进行交互。
项目配置
项目依赖配置(pom.xml)
在 pom.xml 中引入 RSocket、WebSocket 以及其他必要的依赖。
<?xml versinotallow="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.icoderoad</groupId>
<artifactId>rsocket-websocket-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rsocket-websocket-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency><!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- RSocket 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
</dependency>
<!-- WebSocket 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- Thymeleaf 模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Lombok 支持 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
应用配置(application.yml)
server:
port: 8080
spring:
rsocket:
server:
port: 7000
transport: websocket
mapping-path: /rsocket
websocket:
enabled: true
mapping: /ws
app:
rsocket-message: "这是来自 RSocket 的消息"
websocket-message: "这是来自 WebSocket 的消息"
读取配置类 (RSocket 和 WebSocket 配置类)
我们使用 @ConfigurationProperties 注解读取配置文件中的 app 节点。这里使用 Lombok 来简化实体类的代码实现。
package com.icoderoad.rwebsocket.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String rsocketMessage;
private String websocketMessage;
}
RSocket 服务端实现
RSocket 提供了灵活的通信模型,允许服务端和客户端以流的方式交换数据。我们通过 @MessageMapping 来定义接收和处理客户端消息的方法。
package com.icoderoad.rwebsocket.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.stereotype.Controller;
import com.icoderoad.rwebsocket.config.AppProperties;
import reactor.core.publisher.Mono;
@Controller
public class RSocketController {
@Autowired
private AppProperties appProperties;
@MessageMapping("rsocket.message")
public Mono<String> sendMessage(String input) {
return Mono.just("RSocket服务端响应: " + input + " | " + appProperties.getRsocketMessage());
}
}
WebSocket 服务端实现
WebSocket 服务端使用 Spring 提供的 TextWebSocketHandler 来处理消息。我们在收到客户端消息后,通过会话对象将响应发送回客户端。
package com.icoderoad.rwebsocket.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import com.icoderoad.rwebsocket.config.AppProperties;
@Controller
public class WebSocketController extends TextWebSocketHandler {
@Autowired
private AppProperties appProperties;
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String clientMessage = message.getPayload();
String responseMessage = "WebSocket服务端响应: " + clientMessage + " | " + appProperties.getWebsocketMessage();
session.sendMessage(new TextMessage(responseMessage));
}
}
WebSocket 配置类
使用 WebSocketConfigurer 来注册 WebSocket 的处理器,并允许跨域访问。
package com.icoderoad.rwebsocket.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import com.icoderoad.rwebsocket.controller.WebSocketController;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
private final WebSocketController webSocketController;
public WebSocketConfig(WebSocketController webSocketController) {
this.webSocketController = webSocketController;
}
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketController, "/ws")
.setAllowedOrigins("*");
}
}
前端实现
前端使用 Thymeleaf 渲染,并通过 jQuery 与后端的 RSocket 和 WebSocket 进行交互。用户可以输入消息,通过点击按钮发送到后端,并接收后端的响应。
在 src/main/resources/templates 目录下创建 index.html 文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RSocket & WebSocket Demo</title>
<!-- 引入 Bootstrap 样式 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- 引入 jQuery 和 Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- 引入 RSocket 库 -->
<script src="/js/rsocket-core.min.js"></script>
<script src="/js/rsocket-websocket-client.min.js"></script>
<style>
.message-box {
max-height: 300px;
overflow-y: auto;
border: 1px solid #dee2e6;
padding: 10px;
border-radius: 0.25rem;
background-color: #f8f9fa;
}
.message-box p {
margin-bottom: 0.5rem;
}
</style>
</head>
<body>
<div class="container">
<h1 class="mt-5">RSocket & WebSocket Demo</h1>
<!-- WebSocket 区域 -->
<div class="mt-5">
<h3>WebSocket 消息</h3>
<div class="input-group mb-3">
<input type="text" id="websocket-input" class="form-control" placeholder="输入 WebSocket 消息">
<button id="send-websocket" class="btn btn-primary">发送 WebSocket 消息</button>
</div>
<div id="websocket-messages" class="message-box"></div>
</div>
<!-- RSocket 区域 -->
<div class="mt-5">
<h3>RSocket 消息</h3>
<div class="input-group mb-3">
<input type="text" id="rsocket-input" class="form-control" placeholder="输入 RSocket 消息">
<button id="send-rsocket" class="btn btn-success">发送 RSocket 消息</button>
</div>
<div id="rsocket-messages" class="message-box"></div>
</div>
</div>
<script>
$(document).ready(function () {
// WebSocket 连接
const ws = new WebSocket("ws://localhost:8080/ws");
ws.onmessage = function (event) {
$("#websocket-messages").append(`<p>${event.data}</p>`);
};
// 发送 WebSocket 消息
$("#send-websocket").click(function () {
const message = $("#websocket-input").val();
if (message.trim()) {
ws.send(message);
$("#websocket-input").val('');
}
});
const { RSocketClient } = window['rsocket-core'];
const { default: RSocketWebSocketClient } = window['rsocket-websocket-client'];
// RSocket 客户端创建和连接
async function createRSocketClient() {
const transportOptions = {
url: 'ws://localhost:7001/rsocket',
};
const setupOptions = {
keepAlive: 60000,
lifetime: 180000,
dataMimeType: 'application/json',
metadataMimeType: 'message/x.rsocket.routing.v0',
};
const transport = new RSocketWebSocketClient(transportOptions);
const client = new RSocketClient({
setup: setupOptions,
transport
});
try {
return await client.connect();
} catch (error) {
console.error("RSocket 连接错误: ", error);
throw error;
}
}
// 处理 RSocket 消息
async function setupRSocket() {
try {
const socket = await createRSocketClient();
$("#send-rsocket").click(function () {
const message = $("#rsocket-input").val();
if (message.trim()) {
socket.requestResponse({
data: message,
metadata: String.fromCharCode('rsocket.message'.length) + 'rsocket.message'
}).subscribe({
onComplete: (response) => {
$("#rsocket-messages").append(`<p>${response.data}</p>`);
$("#rsocket-input").val('');
},
onError: (error) => {
console.error("RSocket 错误: ", error);
}
});
}
});
} catch (error) {
console.error("启动 RSocket 客户端失败: ", error);
}
}
setupRSocket();
});
</script>
</body>
</html>
总结
通过结合 RSocket 和 WebSocket,我们可以在 Spring Boot 3.3 中轻松实现高效的实时通信。RSocket 通过其多种通信模型和背压机制,为流式数据传输提供了强大的支持;WebSocket 则在全双工实时通信方面表现出色,适合需要即时数据更新的场景。通过本文的实例,读者可以在项目中灵活应用这两种技术,实现高效的消息交互。在前端,我们使用简单的输入框和按钮,演示了如何与服务器进行消息通信。这种方式不仅提升了用户体验,还能大幅提高系统的响应速度和处理效率。