从表面上看,实现服务器发送的事件是一项非常简单的任务。借用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个循环脚本的实例
我怀疑我对服务器端代码如何工作、应该如何编码的理解要么有缺陷,要么幼稚,要么两者兼而有之。如果能给我提供正确的方法,我将不胜感激。
您的理解是正确的。PHP需要继续运行,在PHP中,您需要一个循环,很快就会用完免费的Apache线程。
如果你需要处理大量的连接,你需要使用基于事件的服务器,比如Node.js或Tornado,它可以处理大量的开放连接。
如果您更喜欢使用PHP,那么部分解决方案是在几秒钟后关闭连接。浏览器将重新连接,因此您将获得轮询和SSE的混合。
在PHP中,您可以检查sys_getloadavg()
来决定是可以保持连接打开,还是缺少可用进程。
当涉及到SSE时,我一直在寻求回答同样的问题,以下是我的研究:
我有一个基本的javaxHttpServlet,它将每rando秒将数据打印到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>
-回答您的第一个问题:
如果您在运行应用程序时观察控制台,您会注意到控制台在向servlet路径发送HTTP GET请求之前不会打印"you just entered the doGetMethod"。这证实了这样一种理解,即Servlet实例和req/resp对象在有人调用Servlet之前不会创建。servlet是如何工作的
-第二个和第三个问题:
默认情况下,Tomcat将为每个连接分配一个线程(Source)。在我目前的配置中,我的程序最多可以连接6个。每个连接都将创建自己的servlet实例,并且在连接打开时,将停留在while循环中。当我运行服务器并打开单独的连接,看到不同的时间和随机间隔序列时,这一点得到了证明。我的while循环不是无限的,尽管它会等待连接关闭,然后抛出异常并中断while循环。连接关闭后,servlet将关闭。
我做这件事的方式是非常业余的SSE方式。如果你想为此研究一个高级图书馆,我会查看jeaSSE
如果有人想要完整的代码