我们正在使用Jersey服务器发送事件(SSE)来允许我们的应用程序的远程组件侦听由Jersey / Tomcat服务器引发的事件。这很有效。
但是,我们的服务器必须具有当前连接的侦听器(我们的远程组件)的准确列表。为此,我们的服务器每五秒钟向每个调用者发送一条微小的消息(通过eventOutput.write)。如果我们的远程组件在SSE连接时关闭,或者在SSE连接时远程计算机关闭,我们的服务器的eventOutput.write会抛出下面显示的ClientAbortException / SocketException异常。这是完美的:我们捕获异常,将呼叫者标记为不再连接,然后继续前进。
现在,针对这个问题。正如我所提到的,eventOutput.write会在我们的远程组件软件未运行或运行它的计算机已关闭的情况下引发异常。但是,有两种情况,将eventOutput.write调用到不再连接的计算机不会引发异常:1)如果远程计算机的以太网电缆只是在呼叫者连接SSE时被拉动,2)如果当呼叫者进行SSE连接时,远程计算机中的网络适配器被关闭(即通过管理操作)。在这两种情况下,我们可以每隔五秒钟将eventOutput.write调用到远程计算机上几个小时,并且不会抛出任何异常。这使得无法检测到远程计算机不再连接。
我看到EventOutput(和ChunkedOutput)只有很少的方法和属性,但是我想知道是否有任何方法可以配置或使用它,这会导致在写入已断开连接的远程计算机时抛出异常拔掉以太网线或关闭网络适配器。
以下是在eventOutput.write抛出我们想要的异常的情况下得到的(好/有用)异常:
org.apache.catalina.connector.ClientAbortException: null
at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:371) ~[catalina.jar:7.0.53]
at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:333) ~[catalina.jar:7.0.53]
at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:101) ~[catalina.jar:7.0.53]
at org.glassfish.jersey.servlet.internal.ResponseWriter$NonCloseableOutputStreamWrapper.flush(ResponseWriter.java:303) ~[jaxrs-ri-2.13.jar:2.13.]
at org.glassfish.jersey.message.internal.CommittingOutputStream.flush(CommittingOutputStream.java:292) ~[jaxrs-ri-2.13.jar:2.13.]
at org.glassfish.jersey.server.ChunkedOutput$1.call(ChunkedOutput.java:240) ~[jaxrs-ri-2.13.jar:2.13.]
at org.glassfish.jersey.server.ChunkedOutput$1.call(ChunkedOutput.java:190) ~[jaxrs-ri-2.13.jar:2.13.]
at org.glassfish.jersey.internal.Errors.process(Errors.java:315) ~[jaxrs-ri-2.13.jar:2.13.]
at org.glassfish.jersey.internal.Errors.process(Errors.java:242) ~[jaxrs-ri-2.13.jar:2.13.]
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:347) ~[jaxrs-ri-2.13.jar:2.13.]
at org.glassfish.jersey.server.ChunkedOutput.flushQueue(ChunkedOutput.java:190) ~[jaxrs-ri-2.13.jar:2.13.]
at org.glassfish.jersey.server.ChunkedOutput.write(ChunkedOutput.java:180) ~[jaxrs-ri-2.13.jar:2.13.]
at com.appserver.webservice.AgentSsePollingManager$ConnectionChecker.run(AgentSsePollingManager.java:174) ~[AgentSsePollingManager$ConnectionChecker.class:na]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) [na:1.7.0_71]
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304) [na:1.7.0_71]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178) [na:1.7.0_71]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293) [na:1.7.0_71]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [na:1.7.0_71]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [na:1.7.0_71]
at java.lang.Thread.run(Thread.java:745) [na:1.7.0_71]
Caused by: java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method) ~[na:1.7.0_71]
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:113) ~[na:1.7.0_71]
at java.net.SocketOutputStream.write(SocketOutputStream.java:159) ~[na:1.7.0_71]
at org.apache.coyote.http11.InternalOutputBuffer.realWriteBytes(InternalOutputBuffer.java:215) ~[tomcat-coyote.jar:7.0.53]
at org.apache.tomcat.util.buf.ByteChunk.flushBuffer(ByteChunk.java:480) ~[tomcat-coyote.jar:7.0.53]
at org.apache.coyote.http11.InternalOutputBuffer.flush(InternalOutputBuffer.java:119) ~[tomcat-coyote.jar:7.0.53]
at org.apache.coyote.http11.AbstractHttp11Processor.action(AbstractHttp11Processor.java:799) ~[tomcat-coyote.jar:7.0.53]
at org.apache.coyote.Response.action(Response.java:174) ~[tomcat-coyote.jar:7.0.53]
at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:366) ~[catalina.jar:7.0.53]
... 19 common frames omitted
答案 0 :(得分:0)
我认为通过在SSE套接字周围添加代码来解决所有可能的故障是不可能的,即使Jersey添加了可从套接字接口访问的所有信息。唯一可行的解决方案是正确的双向通信。在SSE分块输出流的情况下,拉电缆不会导致任何中断,因为没有任何东西可以告诉它远程主机现在无法访问(直到OS关闭套接字)。
你的第一步是正确的 - 每N秒实施一次心跳。然后你需要做的就是每隔一段时间用另一个微小的http电话报告,你仍然会听。您需要每5秒或每分钟做一次确认 - 取决于您需要多快检测问题。
你可以通过实现@POST在同一个Jersey资源中执行它(在RESTful术语中它读取"创建新的ack,你收到事件")。
注意:浏览器擅长在网络中断的情况下自行重建SSE连接,无需捣乱。