Spring WebSocket多线程发消息报错[TEXT_PARTIAL_WRITING]求解决方案
解决Spring WebSocket多线程并发写入的TEXT_PARTIAL_WRITING错误
兄弟,这个问题我太熟了!之前做Spring WebSocket项目的时候踩过一模一样的坑——多线程同时往同一个WebSocket会话写消息,就会触发这个TEXT_PARTIAL_WRITING的无效状态错误,而且总不能把整个发送方法加个大锁吧?那样不同会话的发送操作都得串行,性能直接拉胯。下面给你几个靠谱的解决方案,按需选就行:
方案1:为每个WebSocketSession单独绑定锁
核心思路是只锁当前要发送的会话,不同会话的发送操作互不影响,完全并行。我们可以用ConcurrentHashMap来维护会话和对应锁对象的映射:
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.TextMessage; import java.io.IOException; import java.util.concurrent.ConcurrentHashMap; public class WebSocketMessageSender { // 用ConcurrentHashMap存储每个会话对应的锁,线程安全 private final ConcurrentHashMap<WebSocketSession, Object> sessionLocks = new ConcurrentHashMap<>(); public void sendMessage(WebSocketSession session, TextMessage message) throws IOException { // 为当前会话获取或创建锁对象 Object lock = sessionLocks.computeIfAbsent(session, k -> new Object()); synchronized (lock) { // 先判断会话是否还处于打开状态 if (session.isOpen()) { session.sendMessage(message); } // 会话关闭后可以移除对应的锁,避免内存泄漏 else { sessionLocks.remove(session); } } } }
这个方案的好处是轻量、可控,完全由自己管理锁的生命周期,不会影响其他会话的并发发送。
方案2:使用Spring官方的ConcurrentWebSocketSessionDecorator
Spring WebSocket自带了一个专门解决并发发送问题的装饰器ConcurrentWebSocketSessionDecorator,它内部会把同一个会话的发送请求排队串行执行,同时允许不同会话并行发送,还支持设置超时时间和缓冲区大小,简直是为这个场景量身定做的:
import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator; import java.io.IOException; public void handleSession(WebSocketSession session) { // 包装原始会话:第一个参数是原始会话,第二个是发送超时时间(毫秒),第三个是缓冲区大小 ConcurrentWebSocketSessionDecorator decoratedSession = new ConcurrentWebSocketSessionDecorator(session, 1000, 1024 * 1024); // 之后所有发送操作都用这个装饰后的会话 try { decoratedSession.sendMessage(new TextMessage("Hello from concurrent sender!")); } catch (IOException e) { // 处理异常 } }
这个方案是我最推荐的,因为是官方实现,稳定性和兼容性都有保障,代码也最简洁,不用自己手动管理锁。
方案3:如果用STOMP协议,直接用SimpMessagingTemplate
如果你的项目是基于STOMP协议的WebSocket服务,那根本不用自己处理并发问题——Spring提供的SimpMessagingTemplate本身就是线程安全的,内部已经帮你搞定了会话的并发写入:
import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.beans.factory.annotation.Autowired; @Service public class StompMessageSender { @Autowired private SimpMessagingTemplate messagingTemplate; public void sendToUser(String username, String destination, Object payload) { // 给指定用户发送消息,完全不用关心并发问题 messagingTemplate.convertAndSendToUser(username, destination, payload); } public void sendToTopic(String destination, Object payload) { // 给主题发送广播消息,同样线程安全 messagingTemplate.convertAndSend(destination, payload); } }
这个方案适合用STOMP的场景,直接借助框架能力,零额外代码解决问题。
内容的提问来源于stack exchange,提问作者Sepehr GH
相关产品推荐
相关产品推荐

