tomcat websocket FutureToSendHandler TimeoutException

来源:互联网 发布:apache官方下载32位 编辑:程序博客网 时间:2024/06/06 16:37

tomcat websocket推送出现停顿查找日志后发现有以下异常信息



[plain] view plain copy
  1. java.io.IOException: java.util.concurrent.TimeoutException: Operation timed out after waiting [20,000] [milliseconds] to complete  
  2.     at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.handleSendFailureWithEncode(WsRemoteEndpointImplBase.java:558)  
  3.     at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.handleSendFailure(WsRemoteEndpointImplBase.java:533)  
  4.     at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessageBlock(WsRemoteEndpointImplBase.java:285)  
  5.     at org.apache.tomcat.websocket.WsSession.sendCloseMessage(WsSession.java:600)  
  6.     at org.apache.tomcat.websocket.WsSession.doClose(WsSession.java:490)  
  7.     at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.onError(WsHttpUpgradeHandler.java:149)  
  8.     at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.access$300(WsHttpUpgradeHandler.java:47)  
  9.     at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onError(WsHttpUpgradeHandler.java:206)  
  10.     at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:189)  
  11.     at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:198)  
  12.     at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:96)  
  13.     at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:661)  
  14.     at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520)  
  15.     at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476)  
  16.     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)  
  17.     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)  
  18.     at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)  
  19.     at java.lang.Thread.run(Thread.java:745)  
  20. Caused by: java.util.concurrent.TimeoutException: Operation timed out after waiting [20,000] [milliseconds] to complete  
  21.     at org.apache.tomcat.websocket.FutureToSendHandler.get(FutureToSendHandler.java:116)  
  22.     at org.apache.tomcat.websocket.WsRemoteEndpointImplBase.startMessageBlock(WsRemoteEndpointImplBase.java:278)  
  23.     ... 15 more  


tomcat的阻塞等待了20秒导致系统无法继续推送


于是我跟踪TOMCAT源代码后发现是tomcat websocket的配置问题。


tomcat官网给出如下解释



The JSR-356 Java WebSocket 1.1 implementation is only available when Tomcat is running on java7 or later.

Tomcat provides a number of Tomcat specific configuration options for WebSocket. It is anticipated that these will be absorbed into the WebSocket specification over time.

The write timeout used when sending WebSocket messages in blocking mode defaults to 20000 milliseconds (20 seconds). This may be changed by setting the property org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT in the user properties collection attached to the WebSocket session. The value assigned to this property should be a Longand represents the timeout to use in milliseconds. For an infinite timeout, use -1.



于是我们在项目的web.xml中增加了org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT的配置,以为如此就能解决问题

[html] view plain copy
  1. <span style="white-space:pre">          </span><context-param>  
  2.                 <param-name>org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT</param-name>  
  3.                 <param-value>500</param-value>  
  4.             </context-param>  

但是加完了之后问题依旧,又仔细阅读了下官网说明发现这玩意不是配在context-param中的,说是配在user properties中。那是个啥玩意。

于是又翻遍了tomcat源码,org.apache.tomcat.websocket.WsRemoteEndpointImplBase类里有如下获取timeout的代码


[java] view plain copy
  1. private long getBlockingSendTimeout() {  
  2.       
  3.        Object obj = wsSession.getUserProperties().get(  
  4.                BLOCKING_SEND_TIMEOUT_PROPERTY);  
  5.        Long userTimeout = null;  
  6.        if (obj instanceof Long) {  
  7.            userTimeout = (Long) obj;  
  8.        }  
  9.        if (userTimeout == null) {  
  10.            return DEFAULT_BLOCKING_SEND_TIMEOUT;  
  11.        } else {  
  12.         System.out.println(BLOCKING_SEND_TIMEOUT_PROPERTY+":"+userTimeout);  
  13.            return userTimeout.longValue();  
  14.        }  
  15.    }  
其实就是在wsSession中有个userProperties的map,把属性put进去就好了。

然后就简单了,在你websocket的类的@OnOpen方法内加入以下代码即可,注意后面的时间一定要是Long型,否则上面的代码会判断不出来。

[java] view plain copy
  1. session.getUserProperties().put( "org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT",1000L);  

[java] view plain copy
  1. @OnOpen  
  2.   public void start(Session session) {  
  3.     session.getUserProperties().put( "org.apache.tomcat.websocket.BLOCKING_SEND_TIMEOUT",1000L);  
  4.       this.session = session;  
  5.       connections.add(this);  
  6.       String message = String.format("* %s %s", nickname, "has joined.");  
  7.       broadcast(message);  
  8.   }  
原创粉丝点击