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响应头但无效。
疑问
- 如何实现服务器向客户端流式传输数据?是否与缓冲/缓存有关?
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
相关产品推荐
相关产品推荐

