Jakarta ee Tyrus websocket:IllegalStateException无法为非异步请求设置WriteListener

Jakarta ee Tyrus websocket:IllegalStateException无法为非异步请求设置WriteListener,jakarta-ee,websocket,grizzly,java-websocket,tyrus,Jakarta Ee,Websocket,Grizzly,Java Websocket,Tyrus,我有一个基于Tyrus实现的标准websocket端点,它会不时触发java.lang.IllegalStateException:无法为非异步或非升级请求设置WriteListener。我们正在运行Payara 4.1 我的标准实现 抽象类在哪里 public abstract class AbstractEndpoint{ // irrelevant onOpen, onOpen handling method 117 protected void sendMess

我有一个基于Tyrus实现的标准websocket端点,它会不时触发
java.lang.IllegalStateException:无法为非异步或非升级请求设置WriteListener
。我们正在运行Payara 4.1

我的标准实现 抽象类在哪里

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            }
    } 
}
非法国家例外 到目前为止,没有什么特别的。我可以完美地沟通和响应我收到的请求,而且,websocket FTW,我可以推送信息并获得反馈。但是,我多次收到一个异常:

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方法尝试 起初,我认为我的异步端点配置错误,所以我尝试了未来的方式,而不是回调方式:

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);
}
RemoteEndpoint.Async Async=session.getAsyncRemote();
async.setSendTimeout(5000);//5秒
Future=async.sendObject(消息);
试一试{
future.get();
}
捕获(InterruptedException | ExecutionException ex){
LOGGER.error(“发送消息时出错”,例如);
}
我也有例外

到目前为止,症状和体征 令人惊讶的是,我只发现谈论这个问题

  • github链接突出显示了缓冲区大小问题。我不使用部分消息,只使用整个消息。此外,无论我是使用默认缓冲区大小还是设置新的缓冲区大小,都会出现异常
  • 我找不到关于如何重现错误的全局规则
  • 在引发异常后,客户端可以继续发送消息,服务器将对其进行处理,但服务器从未回复客户端。传出通信通道似乎已被阻塞
  • 由于服务器一直在处理传入消息,因此异常发生后websocket通道不会关闭
  • 挖掘Tyrus实现 我浏览了tyrus核心实现,发现发送方法取决于一些Grizzly组件。我对Grizzly一无所知,但由于Grizzly的一些限制,似乎无论如何发送都必须是同步的

    问题
  • 有人已经遇到这种情况了吗?如果是,异常是否真的意味着某个地方存在瓶颈,还是意味着其他什么
  • tyrus asynchronous端点真的是异步的吗,比如“处理并忘记”
  • 我还没有找到任何方法让消息和传出消息排队:如果消息a很长,请等待消息a发送完成,然后再发送消息B。是否有方法处理websocket中的大型消息,或者异步端点是唯一的方法
  • 我希望确保发送不会遇到任何问题,因此我选择了异步解决方案。我应该回到同步方式吗
  • 我还没有详细说明我对泰勒斯的调查。如果你觉得相关,请随意询问,我会很乐意进一步了解

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

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

    如果过滤器是通过
    web.xml
    中的
    条目注册的,可以通过设置子元素
    注释来完成,也可以通过将其
    asyncSupported
    属性设置为
    true
    来完成

    @WebFilter(..., asyncSupported="true")
    
    Dynamic filter = servletContext.addFilter(name, type);
    filter.setAsyncSupported(true);
    
    如果过滤器是通过注册的,可以通过设置为
    true
    来完成

    @WebFilter(..., asyncSupported="true")
    
    Dynamic filter = servletContext.addFilter(name, type);
    filter.setAsyncSupported(true);
    
    原因是,WebSocket实现在握手请求期间内部使用,以保持请求-响应管道“永远”打开,直到响应显式关闭。他说:

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

    isAsyncSupported()
    默认为
    false
    ,以避免使用实现不佳的servlet过滤器破坏现有的web应用程序。从技术上讲,只将目标
    Servlet
    标记为支持异步,而不使用过滤器就足够了。一个健全的“catch all”
    过滤器
    不会显式地向HTTP响应写入任何内容,但Servlet API从未禁止这样做,因此这种过滤器可能不幸地存在


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

    如果管道中存在未设置asyncSupported=true的servlet筛选器,则请求不能是异步的。您需要首先通过检查web.xml、@WebFilter和ServletContext#addFilter()来排除这一点。所有过滤器都是由我自己的代码处理的吗?我的意思是,如果我使用的是第三方组件(例如Apache Shiro或我不知道的所谓Grizzly),是否有可能存在一些我无法控制的过滤器?在web.xml中以完全相同的过滤器名称重新定义时,任何第三方过滤器都是可重写的。一些反馈,一周后,我再也没有遇到过非法状态例外。所以这就是解决方案!由于我不确定所有的细节,我将等待一个月,然后发布一个自我回答,如果它能帮助其他人。无论如何,非常感谢@BalusC!我回复了这个评论作为回答。巴卢斯克,你能帮我回答这个问题吗?