Java bayeux 客户端在空闲时与 TimeoutException 断开连接
Posted
技术标签:
【中文标题】Java bayeux 客户端在空闲时与 TimeoutException 断开连接【英文标题】:Java bayeux client disconnects with TimeoutException when idle 【发布时间】:2019-07-14 07:54:54 【问题描述】:我正在初始化 Bayeux 客户端:
SslContextFactory sslContextFactory = new SslContextFactory(true);
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.start();
Map<String, Object> options = new HashMap<String, Object>();
ClientTransport transport = new LongPollingTransport(options, httpClient);
BayeuxClient client = new BayeuxClient("https://localhost:8483/cometd/", transport);
client.handshake();
boolean handshaken = client.waitFor(20000, BayeuxClient.State.CONNECTED);
if (!handshaken)
LOGGER.info("Failed to handshake");
throw new RuntimeException("Failed to handshake");
我用它与服务器进行一些通信,它可以工作,它订阅频道、发送、接收,然后我让它闲置一段时间。我得到以下异常:
java.util.concurrent.TimeoutException: Idle timeout 20000 ms
at org.eclipse.jetty.client.http.HttpConnectionOverHTTP.onIdleExpired(HttpConnectionOverHTTP.java:145)
at org.eclipse.jetty.io.ssl.SslConnection.onIdleExpired(SslConnection.java:286)
at org.eclipse.jetty.io.AbstractEndPoint.onIdleExpired(AbstractEndPoint.java:401)
at org.eclipse.jetty.io.IdleTimeout.checkIdleTimeout(IdleTimeout.java:166)
at org.eclipse.jetty.io.IdleTimeout$1.run(IdleTimeout.java:50)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
以及以下多次:
java.nio.channels.ClosedChannelException: null
at org.eclipse.jetty.io.WriteFlusher.onClose(WriteFlusher.java:502)
at org.eclipse.jetty.io.AbstractEndPoint.onClose(AbstractEndPoint.java:353)
at org.eclipse.jetty.io.ChannelEndPoint.onClose(ChannelEndPoint.java:216)
at org.eclipse.jetty.io.AbstractEndPoint.doOnClose(AbstractEndPoint.java:225)
at org.eclipse.jetty.io.AbstractEndPoint.close(AbstractEndPoint.java:192)
at org.eclipse.jetty.io.AbstractEndPoint.close(AbstractEndPoint.java:175)
at org.eclipse.jetty.io.ssl.SslConnection$DecryptedEndPoint.doClose(SslConnection.java:1132)
at org.eclipse.jetty.io.AbstractEndPoint.doOnClose(AbstractEndPoint.java:220)
at org.eclipse.jetty.io.AbstractEndPoint.close(AbstractEndPoint.java:192)
at org.eclipse.jetty.io.AbstractEndPoint.close(AbstractEndPoint.java:175)
at org.eclipse.jetty.client.http.HttpConnectionOverHTTP.close(HttpConnectionOverHTTP.java:195)
at org.eclipse.jetty.client.http.HttpConnectionOverHTTP.onIdleExpired(HttpConnectionOverHTTP.java:145)
at org.eclipse.jetty.io.ssl.SslConnection.onIdleExpired(SslConnection.java:286)
at org.eclipse.jetty.io.AbstractEndPoint.onIdleExpired(AbstractEndPoint.java:401)
at org.eclipse.jetty.io.IdleTimeout.checkIdleTimeout(IdleTimeout.java:166)
at org.eclipse.jetty.io.IdleTimeout$1.run(IdleTimeout.java:50)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
如果我保持忙碌,它就不会失败。我尝试更改 httpclient 的超时时间,但这只会延迟问题。为什么 Bayeux 关闭通道而不是进行轮询?我使用的是最新版本,org.cometd.java 4.0.2。
另外需要注意的是,我有一个可以正常工作的 javascript 客户端。
有人可以帮忙吗?
【问题讨论】:
【参考方案1】:原来我阻塞了 cometD 线程:
我构建了一个命令行工具来检查服务器,当我收到一条消息(带有来自 cometD 的线程)时,我正在为用户输入保留该线程。如果我持有该线程足够长的时间,cometD 将与上述异常断开连接。
解决方法:收到 CometD 的消息时,不要憋着,换个新线程。
【讨论】:
【参考方案2】:默认心跳在服务器端由timeout
参数控制,默认为30秒。
这意味着当系统空闲时,长轮询每 30 秒发生一次。
您已将客户端 idleTimeout
配置为 20 秒,从您的异常堆栈跟踪中可以明显看出。
当系统空闲时,客户端会在心跳(长轮询)响应之前超时连接,导致你看到的错误。
您应该将客户端idleTimeout
配置为大于心跳timeout
的值,或者 - 等效地 - 将心跳timeout
设置为小于客户端idleTimeout
的值。
【讨论】:
我放了任何 idleTimeout,如果我放得更大,我不再得到 TimeoutException,但我仍然得到 ClosedChannelException。 在***这里很难排查。欢迎您到open an issue at the CometD project 继续讨论。一个疯狂的猜测是您在客户端和正在关闭连接的 CometD 服务器之间有其他东西,或者您没有在客户端和服务器中正确配置空闲超时。打开一个问题并附加调试日志。您尝试过deploy the CometD demo 并且有效吗?以上是关于Java bayeux 客户端在空闲时与 TimeoutException 断开连接的主要内容,如果未能解决你的问题,请参考以下文章
是否有任何 iOS 实现 Bayeux 协议(COMET,服务器推送到客户端)?
Cometd/bayeux 客户端 + Salesforce 流 API 问题