关于.withSockJs()阻塞Stomp连接、CORS无效及WebSocket控制器的疑问
问题解答
1. 仅配置.withSockJS()时连接失败的原因
当你只注册带.withSockJS()的端点时,这个端点是SockJS协议封装的端点,它要求客户端必须使用SockJS客户端库发起连接,不能用原生WebSocket直接连接。
SockJS的工作逻辑是:客户端先发送HTTP GET请求到端点,获取SockJS可用传输方式的信息,再选择合适的方式(包括降级到轮询等)建立连接。而原生WebSocket客户端是直接发起ws://协议的连接请求,服务端的SockJS端点不会处理这种原生WebSocket的握手请求,因此连接会失败。
有效配置的两种场景逻辑:
- 同时注册普通端点和SockJS端点:原生WebSocket客户端可连接普通
/chat端点,SockJS客户端可连接/chat的SockJS封装端点 - 只注册普通端点:原生WebSocket直接发起
ws://连接,服务端的原生WebSocket端点能正常处理握手
2. setAllowedOrigins配置无效的原因
这个问题要分两种场景分析:
- 原生WebSocket连接:
StompEndpointRegistry的.setAllowedOrigins()是针对SockJS端点的CORS配置,对原生WebSocket端点不生效。如果客户端用原生WebSocket连接普通端点,需要单独配置CORS——比如在addEndpoint()后调用.setAllowedOrigins(),或者通过全局CORS配置(WebMvcConfigurer)设置。 - Postman连接:Postman是非浏览器环境,不执行浏览器的CORS安全检查,所以不管服务端的CORS配置如何,Postman都能发起连接,这是正常现象。
你之前切换到原生WebSocket解决CORS问题,大概率是全局CORS配置已经覆盖了原生WebSocket的握手请求,而SockJS的CORS配置未生效导致了之前的问题。
3. WebSocket端点控制器的实现与线程安全问题
- 控制器实例:Spring的
@Controller/@MessageMapping注解的控制器默认是单实例,不管多少客户端连接,整个应用中只有一个控制器实例。 - 消息处理线程:
@MessageMapping标注的消息处理方法是多线程执行的——Spring会用内置任务线程池处理不同客户端的消息,多个客户端的请求会同时调用控制器的方法。 - 用户-session映射的线程安全:因为控制器是单实例且消息处理多线程,所以维护用户名和sessionId映射的Map必须是线程安全的,比如
java.util.concurrent.ConcurrentHashMap,不能用普通HashMap,否则会出现并发修改异常或数据不一致问题。
另外,建议通过@EventListener监听SessionDisconnectEvent事件,及时从Map中移除断开连接的用户信息,避免内存泄漏。
内容的提问来源于stack exchange,提问作者BJagger
相关产品推荐
相关产品推荐

