Spring Cloud Gateway整合基于STOMP协议的WebSocket实战及遇到问题解决
本实例介绍了Spring Cloud Gateway整合基于STOMP协议的WebSocket的实现。开发了聊天功能,和用户在线状态。解决了协议gateway整合websocket出现的问题
技术点
- Spring Cloud Gateway
- Nacos
- WebSocket
- STOMP
WebSocket与STOMP协议详解
1. WebSocket
WebSocket 是一种通信协议,提供了在客户端和服务器之间建立全双工通信的功能。这意味着客户端和服务器可以在任意时间相互发送数据,而不必遵循请求-响应的传统模式(如HTTP)。
特点:
-
双向通信:与传统的HTTP不同,WebSocket允许服务器主动向客户端推送数据,而不必等待客户端请求。
-
持久连接:WebSocket连接一旦建立,可以一直保持连接,直到任意一方关闭它。这减少了频繁建立连接的开销。
-
低延迟:由于WebSocket消除了HTTP请求的头部开销和连接延迟,因此它适合于对延迟敏感的应用,如在线游戏、股票交易和聊天应用。
工作流程:
-
连接建立:客户端通过HTTP的升级机制(Upgrade header)请求建立WebSocket连接。
-
数据传输:一旦连接建立,客户端和服务器可以通过这个连接双向传输数据,使用一种轻量级的帧格式。
-
连接关闭:任意一方可以随时关闭连接,通知对方连接已关闭。
使用场景:
-
实时聊天应用
-
实时数据流(如股票行情、体育比赛更新)
-
在线多人游戏
-
实时协作工具(如Google Docs)
2. STOMP
STOMP(Simple Text Oriented Messaging Protocol) 是一种简单的文本协议,用于在客户端和消息代理(例如,RabbitMQ、ActiveMQ)之间交换消息。它是应用层的协议,常用于消息传递系统,提供了一种基于消息的通信方式。
特点:
-
简单易用:STOMP使用类似于HTTP的文本格式,非常容易理解和实现。
-
基于订阅/发布模型:STOMP支持基于主题(topic)的订阅/发布模型,这使得消息可以广播给多个客户端。
-
支持消息队列:STOMP支持将消息发送到队列,多个消费者可以从同一个队列中读取消息,确保负载均衡。
常用命令:
-
CONNECT:客户端请求连接到STOMP服务器。
-
SEND:客户端发送一条消息到指定的目的地(如某个队列或主题)。
-
SUBSCRIBE:客户端订阅某个目的地,以接收该目的地的所有消息。
-
UNSUBSCRIBE:取消订阅。
-
DISCONNECT:断开与STOMP服务器的连接。
STOMP和WebSocket的结合:
虽然WebSocket本身是一个很好的低延迟双向通信工具,但它只提供了一种基础的传输方式,没有定义消息的格式或通信模式。STOMP可以在WebSocket之上运行,提供了消息格式、路由、确认等高级功能。
例如,在一个实时聊天应用中,WebSocket用于底层的双向通信,而STOMP则负责处理消息的路由和格式化:
-
客户端通过WebSocket连接到服务器,并通过STOMP发送或订阅消息。
-
服务器使用STOMP协议将消息广播给所有订阅了相应主题的客户端。
WebSocket服务端
依赖
<!-- WebSocket依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- nacos依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
枚举类
package com.inspur.message.constant;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
@AllArgsConstructor(access = AccessLevel.PRIVATE)
public enum MessageTypeEnum {
TEXT("文本"),
IMAGE("图片"),
VOICE("语音"),
FILE("文件"),
EMOJI("表情"),
SYSTEM_NOTIFICATION("系统通知"),
LOCATION("位置"),
LINK("链接"),
RECALL("撤回"),
MENTION("@提及"),
SYSTEM_MESSAGE("系统提示"),
VIDEO("视频"),
RED_PACKET("红包"),
VOTE("投票"),
FRIEND_SHARE("好友分享");
// 其他可能的消息类型...
private String description;
public String getDescription() {
return description;
}
public static boolean isFileMessageType(MessageTypeEnum messageType) {
return messageType == IMAGE || messageType == VOICE || messageType == FILE || messageType == VIDEO;
}
}
WebSocket配置类
package com.inspur.message.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker//注解开启STOMP协议来传输基于代理的信息,实现实时双向通信和消息传递
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
/**
* 客户端在订阅或发布消息到目的地路径前,要连接到该端点
*
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();// 启用 SockJS (浏览器不支持WebSocket,SockJS 将会提供兼容性支持)
}
/**
* 配置消息代理
*
* @param register
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry register) {
/**
* 放开的前缀路由,客户端才能接收对应路由开头的信息