《Spring WebFlux 实战:基于 SSE 实现多类型事件流(支持聊天消息、元数据与控制指令混合传输)》

在使用 Server-Sent Events (SSE) 时,如果你需要在一个 Flux 流中穿插不同类型的数据 (比如:普通消息、错误提示、控制指令、元信息等),你可以通过 自定义事件类型(event type) 来区分它们。


✅ 核心机制:SSE 的 event: 字段

标准 SSE 消息格式支持以下字段:

复制代码

text

编辑

复制代码
event: <事件类型>
data: <数据内容>
id: <可选ID>
retry: <重连时间>

其中:

  • event: 字段用于指定事件类型 (默认是 message
  • 客户端可以通过 EventSource.addEventListener('xxx', ...) 监听特定类型的事件

🛠 在 Spring WebFlux 中如何发送不同类型的 SSE?

你需要返回 Flux<ServerSentEvent<T>>,而不是直接 Flux<T>。这样你才能控制 event 类型和 data 内容。

示例:混合多种事件类型

复制代码

java

编辑

复制代码
@GetMapping(value = "/generateStream1", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<?>> generateStream1(
        @RequestParam(value = "message", defaultValue = "给我讲个笑话") String message) {

    var prompt = new Prompt(new UserMessage(message));

    // 假设 chatModel.stream 返回的是 Flux<ChatResponse>
    Flux<ChatResponse> chatStream = chatModel.stream(prompt);

    // 转换为带 event type 的 ServerSentEvent
    Flux<ServerSentEvent<?>> responseEvents = chatStream.map(response ->
        ServerSentEvent.<ChatResponse>builder()
            .event("chat_message")     // 自定义事件类型
            .data(response)
            .build()
    );

    // 插入一个"开始"事件
    ServerSentEvent<Void> startEvent = ServerSentEvent.builder(Void.class)
        .event("status")
        .data(null)
        .comment("Stream started")
        .build();

    // 插入一个"系统提示"事件(比如 token 使用量)
    ServerSentEvent<Map<String, Object>> metaEvent = ServerSentEvent.builder(Map.class)
        .event("metadata")
        .data(Map.of("tokens_used", 42, "model", "gpt-4"))
        .build();

    // 合并多个事件流:先发 start,再发 meta,然后是聊天消息流
    return Flux.concat(
        Flux.just(startEvent),
        Flux.just(metaEvent),
        responseEvents,
        Flux.just(ServerSentEvent.<Void>builder().event("end").build()) // 结束事件
    );
}

🖥 客户端如何处理不同事件类型?

复制代码

javascript

编辑

复制代码
const es = new EventSource('/generateStream1?message=你好');

es.addEventListener('status', (event) => {
    console.log('状态事件:', event);
});

es.addEventListener('metadata', (event) => {
    const data = JSON.parse(event.data);
    console.log('元数据:', data);
});

es.addEventListener('chat_message', (event) => {
    const msg = JSON.parse(event.data);
    console.log('聊天消息:', msg);
});

es.addEventListener('end', () => {
    console.log('流结束');
    es.close();
});

// 默认的 message 事件(如果没指定 event)
es.onmessage = (event) => {
    console.log('默认消息:', event.data);
};

🔒 注意事项

  1. 泛型统一问题
    Flux<ServerSentEvent<?>> 是最灵活的方式。如果你用具体类型(如 String),所有 data 都必须是那个类型。

  2. 序列化

    Spring 会自动用 Jackson 序列化 data 字段。确保你的对象(如 ChatResponse)可被 JSON 序列化。

  3. 不要混用 Flux<T>Flux<ServerSentEvent<T>>

    一旦你要控制 event 类型,就必须用 ServerSentEvent 包装。

  4. 性能 & 缓冲

    SSE 是单向、文本、按行发送的,适合低频到中频流。高频场景需注意背压(backpressure)。


✅ 总结

需求 解决方案
发送不同类型的消息 使用 Flux<ServerSentEvent<?>>
区分消息语义(如消息/错误/控制) 设置 .event("xxx")
客户端按类型处理 addEventListener('xxx', ...)
插入非聊天数据(如状态、统计) 构造额外的 ServerSentEvent 并用 Flux.concat 合并

这样你就可以在一个 SSE 连接中安全、清晰地穿插多种类型的数据了。

相关推荐
迷藏49412 小时前
**eBPF实战进阶:从零构建网络流量监控与过滤系统**在现代云原生架构中,**网络可观测性**和**安全隔离**已成为
java·网络·python·云原生·架构
迷藏49412 小时前
**发散创新:基于Solid协议的Web3.0去中心化身份认证系统实战解析**在Web3.
java·python·web3·去中心化·区块链
qq_4335021812 小时前
Codex cli 飞书文档创建进阶实用命令 + Skill 创建&使用 小白完整教程
java·前端·飞书
safestar201212 小时前
ES批量写入性能调优:BulkProcessor 参数详解与实战案例
java·大数据·运维·jenkins
还在忙碌的吴小二13 小时前
Harness 最佳实践:Java Spring Boot 项目落地 OpenSpec + Claude Code
java·开发语言·spring boot·后端·spring
风吹迎面入袖凉13 小时前
【Redis】Redis的五种核心数据类型详解
java·redis
夕除13 小时前
javaweb--02
java·tomcat
ailvyuanj13 小时前
2026年Java AI开发实战:Spring AI完全指南
java
张np13 小时前
java进阶-Dubbo
java·dubbo
汽车仪器仪表相关领域13 小时前
NHFID-1000型非甲烷总烃分析仪:技术破局,重构固定污染源监测新体验
java·大数据·网络·人工智能·单元测试·可用性测试·安全性测试