Spring Boot 整合 SSE(Server-Sent Events)全网最全


Spring Boot 整合 SSE(Server-Sent Events)

在这里插入图片描述

1、简述

SSE(Server-Sent Events)是一种基于HTTP协议的单向通信机制,允许服务器向浏览器持续发送实时更新。与WebSocket不同,SSE更简单,使用HTTP/1.1协议即可,不需要额外的协议升级。它通过简单的文本流格式(data: ...\n\n)传输事件,支持自定义事件类型和重连机制

SSE与其他技术的对比

技术协议双向通信复杂度适用场景
SSEHTTP单向服务器主动推送(如通知)
WebSocketWS双向实时聊天、游戏
长轮询HTTP半双工兼容性要求高的场景

SSE的特点:

  • 自动重连:客户端在连接断开时会自动尝试重新连接。
  • 轻量级协议:事件流格式简单,无需额外解析库。
  • 浏览器原生支持:现代浏览器通过EventSource API直接使用。

2、Spring Boot 中的SSE实现

2.1 添加依赖与配置

验证Spring Boot版本兼容性:确保使用Spring Boot 2.2+版本以获得更好的异步支持。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.1.0</version>
</parent>

2.2 实现后端接口

代码深度解析

  • 使用Stream.generate生成数据流,但需注意无限流会导致线程阻塞,推荐结合异步线程池。
  • 添加事件ID和重试机制(增强SSE规范支持):
@GetMapping(value = "/sse/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> stream() {
    return Flux.interval(Duration.ofSeconds(1))
              .map(sequence -> "id: " + sequence + "\n" 
                             + "data: " + LocalTime.now() + "\n\n")
              .take(10);
}

2.3 超时与异常处理

精细化配置

  • 通过SseEmitter设置超时回调,记录未完成的连接:
emitter.onTimeout(() -> {
    log.warn("SSE连接超时:{}", emitter);
    emitters.remove(emitter);
});
  • 全局异常处理:使用@ExceptionHandler捕获IOException,避免连接中断导致服务不可用。

2.4 前端实现优化

增强健壮性

  • 监听特定事件类型并处理重连:
eventSource.addEventListener("customEvent", function(e) {
    console.log("自定义事件数据:", e.data);
});

eventSource.onerror = function() {
    setTimeout(() => new EventSource('/sse/stream'), 5000); // 5秒后重连
};
  • 添加心跳检测:服务器定期发送注释行(:keep-alive\n)防止代理超时。

3、高级实践

3.1 结合Spring Security实现鉴权

保护SSE端点

@GetMapping("/sse/private-stream")
public SseEmitter privateStream(Authentication authentication) {
    if (authentication == null) {
        throw new SecurityException("未授权访问");
    }
    return ssePushService.subscribe();
}

Token验证:前端在初始化EventSource时携带Token:

new EventSource('/sse/private-stream?token=xxxx');

3.2 分布式场景下的解决方案

使用消息队列广播事件(以Redis Pub/Sub为例):

  1. 引入依赖:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 订阅Redis频道并推送消息:
@Bean
public MessageListenerAdapter redisListener(SsePushService pushService) {
    return new MessageListenerAdapter((message, channel) -> 
        pushService.pushMessage(message.toString()));
}

3.3 性能监控与调优

  • 监控连接数:通过/actuator/metrics/sse.emitters端点(需集成Actuator)。
  • 背压控制:使用Project Reactor的Flux处理背压,避免服务器过载。

4、注意事项(增强版)

  • 浏览器兼容性

    • IE完全不支持,可通过polyfill(如eventsource库)降级为长轮询。
    • 移动端浏览器需测试网络切换(如4G/WiFi)对连接的影响。
  • 消息格式规范

    • 每条消息以data:开头,结尾必须有两个换行符。
    • 发送JSON数据需转义换行符:data: {\"time\": \"12:00\"}\n\n
  • 安全加固

    • 启用HTTPS防止中间人攻击。
    • 限制每IP连接数,防止DDoS攻击。

5、实战案例:实时日志监控系统

架构设计

  1. 日志收集:Filebeat监听日志文件变动。
  2. 消息中转:Kafka接收日志并缓存。
  3. SSE推送:Spring Boot消费Kafka消息并广播。

关键代码

@KafkaListener(topics = "logs")
public void handleLog(String logMessage) {
    ssePushService.pushMessage(logMessage);
}

6、调试与测试技巧

  • 使用Curl测试SSE
    curl -N http://localhost:8080/sse/stream
    
  • 浏览器开发者工具
    • 网络面板查看EventStream分帧详情。
    • 控制台调试EventSource事件监听。

延伸阅读

Spring Boot 2.7.2 中实现 Server-Sent EventsSSE)功能,可以通过 `ResponseBodyEmitter` 或 `SseEmitter` 来完成。这两种类都支持异步发送事件流,适用于构建实时更新的 Web 应用场景。 ### 配置 SSE 端点 首先,在控制器中创建一个返回 `SseEmitter` 的方法,并设置适当的响应类型为 `text/event-stream`。以下是一个示例代码片段: ```java import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @RestController public class SseController { private final ExecutorService executor = Executors.newCachedThreadPool(); @GetMapping("/sse") public SseEmitter handleSse() { SseEmitter emitter = new SseEmitter(60_000L); // 设置超时时间为60秒 emitter.setRequestHandled(false); executor.submit(() -> { try { for (int i = 0; i < 10; i++) { String event = "Event " + i; emitter.send(event); Thread.sleep(1000); // 模拟延迟 } emitter.complete(); } catch (IOException | InterruptedException e) { emitter.completeWithError(e); } }); return emitter; } } ``` ### 客户端连接 客户端可以使用 JavaScript 的 `EventSource` 对象来连接 `/sse` 端点并接收事件流: ```javascript const eventSource = new EventSource('/sse'); eventSource.onmessage = function(event) { console.log('Received message:', event.data); }; eventSource.onerror = function(err) { console.error('EventSource failed:', err); }; ``` ### 超时与错误处理 为了防止长时间未收到数据导致的超时问题,可以在创建 `SseEmitter` 时设置合理的超时时间,并通过 `emitter.complete()` 和 `emitter.completeWithError()` 方法来显式地结束或中断事件流。此外,建议将耗时操作放在单独的线程中执行,以避免阻塞主线程。 ### 异常处理 如果需要全局处理 `SseEmitter` 相关的异常,可以使用 `@ControllerAdvice` 注解定义一个全局异常处理器: ```java import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.AbstractSseEmitter; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; import org.springframework.web.context.request.async.DeferredResult; @ControllerAdvice public class GlobalExceptionHandler { public void handleException(Throwable ex) { // 处理所有未捕获的异常 System.err.println("Unhandled exception: " + ex.getMessage()); } } ``` ### 性能优化 - **并发控制**:由于每个 `SseEmitter` 实例会占用一定的资源,因此建议根据服务器性能合理设置最大并发数。 - **内存管理**:定期清理已完成的 `SseEmitter` 实例,避免内存泄漏。 - **日志记录**:启用详细的日志记录可以帮助快速定位问题,尤其是在生产环境中。 ### 示例输出 假设客户端成功连接到 `/sse` 端点,则每隔一秒会接收到一次事件消息,例如: ``` Event 0 Event 1 Event 2 ... Event 9 ``` ### 测试与调试 可以通过浏览器开发者工具中的 Network 面板查看 SSE 请求的状态和响应内容。同时,也可以使用 Postman 等工具模拟 SSE 请求进行测试。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值