Server Sent Events with Grails - 如何防止控制器关闭连接
Server Sent Events with Grails - How to prevent the controller from closing the connection
这个问题是关于:
我正在尝试在 grails v2.4 上实现 SSE,但我无法阻止 grails 关闭连接。
我有的是:
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GA
class SseController {
def heartbeat = {
response.contentType = 'text/event-stream'
response.characterEncoding = 'UTF-8'
response.setHeader('Cache-Control', 'no-cache')
response.setHeader('Connection', 'keep-alive')
response << 'data: 12345\n\n'
response.flushBuffer()
def grails_request = request.getAttribute(GA.WEB_REQUEST)
grails_request.setRenderView(false)
}
}
但是如果我这样做,客户端浏览器在订阅频道后报告如下:
Open [object Event]
Data:12345
Error [object Event]
我得到的错误是由于在控制器中完成 'heartbeat' 操作后连接关闭。
如果我添加一个 while 循环来保持这样的动作 运行...
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GA
class SseController {
def heartbeat = {
response.contentType = 'text/event-stream'
response.characterEncoding = 'UTF-8'
response.setHeader('Cache-Control', 'no-cache')
response.setHeader('Connection', 'keep-alive')
response << 'data: 12345\n\n'
response.flushBuffer()
def grails_request = request.getAttribute(GA.WEB_REQUEST)
grails_request.setRenderView(false)
while(true) {
sleep(10000)
}
}
}
... 那么数据永远不会被客户端浏览器接收到。
response.flushBuffer()
命令没有任何效果。
连接未关闭,没关系,但数据未发送到客户端。
所以正确的解决方案是摆脱 while 循环,同时告诉 Grails 在执行操作后不要关闭连接。
有人知道怎么做吗?
顺便说一句,我尝试同时使用 Tomcat 和 Jetty 服务器,两者的结果相同;控制器操作完成后立即发送所有消息。
对 Server Sent Events has been implemented for Grails 3.2 的原生支持。
可以找到插件源here
如果您希望继续使用 Grails 2,可以将此插件移植到 Grails 2。但是,如果您打算推出自己的解决方案,那么实施的关键是您需要启动非阻塞异步响应.您可以在插件的 RxResultTransformer class 中看到这是如何完成的。
关键部分是这样的:
webRequest.setRenderView(false)
// Create the Async web request and register it with the WebAsyncManager so Spring is aware
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request)
AsyncWebRequest asyncWebRequest = new AsyncGrailsWebRequest(
request,
response,
webRequest.servletContext)
asyncManager.setAsyncWebRequest(asyncWebRequest)
// Start async processing and create the GrailsAsync object
asyncWebRequest.startAsync()
request.setAttribute(GrailsApplicationAttributes.ASYNC_STARTED, true)
GrailsAsyncContext asyncContext = new GrailsAsyncContext(asyncWebRequest.asyncContext, webRequest)
response.setContentType(CONTENT_TYPE_EVENT_STREAM);
response.flushBuffer()
然后您需要启动另一个容器线程,定期将数据发送回客户端。该插件使用 RxJava 执行此操作:
Observable newObservable = Observable.create( { Subscriber newSub ->
asyncContext.start {
// your code here
}
} as Observable.OnSubscribe)
newObservable.subscribe(subscriber)
如果您不想使用 RxJava,那么您可以简单地使用 while 循环或任何适合您的方法。
asyncContext.start {
while(true) {
// write event
}
}
这个问题是关于:
我正在尝试在 grails v2.4 上实现 SSE,但我无法阻止 grails 关闭连接。 我有的是:
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GA
class SseController {
def heartbeat = {
response.contentType = 'text/event-stream'
response.characterEncoding = 'UTF-8'
response.setHeader('Cache-Control', 'no-cache')
response.setHeader('Connection', 'keep-alive')
response << 'data: 12345\n\n'
response.flushBuffer()
def grails_request = request.getAttribute(GA.WEB_REQUEST)
grails_request.setRenderView(false)
}
}
但是如果我这样做,客户端浏览器在订阅频道后报告如下:
Open [object Event]
Data:12345
Error [object Event]
我得到的错误是由于在控制器中完成 'heartbeat' 操作后连接关闭。 如果我添加一个 while 循环来保持这样的动作 运行...
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes as GA
class SseController {
def heartbeat = {
response.contentType = 'text/event-stream'
response.characterEncoding = 'UTF-8'
response.setHeader('Cache-Control', 'no-cache')
response.setHeader('Connection', 'keep-alive')
response << 'data: 12345\n\n'
response.flushBuffer()
def grails_request = request.getAttribute(GA.WEB_REQUEST)
grails_request.setRenderView(false)
while(true) {
sleep(10000)
}
}
}
... 那么数据永远不会被客户端浏览器接收到。
response.flushBuffer()
命令没有任何效果。
连接未关闭,没关系,但数据未发送到客户端。
所以正确的解决方案是摆脱 while 循环,同时告诉 Grails 在执行操作后不要关闭连接。
有人知道怎么做吗? 顺便说一句,我尝试同时使用 Tomcat 和 Jetty 服务器,两者的结果相同;控制器操作完成后立即发送所有消息。
对 Server Sent Events has been implemented for Grails 3.2 的原生支持。
可以找到插件源here
如果您希望继续使用 Grails 2,可以将此插件移植到 Grails 2。但是,如果您打算推出自己的解决方案,那么实施的关键是您需要启动非阻塞异步响应.您可以在插件的 RxResultTransformer class 中看到这是如何完成的。
关键部分是这样的:
webRequest.setRenderView(false)
// Create the Async web request and register it with the WebAsyncManager so Spring is aware
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request)
AsyncWebRequest asyncWebRequest = new AsyncGrailsWebRequest(
request,
response,
webRequest.servletContext)
asyncManager.setAsyncWebRequest(asyncWebRequest)
// Start async processing and create the GrailsAsync object
asyncWebRequest.startAsync()
request.setAttribute(GrailsApplicationAttributes.ASYNC_STARTED, true)
GrailsAsyncContext asyncContext = new GrailsAsyncContext(asyncWebRequest.asyncContext, webRequest)
response.setContentType(CONTENT_TYPE_EVENT_STREAM);
response.flushBuffer()
然后您需要启动另一个容器线程,定期将数据发送回客户端。该插件使用 RxJava 执行此操作:
Observable newObservable = Observable.create( { Subscriber newSub ->
asyncContext.start {
// your code here
}
} as Observable.OnSubscribe)
newObservable.subscribe(subscriber)
如果您不想使用 RxJava,那么您可以简单地使用 while 循环或任何适合您的方法。
asyncContext.start {
while(true) {
// write event
}
}