为什么DeferredResult在尝试使用SSE时结束于setResult()

时间:2014-09-17 00:27:53

标签: html5 spring spring-mvc server-sent-events

我正在尝试实施由Spring提供支持的服务器发送事件(SSE)网页。我的测试代码执行以下操作:

浏览器使用EventSource(url)连接到服务器。 Spring使用以下控制器代码接受请求:

@RequestMapping(value="myurl", method = RequestMethod.GET, produces = "text/event-stream")
@ResponseBody
public DeferredResult<String> subscribe() throws Exception {
    final DeferredResult<String> deferredResult = new DeferredResult<>();
    resultList.add(deferredResult);

    deferredResult.onCompletion(() -> {
        logTimer.info("deferedResult "+deferredResult+" completion");
        resultList.remove(deferredResult);
    });
    return deferredResult;
}

所以主要是将DeferredResult放在List中并注册一个完成回调,以便我可以在完成时从List中删除这个东西。

现在我有一个计时器方法,它会周期性地将当前时间戳输出到所有已注册的&#34;浏览器&#34;通过他们的DeferredResults。

@Scheduled(fixedRate=10000)
public void processQueues() {
    Date d = new Date();
    log.info("outputting to "+ LoginController.resultList.size()+ " connections");
    LoginController.resultList.forEach(deferredResult -> deferredResult.setResult("data: "+d.getTime()+"\n\n"));
}

数据将发送到浏览器,以下客户端代码可以正常工作:

 var source = new EventSource('/myurl');
   source.addEventListener('message', function (e) {
            console.log(e.data);
            $("#content").append(e.data).append("<br>");
        });

现在出现问题:

在计时器线程中的每个setResult()调用上调用DeferredResult上的完成回调。因此,出于某种原因,在setResult()调用之后关闭了连接。浏览器中的SSE按照规范重新连接,然后再次重新连接。所以在客户端我有一个轮询行为,但我想要一个保持打开的请求,我可以反复推送同一个DeferredResult上的数据。

我在这里想念一下吗? DeferredResult不能发送多个结果吗?我在计时器线程中延迟10秒,以查看请求是否仅在setResult()之后终止。因此,在浏览器中,请求保持打开,直到计时器推送数据但随后关闭。

感谢您的任何提示。还有一点需要注意:我在tomcat中为所有过滤器/ servlet添加了异步支持。

1 个答案:

答案 0 :(得分:2)

实际上DeferredResult只能设置一次(注意setResult返回一个布尔值)。它使用全系列的Spring MVC处理选项完成处理,即意味着您对Spring MVC请求期间发生的事情的所有了解大致相同,除了异步生成的返回值。

SSE需要的是更集中的内容,即使用HttpMessageConverter将每个值写入响应。我已为https://jira.spring.io/browse/SPR-12212创建了一张票。

请注意,Spring的SockJS支持确实有一个SSE传输,它会处理一些额外的事情,例如使用cookie的跨域请求(对IE很重要)。它还用于WebSocket API和WebSocket样式的消息传递(即使WebSocket在客户端或服务器端都不可用),它完全抽象了HTTP长轮询的细节。

作为一种解决方法,您还可以使用HttpMessageConverter直接写入Servlet响应。