WebSocketMessageBrokerConfigurer详细讲解下这个类的作用及用法
WebSocketMessageBrokerConfigurer
是 Spring Framework 中用于配置基于 WebSocket 的消息代理的一个重要接口。借助这个接口,你能够对 WebSocket 连接、消息代理、消息映射等方面进行定制化设置,以满足不同场景下的实时通信需求。下面为你详细介绍其作用和用法。
作用
- WebSocket 连接配置:可以对客户端与服务器之间的 WebSocket 连接进行定制,比如指定连接端点、配置拦截器等。
- 消息代理配置:能够配置消息代理,像使用 STOMP(Simple Text Oriented Messaging Protocol)代理,来处理消息的发送、订阅和广播等操作。
- 消息映射配置:可以定义消息的映射规则,例如指定客户端发送消息的目的地和服务器处理消息的方法。
用法
1. 添加依赖
若你使用的是 Spring Boot 项目,需要在 pom.xml
里添加如下依赖:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-messaging</artifactId>
</dependency>
2. 创建配置类
创建一个配置类并实现 WebSocketMessageBrokerConfigurer
接口,在该类中重写相应的方法来进行配置。
java
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
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
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
// 注册 STOMP 端点
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
// 配置消息代理
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// 启用简单的内存消息代理,用于广播消息,客户端可以订阅以 /topic 开头的目的地
registry.enableSimpleBroker("/topic");
// 设置客户端发送消息的前缀,客户端发送的消息需要以 /app 开头
registry.setApplicationDestinationPrefixes("/app");
}
// 配置消息通道
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
// 可以在这里添加拦截器等配置
registration.interceptors(new CustomChannelInterceptor());
}
}
代码解释
@EnableWebSocketMessageBroker
:此注解用于启用基于 WebSocket 的消息代理功能。registerStompEndpoints
方法 :该方法用于注册 STOMP 端点,客户端通过这些端点来建立 WebSocket 连接。withSockJS()
方法是为了提供对不支持 WebSocket 的浏览器的兼容性。configureMessageBroker
方法 :该方法用于配置消息代理。enableSimpleBroker
方法启用了一个简单的内存消息代理,客户端可以订阅以/topic
开头的目的地来接收广播消息;setApplicationDestinationPrefixes
方法设置了客户端发送消息的前缀,客户端发送的消息需要以/app
开头。configureClientInboundChannel
方法:该方法用于配置客户端入站消息通道,可以在这里添加拦截器等配置。
3. 创建消息处理器
创建一个控制器类来处理客户端发送的消息。
java
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class MessageController {
@MessageMapping("/hello")
@SendTo("/topic/greetings")
public String handleMessage(String message) {
return "Server received: " + message;
}
}
代码解释
@MessageMapping
:该注解用于将客户端发送的消息映射到特定的处理方法上,这里客户端发送到/app/hello
的消息会被handleMessage
方法处理。@SendTo
:该注解用于指定处理方法返回的消息将被发送到哪个目的地,这里处理结果会被发送到/topic/greetings
,订阅该目的地的客户端都能收到消息。
4. 客户端使用
在前端页面中使用 JavaScript 来建立 WebSocket 连接并发送、接收消息。
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Example</title>
<script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/stomp.min.js"></script>
</head>
<body>
<input type="text" id="messageInput" placeholder="Enter message">
<button onclick="sendMessage()">Send</button>
<div id="messages"></div>
<script>
var socket = new SockJS('/ws');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (message) {
var messagesDiv = document.getElementById('messages');
messagesDiv.innerHTML += '<p>' + JSON.parse(message.body) + '</p>';
});
});
function sendMessage() {
var message = document.getElementById('messageInput').value;
stompClient.send("/app/hello", {}, message);
}
</script>
</body>
</html>
总结
通过实现 WebSocketMessageBrokerConfigurer
接口,你可以方便地配置基于 WebSocket 的消息代理,实现客户端与服务器之间的实时通信。这种方式在很多场景下都非常有用,比如实时聊天、实时数据推送等。
如下是我的配置WebSocket的配置类
配置 WebSocket 和 STOMP 消息代理,包括端点注册、跨域设置、消息代理配置、拦截器配置、线程池配置和 WebSocket 传输配置等
java
import com.incomessage.interceptor.HttpHandShakeInterceptor;
import com.incomessage.interceptor.SocketChannelInterceptor;
import com.incomessage.util.WebsocketSessionHolder;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
import org.springframework.web.socket.server.HandshakeInterceptor;
/**
* 配置 WebSocket 和 STOMP 消息代理,包括端点注册、跨域设置、消息代理配置、拦截器配置、线程池配置和 WebSocket 传输配置等
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
private static final Logger log = LoggerFactory.getLogger(SocketChannelInterceptor.class);
/* @Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, topic());
return container;
}
@Bean
MessageListenerAdapter listenerAdapter(MessageReceiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
@Bean
ChannelTopic topic() {
return new ChannelTopic("messages");
}*/
@Autowired
SocketChannelInterceptor socketChanelInterceptor;
@Autowired
WebsocketSessionHolder websocketSessionHolder;
@Autowired
HttpHandShakeInterceptor handShakeInterceptor;
@Autowired
RedisTemplate redisTemplate;
@Autowired
Environment environment;
@Value("${websocket.allowed.origins}")
private String allowedOriginPattern;
public WebSocketConfig() {
}
/**
* 注册stomp端点
* @param registry
*/
public void registerStompEndpoints(StompEndpointRegistry registry) {
//注册一个 STOMP 端点 /ws,客户端可以通过这个端点建立 WebSocket 连接。
registry.addEndpoint(new String[]{"/ws"})
//设置允许的跨域请求源,这里将 allowedOriginPattern 字符串按逗号分割成数组,指定了多个允许的源模式。
.setAllowedOriginPatterns(this.allowedOriginPattern.split(","))
//添加一个握手拦截器 handShakeInterceptor,用于在 WebSocket 握手阶段进行额外的处理,如验证请求、添加额外的请求头信息等。
.addInterceptors(new HandshakeInterceptor[]{this.handShakeInterceptor})
//启用 SockJS 支持,SockJS 是一个 JavaScript 库,用于在不支持 WebSocket 的浏览器中提供类似 WebSocket 的功能,它会自动选择合适的传输机制(如 XHR 轮询、事件源等)。
.withSockJS()
//设置流传输的字节限制为 524288 字节。
.setStreamBytesLimit(524288)
//设置 HTTP 消息缓存的大小为 1000。
.setHttpMessageCacheSize(1000);
}
/**
* 配置消息代理
*
* @param registry
*/
public void configureMessageBroker(MessageBrokerRegistry registry) {
//设置应用程序的目标前缀为 /app/,客户端发送到以 /app/ 开头的消息会被路由到控制器方法进行处理。
registry.setApplicationDestinationPrefixes(new String[]{"/app/"});
//启用一个简单的消息代理,客户端订阅以 /topic/ 或 /user/ 开头的目的地时,消息会被直接发送到这些目的地。
registry.enableSimpleBroker(new String[]{"/topic/", "/user/"})
//设置心跳值为 10000 毫秒,即每隔 10 秒发送一次心跳消息,用于保持连接的活跃状态。
.setHeartbeatValue(new long[]{10000L, 10000L})
//设置任务调度器,用于处理心跳消息的发送。
.setTaskScheduler(this.heartBeatScheduler());
//设置用户目标前缀为 /user/,用于处理一对一的消息传递。
registry.setUserDestinationPrefix("/user/");
}
@Bean
public TaskScheduler heartBeatScheduler() {
return new ThreadPoolTaskScheduler();
}
/**
* 配置客户端入站通道
*
* @param registration
*/
public void configureClientInboundChannel(ChannelRegistration registration) {
//为客户端入站通道添加一个拦截器 socketChanelInterceptor,用于在消息到达应用程序之前进行拦截和处理。
registration.interceptors(new ChannelInterceptor[]{this.socketChanelInterceptor});
//创建一个线程池任务执行器。
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(400);//设置线程池的核心线程数为 400。
taskExecutor.setMaxPoolSize(600);//设置线程池的最大线程数为 600。
taskExecutor.setKeepAliveSeconds(101);//设置线程的空闲存活时间为 101 秒。
registration.taskExecutor(taskExecutor);//为客户端入站通道设置任务执行器,用于处理消息的异步处理。
}
/**
* 配置WebSocket传输
* @param webSocketTransportRegistration
*/
public void configureWebSocketTransport(WebSocketTransportRegistration webSocketTransportRegistration) {
//
webSocketTransportRegistration.setMessageSizeLimit(8192);//设置 WebSocket 消息的大小限制为 8192 字节。
webSocketTransportRegistration.setSendBufferSizeLimit(8192);//设置发送缓冲区的大小限制为 8192 字节。
webSocketTransportRegistration.setSendTimeLimit(10000);//设置发送消息的时间限制为 10000 毫秒。
//添加一个 WebSocket 处理器装饰器工厂,用于在 WebSocket 连接建立和处理错误时进行额外的处理。
webSocketTransportRegistration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
public WebSocketHandler decorate(WebSocketHandler handler) {
return new WebSocketHandlerDecorator(handler) {
//在 WebSocket 连接建立后,从会话属性中获取 sessionId,如果 sessionId 不为空,则将该会话添加到 websocketSessionHolder 中。
public void afterConnectionEstablished(final WebSocketSession session) throws Exception {
String sessionId = (String)session.getAttributes().get("sessionId");
if (StringUtils.isNotBlank(sessionId)) {
WebSocketConfig.this.websocketSessionHolder.addWebSocketSessions(sessionId, session);
}
super.afterConnectionEstablished(session);
}
//在 WebSocket 传输过程中出现错误时,记录错误日志,包含错误信息、WebSocket 会话 ID 和用户会话 ID。
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
String userSessionId = (String)session.getAttributes().get("sessionId");
log.error("handleTransportError error {},socketSessionId,{},userSessionId,{}", new Object[]{exception.getMessage(), session.getId(), userSessionId == null ? "" : userSessionId});
super.handleTransportError(session, exception);
}
};
}
});
}
}