当前位置: 首页 > news >正文

springboot项目建立sse接口

在Spring Boot项目中实现SSE(Server-Sent Events)接口非常简单,Spring框架提供了专门的SseEmitter类来处理SSE连接。下面我将展示如何在Spring Boot中创建一个完整的SSE接口,包括服务端和客户端示例:

服务端实现(Spring Boot)

1. 添加依赖

如果是Maven项目,确保spring-boot-starter-web已添加:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. 创建SSE控制器
import org.springframework.http.MediaType;
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.CopyOnWriteArrayList;@RestController
public class SseController {// 存储所有客户端的SseEmitterprivate final CopyOnWriteArrayList<SseEmitter> emitters = new CopyOnWriteArrayList<>();// 客户端订阅SSE的接口@GetMapping(path = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public SseEmitter subscribe() {SseEmitter emitter = new SseEmitter(60_000L); // 设置超时时间60秒// 添加到emitters列表emitters.add(emitter);// 设置回调emitter.onCompletion(() -> emitters.remove(emitter));emitter.onTimeout(() -> emitters.remove(emitter));emitter.onError((e) -> emitters.remove(emitter));return emitter;}// 向所有客户端发送消息public void sendMessageToAll(String message) {emitters.forEach(emitter -> {try {// 发送数据emitter.send(SseEmitter.event().id(String.valueOf(System.currentTimeMillis())).name("message").data(message));} catch (IOException e) {// 发送失败时移除emitteremitter.completeWithError(e);emitters.remove(emitter);}});}
}
3. 创建定时任务发送消息(可选)
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
@EnableScheduling
public class MessageScheduler {private final SseController sseController;public MessageScheduler(SseController sseController) {this.sseController = sseController;}// 每5秒向所有客户端发送一次消息@Scheduled(fixedRate = 5000)public void sendPeriodicMessages() {String message = "Server time: " + System.currentTimeMillis();sseController.sendMessageToAll(message);}
}

客户端实现(JavaScript)

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>SSE Demo</title>
</head>
<body><h1>SSE实时消息</h1><div id="messages"></div><script>// 创建EventSource连接const eventSource = new EventSource('/sse');// 监听默认消息事件eventSource.onmessage = function(event) {const messageElement = document.createElement('div');messageElement.textContent = `[${new Date().toLocaleTimeString()}] ${event.data}`;document.getElementById('messages').appendChild(messageElement);};// 监听自定义事件(如果服务端发送了name字段)eventSource.addEventListener('message', function(event) {console.log('Custom event received:', event.data);});// 错误处理eventSource.onerror = function(error) {console.error('EventSource failed:', error);if (eventSource.readyState === EventSource.CLOSED) {console.log('Connection closed by the server');}};</script>
</body>
</html>

关键技术点说明

  1. SseEmitter类

    • SseEmitter是Spring提供的处理SSE的核心类,用于管理服务器到客户端的长连接。
    • 支持设置超时时间、完成回调、错误处理等。
  2. 消息格式

    • 服务端发送的消息默认格式为:
      id: [唯一标识]\n
      event: [事件类型]\n
      data: [消息内容]\n\n
      
  3. 心跳机制

    • 长时间无数据时,需定期发送注释行(以: 开头)保持连接:
      emitter.send(SseEmitter.event().comment("heartbeat"));
      
  4. 错误处理

    • 客户端可通过eventSource.onerror监听连接错误。
    • 服务端需在发送失败时移除失效的SseEmitter

测试与验证

  1. 启动Spring Boot应用,访问http://localhost:8080/sse

    • 浏览器会保持长连接,控制台显示HTTP 200状态。
  2. 查看客户端接收消息

    • 打开HTML页面,每5秒会显示一条新消息。
  3. 手动发送消息

    • 可通过调用SseControllersendMessageToAll方法测试消息推送。

生产环境优化建议

  1. 连接管理

    • 使用ConcurrentHashMap存储不同用户的SseEmitter,支持定向推送。
    • 限制最大连接数,避免资源耗尽。
  2. 异步处理

    • 使用@Async注解让SSE处理在独立线程中执行,避免阻塞Servlet容器。
  3. 安全加固

    • 添加身份验证(如JWT)保护SSE接口。
    • 配置CORS策略,限制访问来源。
  4. 高可用部署

    • 使用Redis等中间件实现跨节点的消息广播。
    • 部署多个服务实例,通过负载均衡器分发请求。

常见问题与解决方案

问题原因分析解决方案
连接频繁断开Nginx等代理默认有连接超时限制配置代理proxy_read_timeout
消息丢失SseEmitter未正确管理添加错误处理和重试机制
跨域问题未配置CORS添加@CrossOrigin注解
高并发性能问题同步处理阻塞线程池使用@Async和异步线程池

通过以上步骤,你可以在Spring Boot项目中成功实现SSE接口,满足实时消息推送的需求。

http://www.dtcms.com/a/296819.html

相关文章:

  • tokenID和位置嵌入有关系吗,qwen 模型使用时候仅仅有tokenid 映射为向量,位置编码在哪里
  • C++的虚基类?
  • 黑马头条项目详解
  • cmake应用:集成gtest进行单元测试
  • MUX同步器
  • 人工智能概念:常用的模型压缩技术(剪枝、量化、知识蒸馏)
  • 一篇文章了解HashMap和ConcurrentHashMap的扩容机制
  • ESP32入门实战:PC远程控制LED灯完整指南
  • pandas库的数据导入导出,缺失值,重复值处理和数据筛选,matplotlib库 简单图绘制
  • AD一张原理图分成多张原理图
  • iview Select的Option边框显示不全(DatePicker也会出现此类问题)
  • rust-参考与借用
  • 爬虫逆向--Day12--DrissionPage案例分析【小某书评价数据某东评价数据】
  • MySQL零基础教程增删改查实战
  • java后端
  • mujoco playground
  • DBA常用数据库查询语句
  • DevOps 完整实现指南:从理论到实践
  • 论文阅读:《Many-Objective Evolutionary Algorithms: A Survey. 》多目标优化问题的优化目标评估的相关内容介绍
  • Android LiveData 全面解析:原理、使用与最佳实践
  • Rust生态中的LLM实践全解析
  • 【C# 找最大值、最小值和平均值及大于个数和值】2022-9-23
  • 项目质量如何提升?
  • 教育培训系统源码如何赋能企业培训学习?功能设计与私有化部署实战
  • 使用 Vue 实现移动端视频录制与自动截图功能
  • MySQL---索引、事务
  • Docker 打包Vue3项目镜像
  • 互联网广告中的Header Bidding与瀑布流的解析与比较
  • 性能测试-groovy语言1
  • 使用 LLaMA 3 8B 微调一个 Reward Model:从入门到实践