为什么我的服务器发送的事件成批到达?

dav*_*ave 6 javascript java spring server-sent-events

我有一个基于Java 8 / Spring4的Web应用程序,它向使用某些Javascript并更新进度条的基于浏览器的客户端报告使用服务器发送事件(SSE)的长时间运行过程的进度。在我的开发环境和开发服务器上,SSE几乎实时地到达客户端。我可以看到他们使用Chrome开发器工具(以及它们的时间戳)到达了,进度条也顺利更新。

但是,当我部署到生产环境中时,会观察到不同的行为。在长时间运行的过程完成之前,事件不会到达浏览器。然后它们全部到达一个突发状态(根据开发工具,所有事件的时间戳都在几百毫秒之内)。进度条在整个过程中停留在0%,然后很快迅速跳到100%。同时,我的服务器日志告诉我事件是定期生成和发送的。

这是相关的服务器端代码:

public class LongRunningProcess extends Thread {
    private SseEmitter emitter;
    public LongRunningProcess(SseEmitter emitter) {
        this.emitter = emitter;
    }
    public void run() {
        ...
        // Sample event, representing 10% progress
        SseEventBuilder event = SseEmitter.event();
        event.name("progress");
        event.data("{ \"progress\": 10 }"); // Hand-coded JSON
        emitter.send(event);
        ...
    }
}

@RestController
public class UploadController {
    @GetMapping("/start")
    public SseEmitter start() {
        SseEmitter emitter = new SseEmitter();
        LongRunningProcess process = new LongRunningProcess(emitter);
        process.start();
        return emitter;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是相关的客户端Javascript:

EventSource src = new EventSource("https://www.example.com/app/start");
src.addEventListener('progress', function(event) {
    // Process event.data and update progress bar accordingly
});
Run Code Online (Sandbox Code Playgroud)

我相信我的代码相当典型,并且在DEV中也可以正常工作。但是,如果有人可以看到问题,请告诉我。

该问题可能与生产服务器的配置有关。DEV和PROD都运行相同版本的Tomcat。但是,其中一些可通过负载平衡器访问(在情况下为F5)。几乎所有人都位于CDN(在我们的情况下为Akamai)后面。此设置中是否有一部分会导致SSE进行缓冲(或排队或缓存),从而产生我所看到的内容?

遵循基础结构配置思想,我在响应头中观察到以下内容。在开发环境中,我的浏览器收到:

Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: Keep-Alive
Content-Type: text/event-stream;charset=UTF-8
Keep-Alive: timeout=15, max=99
Pragma: no-cache
Server: Apache
Transfer-Encoding: chunked
Via: 1.1 example.com
Run Code Online (Sandbox Code Playgroud)

这是我期望的事件流。内容长度未知的分块响应。在生产环境中,我的浏览器收到了一些不同的信息:

Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Connection: keep-alive
Content-Type: text/event-stream;charset=UTF-8
Content-Encoding: gzip
Content-Length: 318
Pragma: no-cache
Vary: Accept-Encoding
Run Code Online (Sandbox Code Playgroud)

在这里,返回的内容具有已知的长度并被压缩。我认为事件流不会发生这种情况。似乎有些东西将我的事件流转换为单个文件。关于如何确定正在执行的操作的任何想法?

dav*_*ave 5

需要进行大量调查才能确定问题的原因是我们网络路径中的元素。所以上面的代码是正确的,可以安全使用。如果您发现 SSE 缓冲,您很可能需要检查关键网络元素的配置。

就我而言,它是 Akamai 作为我们的 CDN,并使用F5设备作为负载平衡器。事实上,两者都可以引入缓冲,这使得诊断问题变得非常困难。

Akamai Edge 服务器默认缓冲事件流。这可以通过使用 Akamai 的高级元数据禁用并通过自定义行为进行控制。目前,这不能通过 Amakai 的门户直接控制,因此您需要让他们的工程师为您做一些工作。

F5 设备似乎也默认缓冲响应数据。幸运的是,这很容易更改,并且可以通过设备的配置门户自己完成。对于有问题的虚拟设备,转到Profile : Services : HTTP并将Response Chunking的配置更改为Preserve(在我们的例子中它默认为Selective)。

进行这些更改后,我开始从我们的 PROD 服务器(而不仅仅是我们的 DEV 服务器)近乎实时地接收 SSE。