首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Tyrus : IllegalStateException不能为非异步请求设置WriteListener。

Tyrus : IllegalStateException不能为非异步请求设置WriteListener。
EN

Stack Overflow用户
提问于 2017-08-03 14:10:04
回答 1查看 872关注 0票数 1

我有一个基于Tyrus实现的标准websocket端点,它的时间会触发java.lang.IllegalStateException: Cannot set WriteListener for non-async or non-upgrade request。我们在Payara 4.1上运行。

我的标准实现

代码语言:javascript
复制
@ServerEndpoint(value = "...", decoders=MessageDecoder.class, encoders=MessageEncoder.class)
public class EndpointImpl extends AbstractEndpoint{
    // onOpen, onClose, onMessage, onError methods
}

抽象类在哪里

代码语言:javascript
复制
public abstract class AbstractEndpoint{

    // irrelevant onOpen, onOpen handling method

117        protected void sendMessage(Session session, Message message){
118            if(message == null){
119                LOGGER.error("null message");
120            } else if(!session.isOpen()){
121                LOGGER.error("session is not opened");
122            } else{
>>>123                session.getAsyncRemote().sendObject(message, (result) -> {
124                    if (result.isOK()) {
125                        LOGGER.info("success! yeah!");
126                    } else {
127                        LOGGER.error("error when sending message", result.getException());
128                    }
129                });
130            }
    } 
}

IllegalStateException

到目前为止没什么特别的。我可以很好地沟通和回应我收到的请求,websocket,我可以推送信息并得到反馈。但是,我有时会收到一个例外:

代码语言:javascript
复制
java.lang.IllegalStateException: Cannot set WriteListener for non-async or non-upgrade request
        at org.apache.catalina.connector.OutputBuffer.setWriteListener(OutputBuffer.java:536)
        at org.apache.catalina.connector.CoyoteOutputStream.setWriteListener(CoyoteOutputStream.java:223)
        at org.glassfish.tyrus.servlet.TyrusServletWriter.write(TyrusServletWriter.java:140)
        at org.glassfish.tyrus.core.ProtocolHandler.write(ProtocolHandler.java:486)
        at org.glassfish.tyrus.core.ProtocolHandler.send(ProtocolHandler.java:274)
        at org.glassfish.tyrus.core.ProtocolHandler.send(ProtocolHandler.java:332)
        at org.glassfish.tyrus.core.TyrusWebSocket.sendText(TyrusWebSocket.java:317)
        at org.glassfish.tyrus.core.TyrusRemoteEndpoint.sendSyncObject(TyrusRemoteEndpoint.java:429)
        at org.glassfish.tyrus.core.TyrusRemoteEndpoint$Async.sendAsync(TyrusRemoteEndpoint.java:352)
        at org.glassfish.tyrus.core.TyrusRemoteEndpoint$Async.sendObject(TyrusRemoteEndpoint.java:249)
        at com.mycompany.websocket.AbstEndpoint.sendMessage(AbstEndpoint.java:123)

第二次sendMessage方法尝试

起初,我认为异步端点配置错误,所以我尝试了Future<>方式,而不是回调方式:

代码语言:javascript
复制
RemoteEndpoint.Async async = session.getAsyncRemote();
async.setSendTimeout(5000); // 5 seconds
Future<Void> future = async.sendObject(message);
try{
    future.get();
}
catch(InterruptedException | ExecutionException ex){
    LOGGER.error("error when sending message", ex);
}

我也有例外。

到目前为止还有症状

令人惊讶的是,我只发现一个环节在谈论这个问题。

  1. github链接突出了缓冲区大小问题。我不使用部分信息,只使用全部信息。此外,不管我使用的是默认缓冲区大小还是设置了新缓冲区大小,都会出现异常
  2. 我找不到关于如何重现错误的全局规则。
  3. 引发异常后,客户端可以继续发送消息,服务器将处理它,但服务器从未回复客户端。看来传出通信通道被阻塞了。
  4. 由于服务器一直在处理传入的消息,所以在出现异常后,websocket通道不会关闭。

挖掘Tyrus的实现

我浏览了tyrus-core实现,发现发送方法依赖于某些Grizzly组件。我对灰熊一无所知,但是由于灰熊的限制,发送必须是同步的。

问题

  1. 有人已经遇到这样的情况了吗?如果是,异常是否真的意味着某个地方存在瓶颈,还是意味着其他什么?
  2. tyrus异步端点真的是异步的吗?
  3. 我还没有找到让消息和传出消息排队的任何方法:如果消息A很长,在发送消息B之前等待消息A发送完成。是否有一种方法可以处理websocket中的大型消息,或者异步端点是唯一的方法?
  4. 我想确保发送没有遇到任何问题,因此我选择了异步解决方案。我应该回到同步的方式吗?

我还没有详细说明我的泰勒斯调查。如果你觉得这是相关的,请随便问,我会乐意发展的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-08-13 20:30:00

java.lang.IllegalStateException:无法为非异步或非升级请求设置WriteListener

为了使请求完全异步,必须显式地设置请求响应链中的 Filter以支持异步请求。特别是那些映射在/*上的“所有”过滤器。

如果过滤器是通过<filter>条目在web.xml中注册的,这可以通过将子元素<async-supported>设置为true来完成。

代码语言:javascript
复制
<filter>
    ...
    <async-supported>true</async-supported>
</filter>

如果过滤器是通过@WebFilter注释注册的,可以通过将其asyncSupported属性设置为true来完成。

代码语言:javascript
复制
@WebFilter(..., asyncSupported="true")

如果过滤器是通过ServletContext#addFilter()注册的,可以通过将设置为true来完成。

代码语言:javascript
复制
Dynamic filter = servletContext.addFilter(name, type);
filter.setAsyncSupported(true);

原因是,WebSocket实现在握手请求期间内部使用ServletRequest#startAsync(),以便将请求-响应管道“永远”打开,直到响应显式关闭。它的javadoc说:

抛出 IllegalStateException -如果此请求位于不支持异步操作的筛选器或servlet的范围内(即isAsyncSupported()返回false),或者如果该方法再次被调用而没有任何异步分派(由AsyncContext.dispatch()方法之一产生),则在任何此类分派范围之外调用,或者在同一调度范围内再次调用,或者如果响应已经关闭。

isAsyncSupported()默认为false,以避免使用实现不佳的servlet过滤器破坏现有的web应用程序。从技术上讲,只将目标Servlet标记为异步支持就足够了,而不需要使用过滤器。一个正常的“所有捕获”的Filter不会显式地向HTTP响应写入任何东西,但是Servlet从来没有禁止这种情况,因此不幸的是,这样的过滤器可能存在。

如果您有一个这样的过滤器,那么您应该修复它,不再向响应写入任何东西,这样您就可以安全地标记它以支持异步请求,或者调整它的URL模式以不覆盖WebSocket请求。也就是说,不要再在/*上映射它了。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45486679

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档