尝试同时写入 websocket session 时,Tomcat 抛出 "The remote endpoint was in state [BINARY_FULL_WRITING] ..."

标签 tomcat asynchronous websocket illegalstateexception jsr356

我正在使用 tomcat 8.0.23 来终止我的 websocket 连接。 我有以下代码来处理传入的消息:

@OnMessage
public void onMsg(Session session, byte[] request) {
        executorService.execute(() ->
                session.getAsyncRemote().sendBinary(
                        ByteBuffer.wrap(getResponse(session, request)), result -> {
                            if (!result.isOK()) {
                                LOGGER.catching(result.getException());
                            }
                        }
                ));
}

但我得到以下异常:

Exception in thread "pool-6-thread-10160" java.lang.IllegalStateException: The remote endpoint was in state [BINARY_FULL_WRITING] which is an invalid state for called method
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.checkState(WsRemoteEndpointImplBase.java:1148)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase$StateMachine.binaryStart(WsRemoteEndpointImplBase.java:1101)
    at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.sendBytesByCompletion(WsRemoteEndpointImplBase.java:152)
    at org.apache.tomcat.websocket.WsRemoteEndpointAsync.sendBinary(WsRemoteEndpointAsync.java:65)

看起来,当我尝试同时写入同一个 session 时,tomcat 抛出了该异常。

错误来自this method :

    private void checkState(State... required) {
        for (State state : required) {
            if (this.state == state) {
                return;
            }
        }
        throw new IllegalStateException(
                sm.getString("wsRemoteEndpoint.wrongState", this.state));
    }

我没想到 sendBinary 会抛出那个异常,因为基于 java doc :

sendBinary void sendBinary(ByteBuffer data, SendHandler handler)

Throws: IllegalArgumentException - if either the data or the handler are null.

所以看起来 tomcat 实现检查状态是否为 open this code :

    public synchronized void binaryStart() {
        checkState(State.OPEN);
        state = State.BINARY_FULL_WRITING;
    }

如果它没有打开那么它会抛出异常。

有趣的是,在 java doc 下对于 RemoteEndpoint.Basic(不是 RemoteEndpoint.Async),我们阅读:

If the websocket connection underlying this RemoteEndpoint is busy sending a message when a call is made to send another one, for example if two threads attempt to call a send method concurrently, or if a developer attempts to send a new message while in the middle of sending an existing one, the send method called while the connection is already busy may throw an IllegalStateException.

RemoteEndpoint.Async 没有这样的段落!

现在是问题

在 session 上调用 RemoteEndpoint.Async.sendBinary 而其他东西正在写入同一 session 是 Not Acceptable 吗?

如果 Not Acceptable ,我该如何在尝试写入之前检查远程端点的状态!


更新 1:

看起来有一个 discussion围绕 java.net 上的相同问题。

更新 2:

类似的链接bug report在 apache bugzilla 上。

最佳答案

我遇到了同样的情况。医生是狗屎。这是我的处理方式。

其实我写了一个actor来包装socketSession。当调用发送方法时,它将产生一个事件。每个 actor 都将注册到一个 Looper 中,该 Looper 包含一个工作线程和一个事件队列。与此同时,工作线程不断发送消息。

因此,我将在内部使用 sync-send 方法,actor 模型将确保并发性。

现在的关键问题是关于Looper的数量。你知道,你不能制造太多或太少的线程。但您仍然可以根据您的业务案例估算一个数字,并不断调整它。

关于尝试同时写入 websocket session 时,Tomcat 抛出 "The remote endpoint was in state [BINARY_FULL_WRITING] ...",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33381420/

相关文章:

apache - 如何在apache2服务器上部署j2ee应用

ios - 如何在 Swift 中使函数成为原子函数?

Python Twisted 向代理发送事件信号的最佳方式

Java WebSocket : How can I test a Server WebSocket endpoint without writing a client

Grails Controller 服务 - 用于属性值的 src/groovy 轮询 Controller

java - 尝试从远程 SSL 地址下载和设置证书以进行 WebSocket 连接,但出现 "Signature does not match"异常

java - 如何在 Java 中将多部分文件作为输入流直接流式传输到 AWS S3?

java - tomcat日志文件中的异常

javascript - 处理异步调用

java - Tomcat 忽略我的自定义错误 servlet/页面