Grails服务器发送事件

4
我需要让Grails与服务器发送事件(Server-sent-events)工作。我感觉离成功很近,但还差了一点。JavaScript的请求能够成功地到达控制器(controller),但每次都会抛出错误。它持续以每2秒一次的频率进行重试(可能是由于错误导致的)。
当服务端的会话计时器下降到5分钟以下时,我需要向用户发送一个事件。我正在尝试使用HTML5的EventSource,因为我只想向服务器发送一个单一请求(多个请求将每次重置会话计时器)。根据Lean Java Engineering的说法,HTML5满足我的需求。
-Javascript-
console.log("Starting eventSource");
var eventSource = new EventSource("SSETest/push");
console.log("Started eventSource");
eventSource.onmessage   = function(event) { console.log("Message received: " + event.data); };
eventSource.onopen      = function(event) { console.log("Open " + event); };
eventSource.onerror     = function(event) { console.log("Error " + event); };
console.log("eventState: " + eventSource.readyState);
// Stop trying after 10 seconds of errors
setTimeout(function() {eventSource.close();}, 10000);

-Grails控制器-

package sessionManager
class SSETestController {
    def push = {
        println "$request made it!"
        response.setContentType("text/event-stream, charset=UTF-8")
        response << "data: the data"
        render "HI"
    }
}

-Grails 控制台-

org.apache.catalina.core.ApplicationHttpRequest@4f889fad made it!
org.apache.catalina.core.ApplicationHttpRequest@13f78b8c made it!
org.apache.catalina.core.ApplicationHttpRequest@4b50734c made it!
org.apache.catalina.core.ApplicationHttpRequest@4c4bde24 made it!

-JavaScript控制台-

Starting eventSource
Started eventSource
eventState: 0
Open [object Event] 
Error [object Event] 
Open [object Event] 
Error [object Event]
Open [object Event] 
Error [object Event]
Open [object Event]
Error [object Event]

预先感谢, 克里斯·汉考克

2个回答

5
在Grails控制器中,您需要更改为以下内容:
package sessionManager
class SSETestController {
    def push = {
        println "$request made it!"
        response.setContentType("text/event-stream, charset=UTF-8")
        //response << "data: the data\n\n"
        render "data: the data\n\n"
    }
}

在基本形式中,响应应包含一个"data:"行,后跟您的消息,再后跟两个"\n"字符以结束流:
data: My message\n\n

现在控制台中的响应是:

Starting eventSource
Started eventSource
eventState: 0
Open [object Event]
Message received: the data
Error [object Event]
Open [object Event]
Message received: the data
Error [object Event]
Open [object Event]
Message received: the data
Error [object Event]
Open [object Event]
Message received: the data
Error [object Event]

如果需要更多细节,您可以查看http://www.html5rocks.com/en/tutorials/eventsource/basics/

更新

但是为什么每次都会抛出错误?

可能有不同的错误原因,例如:网络超时。在我们的情况下,是由于服务器和浏览器之间的连接被关闭导致的。

为什么它会不断地向服务器发送ping请求?

浏览器在获取数据后,与服务器之间的连接就会关闭。因此,浏览器会在每次连接关闭后大约3秒钟尝试重新连接。

我以为HTML5应该在JavaScript第一次运行时连接一次,只要控制器发送事件,就可以接收到事件。

它应该始终与服务器保持连接,而不是仅连接一次。
是的,只要控制器发送消息,它就会接收到事件。但在我们的情况下,服务器/控制器只发送data: the data\n\n,然后连接就关闭了。
为了持续接收消息,您需要在控制器操作中使用循环。例如:

package sessionManager
class SSETestController {
    def push = {
        println "$request made it!"
        response.setContentType("text/event-stream, charset=UTF-8")
        for(int i = 0 ; i < 10 ; i++){
            render "data: the data\n\n"
            Thread.sleep(1000)
        }
        render "data: finished\n\n"
    }
}

你也可以使用 while(true) 循环。

如果想了解更多关于 onerror 的信息,可以查看此链接:http://www.htmlgoodies.com/beyond/reference/receive-updates-from-the-server-using-the-eventsource.html

有一个 onerror() 事件处理程序,但它并不像你想的那么有用。不过,由于 EventSource 的 readyState 属性,它确实可以告诉我们一些关于正在发生的事情的信息。例如,EventSource.CONNECTING 的值意味着连接已丢失并且 EventSource 正在尝试重新连接。如果出现严重问题,则值为 EventSource.CLOSED。除此之外,由于没有错误对象可供查询,因此无法获取太多信息。相反,是事件对象本身传递给处理程序。EventSource 可通过 eventSource 或 eventTarget 属性访问,因为两者都指向同一个对象。


谢谢你的回答!但是为什么它每次都会抛出错误呢?而且为什么它不断地向服务器发送请求?我以为HTML5应该在JavaScript第一次运行时连接一次,并在控制器发送事件和页面加载时接收事件。 - Chris H
1
谢谢你,Ramsharan!我很感激你的回复! - Chris H
Ramsharan,如果我按照你的最后一个例子(使用for循环)进行操作,我可以看到消息直到控制器动作实际完成(最后的渲染行)才会被发送。在这种情况下,浏览器会一次性接收所有消息。有没有一种方法可以在防止连接关闭的同时释放消息? - user3621841

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接