You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Undertow 2.X ServerSentEventHandler无法向客户端流式传输数据排查

Undertow 2.x Server-Sent Events 流式传输与lastEventId问题

问题场景

实现代码

public class SimpleServer {

  public static void main(String[] args) {
    Undertow server = Undertow.builder()
            .addHttpListener(8080, "localhost")
            .setHandler(new ServerSentEventHandler((connection, lastEventId) -> {

                for (int i = 0; i < 10; i++) {
                    System.out.println("LastEventId: " + lastEventId + ", i: " + i);
                    connection.send("Numbers :: " + i);

                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            })).build();
    server.start();
  }
}

Maven依赖

<dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-core</artifactId>
    <version>2.3.12.Final</version>
</dependency>
<dependency>
    <groupId>com.networknt</groupId>
    <artifactId>server</artifactId>
    <version>0.1.1</version>
</dependency>

现象

调用API时控制台依次打印日志:

LastEventId: null, i: 0
LastEventId: null, i: 1
LastEventId: null, i: 2
LastEventId: null, i: 3
LastEventId: null, i: 4
LastEventId: null, i: 5
LastEventId: null, i: 6
LastEventId: null, i: 7
LastEventId: null, i: 8
LastEventId: null, i: 9

但所有日志打印完成后,浏览器、Postman、curl才一次性接收所有数据,而非实时推送。已添加no-cache响应头但无效。

疑问

  1. 如何实现服务器向客户端流式传输数据?是否与缓冲/缓存有关?
  2. lastEventId始终为null的原因是什么?

解决方案与原因分析

1. 流式传输失效的原因与修复

问题核心是Undertow的输出缓冲机制:默认情况下,Undertow会缓冲输出内容,直到缓冲区满才会将数据发送给客户端,而非每次调用send()就立即推送。

修复方法:

  • 在调用connection.send()后,手动调用connection.flush()强制刷新缓冲区,确保数据立即发送到客户端。
  • 也可以手动设置Transfer-Encoding: chunked响应头明确启用分块传输(Undertow在SSE场景下通常会自动处理,但手动设置更稳妥)。

修改后的核心代码:

for (int i = 0; i < 10; i++) {
    System.out.println("LastEventId: " + lastEventId + ", i: " + i);
    connection.send("Numbers :: " + i);
    connection.flush(); // 新增:强制刷新缓冲区,实时推送数据

    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

2. lastEventId为null的原因

lastEventId的值来自客户端请求中的Last-Event-ID请求头,只有当客户端断开重连时才会携带这个头,用于告知服务器上次接收的最后一个事件ID,实现断点续传。

你的场景是首次连接,客户端没有发送Last-Event-ID请求头,因此lastEventId自然为null。若要测试该值的有效性,需要:

  • 服务器发送事件时指定ID:使用connection.send(ServerSentEvent.builder().data("Numbers :: " + i).id(String.valueOf(i)).build())
  • 客户端断开后重新连接,浏览器会自动携带Last-Event-ID头,此时服务器就能获取到对应的值。

内容的提问来源于stack exchange,提问作者JustAnotherDev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 19:30:12