3

我正在使用 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)

看起来当我尝试同时写入同一个会话时,tomcat 正在抛出该异常。

错误来自此方法

    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 数据,SendHandler 处理程序)

抛出: IllegalArgumentException - 如果数据或处理程序为空。

所以看起来tomcat实现检查状态是否open这段代码中:

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

如果不是,open那么它将抛出该异常。

有趣的是,在java doc for RemoteEndpoint.Basic(not RemoteEndpoint.Async) 下,我们读到:

如果在调用发送另一个消息时,此 RemoteEndpoint 底层的 websocket 连接正忙于发送消息,例如,如果两个线程尝试同时调用发送方法,或者如果开发人员在中间尝试发送新消息发送一个现有的,在连接已经很忙时调用的发送方法可能会抛出一个 IllegalStateException。

没有这样的段落RemoteEndpoint.Async

现在的问题

RemoteEndpoint.Async.sendBinary在其他内容写入同一会话时调用会话是否不可接受?

如果不可接受,我如何在尝试写入远程端点之前检查它的状态!


更新1:

看起来在 java.net 上围绕同一问题进行了讨论。

更新 2:

链接到关于 apache bugzilla的类似错误报告。

4

1 回答 1

1

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

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

所以,我会在里面使用sync-send方法,actor模型会确保并发。

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

于 2016-09-11T06:08:38.450 回答