Grails 服务器发送事件

Posted

技术标签:

【中文标题】Grails 服务器发送事件【英文标题】:Grails Server Sent Event 【发布时间】:2015-06-16 11:57:53 【问题描述】:

我需要让服务器发送的事件与 Grails 一起使用。我觉得我很接近,但并不完全在那里。 javascript 请求成功到达控制器,但每次都抛出错误。它会每 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]

提前致谢, 克里斯·汉考克

【问题讨论】:

【参考方案1】:

在 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 服务器?

浏览器获取the data后服务器与浏览器的连接关闭。因此浏览器会在每次连接关闭后大约 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 属性,因为两者都指向同一个对象。

【讨论】:

感谢您的回答!但是,为什么每次都会抛出错误?为什么它会不断地 ping 服务器?我认为 HTML5 应该在 JavaScript 第一次运行时连接一次,并且只要控制器发送事件并加载页面就可以接收事件? 谢谢你,拉姆夏兰!感谢您的回复! Ramsharan,如果我按照您的最后一个示例(使用 for 循环),我可以看到在控制器操作实际完成(最后一个渲染行)之前不会发送消息。如果是这样,浏览器会立即收到所有消息。有没有办法在阻止连接关闭的同时释放消息?【参考方案2】:

请注意,在 Grails 3.2 中,通过 RxJava plugin 原生支持服务器发送事件。

还提供example application,以及grails guides page 上的指南。

【讨论】:

以上是关于Grails 服务器发送事件的主要内容,如果未能解决你的问题,请参考以下文章

Grails - 限制 IP 地址的上传速率

Grails:如何从控制器或服务访问 i18n?

ReactJS:无法在一次 POST 调用中发送 JSON 数据和 PDF 文件

Spring boot SSE(服务器发送事件):如何动态发送响应

SSE 服务器发送事件 - 客户端不断发送请求(如池)

服务器发送事件; `EventSource.onmessage`没有触发