Java Servlet 和 SSE 中的连接关闭

Posted

技术标签:

【中文标题】Java Servlet 和 SSE 中的连接关闭【英文标题】:Connection close in Java Servlet and SSE 【发布时间】:2014-08-29 16:47:29 【问题描述】:

我尝试使用服务器上的 Java Serlvet 在我的 Web 应用程序中实现 Server-Sent-Event。

是否可以在 Servlet 中检查连接已被客户端关闭?即使客户端浏览器关闭,Servlet 中的循环while(true) 也是无限的。

客户端代码

    function startLogSSE(lastEventId, level) 
        var eventSource = new EventSource("log-sse?last-event-id=" + lastEventId + "&level=" + level);
        eventSource.onmessage = function (event) 
            document.getElementById('log').innerhtml = event.data + "\n" + document.getElementById('log').innerHTML;
        ;
    

服务器代码

public class LogSSEServlet extends HttpServlet 
    private static final Logger logger = LoggerFactory.getLogger(LogSSEServlet.class);

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("UTF-8");

        PrintWriter writer = response.getWriter();

        // get logger purgerDB appender
        PurgerDBAppender appender = LogUtils.getPurgerDBAppender();
        if (appender == null) 
            writer.write("data: [ERROR] Appender 'purgerDB' isn't found for logger 'com.bp3'\n\n");
            writer.close();
            return;
        

        int eventId = 0;
        // get last-event-id
        String lastEventId = request.getHeader("last-event-id");
        if (lastEventId == null) 
            // try to get lastEventId from parameter
            lastEventId = request.getParameter("last-event-id");
        
        if (lastEventId != null) 
            try 
                eventId = Integer.parseInt(lastEventId);
             catch (NumberFormatException e) 
                logger.error("Failed to parse last-event-id: " + lastEventId);
            
        
        String minLevel = request.getParameter("level");
        if (minLevel == null) 
            minLevel = "TRACE";
        

        // get logs from purgerDB logger appender
        LogServices logServices = new LogServices();
        try 
            logServices.open();
         catch (SQLException e) 
            throw new ServletException(e);
        
        try 
            while (true) 
                List<LogMessage> messages = logServices.getLastMessages(Level.toLevel(minLevel), eventId, 0);
                if (messages.size() > 0) 
                    writer.write("id: " + messages.get(0).getEventId() + "\n");
                    writer.write("data: " + LogUtils.formatLog(messages) + "\n");
                    writer.flush();
                
                try 
                    Thread.sleep(100);
                 catch (InterruptedException e) 
                    break;
                
            
         catch (SQLException e) 
            throw new ServletException(e);
         finally 
            logServices.closeQuietly();
        
    

【问题讨论】:

在我看来,您正在尝试重新实现 WebSockets。或者彗星。 如果您试图保持打开状态,请在“持久连接”上进行一些谷歌搜索,否则默认情况下,我相信一旦服务器传输了所有数据,连接就应该关闭。要创建持久连接,请参阅***.com/questions/3304006/… 【参考方案1】:

是否可以在 Servlet 中检查连接已被客户端关闭?

最终会抛出异常:IOException: connection reset 如果您直接流式传输到套接字,或者 OutOfMemoryError 如果容器正在流式传输到内存,当您不使用固定长度时会抛出异常或分块传输模式。

即使客户端浏览器关闭,Servlet 中的 while(true) 循环也是无限的。

不,不是。

【讨论】:

是的,它是:)。不会抛出异常。我也预料到了。使用的应用服务器:WebSphere Application Server Liberty Profile 嗨,@sasha_trn,您能与我们分享您对这个问题的结论吗?谢谢! 嗨@Ricardo,那是很久以前的事了。 AFAIR,我没有找到一种方法来确定客户端关闭连接。我在没有无限循环的情况下更改了代码。 谢谢,@sasha_trn,来自HttpServletHttpServletRequestHttpServletResponse source codes,我没有看到任何关于连接的信息,也没有Exception。最好的,【参考方案2】:

Servlet 中检查连接是否关闭的一种方法是使用writer.checkError() 方法。我在 Chrome 上测试了这个修复程序,它可以工作。您的代码将是:

            boolean error=false;
            while (!error) 
                //...                   
                writer.write("data: " + /*...*/  "\n");
                //writer.flush();
                error = writer.checkError();    //internally calls writer.flush()
            

详情

PrintWriter's API 说:

这个类中的方法从不抛出 I/O 异常,尽管它的一些 构造函数可能。客户可以询问是否有任何错误 通过调用 checkError() 发生。

checkError() 说:

如果流未关闭,则刷新流并检查其错误状态

【讨论】:

感谢我的朋友 Shuge Li 和 Enyong Chen,还没有加入这个社区。​​span>

以上是关于Java Servlet 和 SSE 中的连接关闭的主要内容,如果未能解决你的问题,请参考以下文章

使用 Java servlet 时何时打开以及何时关闭 mysql 连接?

Ktor sse 客户端断开连接

stop tomcat web application 优雅地关闭所有 servlet 连接

如何在 Spring Boot 应用程序中关闭客户端 sse 连接

java servlet中的空指针异常[关闭]

在 Java 8 中释放打开的连接