最近想起了看一下websocket,想起之前一个项目里遇到过的问题。使用的是spring里的spring-boot-starter-websocket依赖。使用websocketsession.sendMessage()方法,因为这个方法并没有做线程安全处理,所以多线程对同一个发消息会出现java.lang.IllegalStateException。
最初就是加synchronized,但是并发高时阻塞严重。ConcurrentWebSocketSessionDecorator,这是 Spring WebSocket 提供的一个装饰器类,用于增强底层的 WebSocketSession 的线程安全性。它通过并发安全的方式包装原始的 WebSocketSession 对象,确保在多线程环境下安全地访问和修改会话属性,以及进行消息发送操作。
但是看ConcurrentWebSocketSessionDecorator代码发现一个问题:
public void sendMessage(WebSocketMessage<?> message) throws IOException {
if (!this.shouldNotSend()) {
this.buffer.add(message);
this.bufferSize.addAndGet(message.getPayloadLength());
if (this.preSendCallback != null) {
this.preSendCallback.accept(message);
}
do {
if (!this.tryFlushMessageBuffer()) {//尝试发送失败后退出了循环
if (logger.isTraceEnabled()) {
logger.trace(String.format("Another send already in progress: session id '%s':, \"in-progress\" send time %d (ms), buffer size %d bytes", this.getId(), this.getTimeSinceSendStarted(), this.getBufferSize()));
}
this.checkSessionLimits();
break;
}
} while(!this.buffer.isEmpty() && !this.shouldNotSend());
}
}
private boolean tryFlushMessageBuffer() throws IOException {
if (!this.flushLock.tryLock()) {//获取锁失败直接返回
return false;
} else {
try {
while(true) {
WebSocketMessage<?> message = (WebSocketMessage)this.buffer.poll();
if (message == null || this.shouldNotSend()) {
return true;
}
this.bufferSize.addAndGet(-message.getPayloadLength());
this.sendStartTime = System.currentTimeMillis();
this.getDelegate().sendMessage(message);
this.sendStartTime = 0L;
}
} finally {
this.sendStartTime = 0L;
this.flushLock.unlock();
}
}
}
在获取锁失败,未能发送消息后退出了,除了检查一些限制checkSessionLimits后并没有做其他操作,虽然未能发送的消息保存在buffer队列中,下次可以再发。但是万一就是最后一条消息,不会再有下次发送,对某个客户端来说直到连接断开都不会再调用sendMessage()方法了,那放在buffer队列中的消息就一直没发出去。。。虽然自己可以做一些重调机制。。。