4

从表面上看,实现服务器发送的事件是一项非常简单的任务。借用 Mozilla 文档中的示例,客户端代码将类似于

var evtSource = new EventSource("ssedemo.php");
evtSource.onmessage = function(e){//do stuff with e.data here}

我难以理解的是服务器端发生了什么。让我困惑的事情

  • 因此,您想继续从 ssdemo.php 发送事件意味着您需要循环运行它并在它不发送时让它休眠
  • 但默认情况下,Apache 设置为杀死执行时间“太长”的脚本,因此这不可能是无限循环,除非您设置该脚本以这种方式运行
  • 如果我有 10 个用户来请求相同的 SSE 服务 (ssdemo.php),这是否意味着我将有 10 个该循环脚本的实例?

我怀疑我对服务器端代码如何工作的理解应该是有缺陷的或幼​​稚的或两者兼而有之。我将非常感谢任何指向正确方法的指针。

4

2 回答 2

2

你的理解是正确的。PHP 需要继续运行,而在 PHP 中您将需要一个循环,并且您将很快用完免费的 Apache 线程。

如果您需要处理大量连接,则需要使用基于事件的服务器,例如可以处理大量打开连接的 Node.js 或 Tornado。

如果您更愿意使用 PHP,那么部分解决方案是在几秒钟后关闭连接。浏览器将重新连接,因此您将获得轮询和 SSE 的混合。

在 PHP 中,您可以检查sys_getloadavg()以决定是否可以保持连接打开,或者您是否在空闲进程上运行不足。

于 2013-09-01T18:39:02.747 回答
0

当谈到 SSE 时,我一直在寻求回答同样的问题,这是我的研究:

我有一个基本的 javax HttpServlet,它将每隔随机秒将数据打印到 PrintWriter。

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException {
  System.out.println("You just entered the doGetMethod");
  response.setContentType("text/event-stream");
  response.setCharacterEncoding("UTF-8");
  PrintWriter printWriter = null;
  while(true){
    try{
      System.out.println("You just entered the while loop");
      double randomNumber = Math.random()*10000;
      printWriter = response.getWriter();
      printWriter.print("data: " + "[next server time check event in " +
      Math.round(randomNumber/1000) + " seconds]\n");
      printWriter.print("data: " + "Time: " + Calendar.getInstance().getTime() + "\n\n");
      response.flushBuffer();
      Thread.sleep((long)randomNumber);

    } catch (IOException | InterruptedException e){
      e.printStackTrace();
      break;
    }
  }
  System.out.println("Connection was aborted");
}

这是填充 {textarea id="displayTextArea} 元素的脚本

<script>
  var eventSource = null;
  function start(){
    eventSource = new EventSource('http://localhost:8080/SSEServlet');
    eventSource.onopen = function(){displayTextArea.value+='Connected ..' + '\n';};
    eventSource.onmessage = function(message){displayTextArea.value+=message.data + '\n\n';};
    eventSource.onerror = function(){displayTextArea.value+='Error Occurred...' + '\n';};
  }
  function stop(){
    eventSource.close();
  }
  function clearText(){
    displayTextArea.value = '';
  }
</script>

-回答您的第一个问题
如果您在运行应用程序时观察控制台,您会注意到控制台不会打印“您刚刚输入 doGetMethod”,直到您向 servlet 路径发送 HTTP GET 请求。这证实了在有人调用 servlet 之前不会创建 Servlet 实例和 req/resp 对象的理解。servlet 是如何工作的?
-第二和第三个问题:
默认情况下,Tomcat 会为每个连接分配一个线程(Source)。在我当前的配置中,我的程序最多可以连接 6 个连接。每个连接都将创建自己的 servlet 实例,并且当连接打开时,将停留在 while 循环中。当我运行服务器并打开单独的连接,看到不同的时间和随机间隔序列时,这一点得到了证明。虽然我的while循环不是无限的,它一直等到连接关闭,然后抛出异常并中断while循环。连接关闭后,servlet 将关闭。

我这样做的方式是非常业余的 SSE 方式。如果您想为此学习高级库,我会查看jeaSSE

如果有人想要完整的代码

于 2017-07-19T14:36:15.617 回答