泽西服务器发送的事件-写入断开的连接不会引发异常 [英] Jersey Server-Sent Events - write to broken connection does not throw exception

查看:109
本文介绍了泽西服务器发送的事件-写入断开的连接不会引发异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在使用Jersey服务器发送的事件(SSE),以允许应用程序的远程组件侦听Jersey/Tomcat服务器引发的事件.效果很好.

We are using Jersey Server-Sent Events (SSE) to allow remote components of our application to listen to events raised by our Jersey/Tomcat server. This works great.

但是,至关重要的是,我们的服务器必须具有当前连接的侦听器(我们的远程组件)的准确列表.为此,我们的服务器每五秒钟发送一次微小的消息给每个调用者(通过eventOutput.write).如果在连接SSE时关闭了远程组件,或者在连接SSE时关闭了远程计算机,则服务器的eventOutput.write会引发如下所示的ClientAbortException/SocketException异常.完美:我们捕获到异常,将呼叫者标记为不再连接,然后继续前进.

However, it is crucial that our server have an accurate list of currently-connected listeners (our remote components). To this end, our server sends a tiny message to each caller (via eventOutput.write) once every five seconds. If our remote component is shut down while SSE-connected, or if the remote computer is powered off while SSE-connected, our server's eventOutput.write throws the ClientAbortException/SocketException exception shown below. That's perfect: we catch the exception, mark that caller as no longer connected, and move on.

现在,解决问题.正如我所提到的,如果我们的远程组件软件未运行,或者运行它的计算机已关闭电源,则eventOutput.write会引发异常.但是,在两种情况下,调用eventOutput.write到不再连接的计算机上不会引发异常:1)如果在调用者连接SSE时简单地拉开了远程计算机的以太网电缆,则2)当呼叫者连接到SSE时,远程计算机中的网络适配器将关闭(即,通过管理操作).在这两种情况下,我们可以每五秒钟将eventOutput.write调用到远程计算机上达几个小时,并且不会引发异常.这使得无法检测到远程计算机不再连接.

Now, for the problem. As I mentioned, eventOutput.write throws an exception in cases where our remote component software is not running, or where the computer it runs on has been powered down. However, there are two cases where calling eventOutput.write to a no-longer-connected computer does NOT throw an exception: 1) if the Ethernet cable of the remote computer is simply pulled while the caller is SSE-connected, and 2) if the network adapter in the remote computer is turned off (i.e., by an administrative action) while the caller is SSE-connected. In these two cases, we can call eventOutput.write to the remote computer every five seconds for hours and no exception is thrown. This makes it impossible to detect that the remote computer is no longer connected.

我看到EventOutput(和ChunkedOutput)具有很少的方法和属性,但是我想知道是否有任何方法可以配置或使用它,从而导致在写入已断开连接的远程计算机时引发异常.拔掉以太网电缆或关闭网络适配器.

I see that EventOutput (and ChunkedOutput) has very few methods and properties, but I wonder if there is any way to configure or use it that will cause an exception to be thrown when writing to a remote computer that has been disconnected by having its Ethernet cable pulled or network adapter turned off.

这是在eventOutput.write确实抛出我们想要的异常的情况下得到的(好/有用)异常:

And here is the (good/useful) exception we get in cases where eventOutput.write DOES throw the exception we want:

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

推荐答案

即使泽西岛添加了可从套接字接口访问的所有信息,我也不可能通过在SSE套接字周围添加代码来解决所有可能的失败. .唯一可行的解​​决方案是正确的双向通信.对于SSE分块的输出流,拉出的电缆不会造成任何中断,因为不应有任何消息告诉它远程主机现在无法访问(直到操作系统关闭套接字).

I do not think it will be possible to account for all the possible failures by adding the code around SSE sockets even if Jersey adds all information accessible from the socket interface. The only viable solution is proper two way communication. In case of SSE chunked output stream, pulled cable does not cause any interruption because nothing is supposed to tell it that remote host is now unreachable (until OS closes the socket).

您的第一步是正确的-每N秒执行一次心跳.然后,您需要做的就是每隔很久就报告另一个微小的http调用,以使您仍然可以收听.由您决定每5秒或每分钟进行一次确认-取决于您需要多快的时间来进行问题检测.

Your first step is right - implement heartbeats every N seconds. Then all you need to do is to report back with another tiny http call every so often that you still listen. It is up to you to do acknowledgments every 5 seconds or every minute - depends on how fast do you need a problem detection.

您可以通过实现@POST在相同的Jersey资源中进行操作(以RESTful形式显示为创建接收事件的新确认").

You can do it in the same Jersey Resource by implementing @POST (in RESTful terms it reads "create new ack that you receive events").

注意:浏览器擅长于在网络中断的情况下自行重新建立SSE连接,而无需摆弄它.

Note: browsers are good at re-establishing SSE connection on their own in case of network interrupts, no need to fiddle with it.

这篇关于泽西服务器发送的事件-写入断开的连接不会引发异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆