WebSocketMessageBrokerConfigurer的作用及用法

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);
                    }
                };
            }
        });
    }


}
相关推荐
努力的小雨3 分钟前
行业案例分享:汽车售后智能助手
后端
GoGeekBaird26 分钟前
69天探索操作系统-第53天:高级分布式操作系统算法和共识协议
后端·操作系统
小杨4042 小时前
springboot框架项目实践应用八(validation自定义校验)
spring boot·后端·架构
Cloud_.2 小时前
Spring Boot整合Sa-Token极简指南
java·后端·springboot·登录校验
冬冬小圆帽2 小时前
防止手机验证码被刷:React + TypeScript 与 Node.js + Express 的全面防御策略
前端·后端·react.js·typescript
陈明勇3 小时前
chromem-go:Go 语言 RAG 应用的高效轻量级向量数据库
后端·go
掘金詹姆斯3 小时前
从Guava缓存源码提炼业务开发心法:Get方法暗藏的12个高并发设计哲学
后端
零零壹113 小时前
理解Akamai EdgeGrid认证在REST API中的应用
前端·后端
uhakadotcom3 小时前
DataWorks邮件外发完全指南:从零开始实现数据自动推送(2025最新实践)
后端·面试·github