使用websocket遇到的一个小问题 The remote endpoint was in state [TEXT_PARTIAL_WRITING] which is an invalid stat

当使用websocket,向前端实时推送消息的时候,遇到如下异常:

2018-06-29 13:42:55 ERROR 988 --- [container-699] org.springframework.data.redis.listener.adapter.MessageListenerAdapter : [TxId :  , SpanId : ] Listener execution failed
org.springframework.data.redis.listener.adapter.RedisListenerExecutionFailedException: Listener method 'crmpushReceive' threw exception; nested exception is java.lang.IllegalStateException: The remote endpoint was in state [TEXT_PARTIAL_WRITING] which is an invalid state for called method
    at org.springframework.data.redis.listener.adapter.MessageListenerAdapter.invokeListenerMethod(MessageListenerAdapter.java:380) [spring-data-redis-1.8.1.RELEASE.jar:?]
    at org.springframework.data.redis.listener.adapter.MessageListenerAdapter.onMessage(MessageListenerAdapter.java:310) [spring-data-redis-1.8.1.RELEASE.jar:?]
    at org.springframework.data.redis.listener.RedisMessageListenerContainer.executeListener(RedisMessageListenerContainer.java:249) [spring-data-redis-1.8.1.RELEASE.jar:?]
    at org.springframework.data.redis.listener.RedisMessageListenerContainer.processMessage(RedisMessageListenerContainer.java:239) [spring-data-redis-1.8.1.RELEASE.jar:?]
    at org.springframework.data.redis.listener.RedisMessageListenerContainer$1.run(RedisMessageListenerContainer.java:967) [spring-data-redis-1.8.1.RELEASE.jar:?]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_65]
Caused by: java.lang.IllegalStateException: The remote endpoint was in state [TEXT_PARTIAL_WRITING] which is an invalid state for called method
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.checkState(WsRemoteEndpointImplBase.java:1224) ~[tomcat-embed-websocket-8.5.11.jar:8.5.11]
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.textPartialStart(WsRemoteEndpointImplBase.java:1182) ~[tomcat-embed-websocket-8.5.11.jar:8.5.11]
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendPartialString(WsRemoteEndpointImplBase.java:222) ~[tomcat-embed-websocket-8.5.11.jar:8.5.11]
    at org.apache.tomcat.websocket.WsRemoteEndpointBasic.sendText(WsRemoteEndpointBasic.java:49) ~[tomcat-embed-websocket-8.5.11.jar:8.5.11]
    at org.springframework.web.socket.adapter.standard.StandardWebSocketSession.sendTextMessage(StandardWebSocketSession.java:203) ~[spring-websocket-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.socket.adapter.AbstractWebSocketSession.sendMessage(AbstractWebSocketSession.java:101) ~[spring-websocket-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at com.msxf.afdi.biz.redis.RedisService.crmpushReceive(RedisService.java:105) ~[classes/:?]
    at sun.reflect.GeneratedMethodAccessor185.invoke(Unknown Source) ~[?:?]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_65]
    at java.lang.reflect.Method.invoke(Method.java:497) ~[?:1.8.0_65]
    at org.springframework.data.redis.listener.adapter.MessageListenerAdapter$MethodInvoker.invoke(MessageListenerAdapter.java:145) ~[spring-data-redis-1.8.1.RELEASE.jar:?]
    at org.springframework.data.redis.listener.adapter.MessageListenerAdapter.invokeListenerMethod(MessageListenerAdapter.java:374) ~[spring-data-redis-1.8.1.RELEASE.jar:?]
    ... 5 more

伪代码如下:

class A{
    public static CopyOnWriteArraySet CONNECTS = new CopyOnWriteArraySet<>();

    public void handlerA(String message){
        for (WebSocketSession session : CONNECTS ) {
            session.sendMessage(new TextMessage(message));
        }
    }

    public void handlerB(String message){
        for (WebSocketSession session : CONNECTS ) {
            session.sendMessage(new TextMessage(message));
        }
    }
}

其实原因就是: handlerA和handlerB两个方法有可能同时执行,当A或者B方法遍历到某一个session并且调用sendMessage发送消息的时候,另外一个方法也正好也在使用相同的session发送另外一个消息(同一个session消息发送冲突了,也就是说同一个时刻,多个线程向一个socket写数据冲突了),就会报上面的异常。

解决方法其实很简单,就是在发送消息的时候加上一把锁,(保证一个session在某个时刻不会被调用多次):

class A{
    public static CopyOnWriteArraySet CONNECTS = new CopyOnWriteArraySet<>();

    public void handlerA(String message){
        for (WebSocketSession session : CONNECTS ) {
            synchronized (session) {
                session.sendMessage(new TextMessage(message));
            }
        }
    }

    public void handlerB(String message){
        for (WebSocketSession session : CONNECTS ) {
            synchronized (session) {
                session.sendMessage(new TextMessage(message));
            }
        }
    }
}

注意:如果服务器是配置了https的话, websocket需要用wss协议

你可能感兴趣的:(Java)