当前位置: 首页 > 知识库问答 >
问题:

服务器发送的事件是如何工作的

宗政金鹏
2023-03-14

我在tomcat 8.0上使用java尝试了一个SSE(服务器发送事件)。以下是我注意到的几件事。

我单击一个按钮,它会自动向servlet发出请求。执行Servlet的GET方法,返回事件流。一旦收到完整的数据流,页面会再次自动发出另一个请求,再次接收相同的数据!!!我没有无限循环!!!

>

什么是正确的方法来确保事件流只发送一次到同一个连接/浏览器会话?

什么是正确的方法来确保事件流被关闭并且不会在服务器上产生资源开销?

如何区分GET和POST请求。它为什么选择GET?

在Tomcat上使用SSE是否为时过早?任何性能问题?

这是好奇者的密码,

@WebServlet("/TestServlet")
public class TestServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        //content type must be set to text/event-stream
        response.setContentType("text/event-stream"); 
        //cache must be set to no-cache
        response.setHeader("Cache-Control", "no-cache");     
        //encoding is set to UTF-8
        response.setCharacterEncoding("UTF-8");

        PrintWriter writer = response.getWriter();

        for(int i=0; i<10; i++) {
            System.out.println(i);
            writer.write("data: "+ i +"\n\n");
            writer.flush();
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        writer.close(); 
    }
}

页面上有Javascript(页面上没有其他内容),

<button onclick="start()">Start</button>

<script type="text/javascript">
    function start() {
        var eventSource = new EventSource("TestServlet");
        eventSource.onmessage = function(event) {
            console.log("data: "+event.data)
            document.getElementById('foo').innerHTML = event.data;
        };
    }
</script>

用卷发试试这个。而回应只有一次。我用的是chrome,所以这一定是chorme的问题??

编辑:

我学到的和学到的现在都记录在我的博客-服务器发送事件中

共有3个答案

颛孙越
2023-03-14

浏览器尝试在每次连接关闭后大约3秒重新连接到源。您可以通过包含以“重试:”开头的行来更改超时,后跟尝试重新连接前要等待的毫秒数。

胡鸿禧
2023-03-14

我强烈建议首先阅读使用服务器发送事件的流更新,以便对该技术有一个良好的总体理解。然后使用异步Servlet通过示例来跟踪服务器发送事件,了解如何将SSE专门用于Servlet技术。

杜鸿彩
2023-03-14

换线

writer.write("data: "+ i +"\n\n");

writer.write("data: "+ i +"\r\n");

顺便说一句,你的代码将有一个严重的性能问题,因为它将保持一个线程,直到所有事件都被发送。请改用异步处理API。例如

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    AsyncContext actx = req.startAsync();
    actx.setTimeout(30*1000);
    //save actx and use it when we need sent data to the client.
}

然后我们可以稍后使用AsyncContext

//write some data to client when a certain event happens
actx.getResponse().getWriter().write("data: " + mydata + "\r\n");
actx.getResponse().getWriter().flush();

如果所有事件都已发送,我们可以关闭它

actx.complete();

更新1:

如果我们不想在服务器完成响应后再次重新连接服务器,我们需要关闭浏览器上的事件源。

eventSource.close();

另一种方法可能会有所帮助,即。我们设置了一个相当大的重试时间,但我没有尝试过。

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    AsyncContext actx = req.startAsync();
    actx.getResponse().getWriter().write("retry: 36000000000\r\n"); // 10000 hours!
    actx.getResponse().getWriter().flush();
    //save actx and use it when we need sent data to the client.
}

更新2:

我认为网络插座可能更适合你的情况。

更新3:(回答问题)

  1. 服务器上实际发生了什么?在正常情况下,tomcat会创建一个线程来处理每个请求。现在发生了什么

如果使用Tomcat 8.0中默认的NIO连接器。X,在整个处理周期内,关于HTTP I/O的请求不会保持线程。如果使用BIO,线程将被保持,直到整个处理周期完成。所有线程都来自线程池,tomcat不会为每个请求创建线程。

执行eventSource。浏览器端的close()是最佳选择。

不要忘记调用AsyncContext。在服务器端完成()。

浏览器中的EventSource API只支持GET请求,但在服务器端没有这种限制。SSE主要用于从服务器接收事件数据。如果事件发生,浏览器可以及时接收到它,并且不需要创建新的请求来轮询它。如果您需要全双工通信,请尝试SSE的WebSocket instriad。

如果我们使用NIO连接器,应该不会有性能问题

 类似资料:
  • 概述 客户端代码 概述 建立连接 open事件 message事件 error事件 自定义事件 close方法 数据格式 概述 data:数据栏 id:数据标识符 event栏:自定义信息类型 retry:最大间隔时间 服务器代码 参考链接 概述 传统的网页都是浏览器向服务器“查询”数据,但是很多场合,最有效的方式是服务器向浏览器“发送”数据。比如,每当收到新的电子邮件,服务器就向浏览器发送一个“

  • 概述 客户端代码 概述 建立连接 open事件 message事件 error事件 自定义事件 close方法 数据格式 概述 data:数据栏 id:数据标识符 event栏:自定义信息类型 retry:最大间隔时间 服务器代码 参考链接 概述 传统的网页都是浏览器向服务器“查询”数据,但是很多场合,最有效的方式是服务器向浏览器“发送”数据。比如,每当收到新的电子邮件,服务器就向浏览器发送一个“

  • 我到处查看,试图找出传输级别上服务器发送的事件是什么。我还是不确定。 这两个来源都声称它们“只是http”。然而,我至少可以用两种方式来解释这种说法。 当我第一次阅读这些文章时,我假设这意味着服务器在同一端口上向客户机发起请求,之前已经从客户机向服务器发出了一些其他常规http请求。这应该适用于普通家庭路由器的地址转换,但我想知道防火墙在公司环境中的含义。 然而,后来,我尝试在Silverligh

  • 如果我正确理解了服务器发送事件的原则,那么每次客户端注册到EventSource时,它实际上会打开一个到管理事件的资源的新HTTP连接。与其他HTTP请求相反,连接保持活动状态,因此专用于此客户端的服务器进程/线程将一直运行,直到客户端断开连接。 如果我们有1000个客户端使用SSE连接到一个应用程序,该怎么办?我们是否会有1000个进程/线程(做同样的事情)并发运行以处理SSE?我想我错了,但如

  • 我试图让服务器发送的事件与Mozilla Firefox一起工作。给定一个Spring Boot的网络服务 使用Chrome浏览器或Edge(始终是最新版本)可以正常工作。我可以在网络分析器选项卡中看到未完成的请求,并且每秒都会显示一个新的时间戳。 然而,当我使用Firefox(84.0.2或更早版本)时,请求也会显示在网络选项卡中,但不会显示响应头或流数据。当我终止Spring后端时,Firef