我正在尝试在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]
我得到的错误是由于在“心跳”之后关闭了连接。动作在控制器中完成。 如果我添加一个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服务器,两者的结果相同;控制器动作完成后,所有消息都会立即发送。
答案 0 :(得分:1)
对Server Sent Events has been implemented for Grails 3.2的原生支持。
可以找到插件来源here
如果您希望继续使用Grails 2,可以将此插件移植到Grails 2.但是,如果您打算推出自己的解决方案,那么实现的关键是您需要启动非阻塞异步响应。您可以在插件的RxResultTransformer类中看到这是如何完成的。
关键部分是:
class MyClass
{
public static $variable;
public function __construct($variable)
{
$this->variable = $variable;
}
}
\MyClass::$variable = null;
echo \MyClass::$variable;
然后,您需要启动另一个定期将数据发送回客户端的容器线程。该插件使用RxJava执行此操作:
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,那么你可以简单地使用while循环或任何适合你的东西。
Observable newObservable = Observable.create( { Subscriber newSub ->
asyncContext.start {
// your code here
}
} as Observable.OnSubscribe)
newObservable.subscribe(subscriber)