Spring中无法维持SSE连接开放的问题求助
问题分析与解决方案
你的SSE连接失败主要是因为缺少响应头配置、未处理Emitter的生命周期回调,以及没有提供消息发送的测试入口,导致连接无法正常维持。以下是具体修复步骤和代码调整:
1. 修复后端SSE控制器
需要为SSE连接设置正确的响应头,并处理Emitter的超时、错误、完成事件,避免无效连接堆积:
import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; @RestController public class SSETestController { private final List<SseEmitter> sseEmitters = new ArrayList<>(); @GetMapping("/message") public SseEmitter eventEmitter() { // 设置30分钟超时,避免连接被过早关闭 SseEmitter sseEmitter = new SseEmitter(TimeUnit.MINUTES.toMillis(30)); // 必须设置SSE专属响应头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.TEXT_EVENT_STREAM); sseEmitter.setHeaders(headers); // 连接完成时移除无效Emitter sseEmitter.onCompletion(() -> sseEmitters.remove(sseEmitter)); // 超时处理 sseEmitter.onTimeout(() -> { sseEmitters.remove(sseEmitter); try { sseEmitter.send(SseEmitter.event().name("timeout").data("连接超时")); sseEmitter.complete(); } catch (IOException e) { sseEmitter.completeWithError(e); } }); // 错误处理 sseEmitter.onError((e) -> { sseEmitters.remove(sseEmitter); sseEmitter.completeWithError(e); }); sseEmitters.add(sseEmitter); return sseEmitter; } // 添加测试消息发送接口 @PostMapping("/send-message") public String sendMessage(String content) { // 遍历副本避免并发修改异常 for (SseEmitter emitter : new ArrayList<>(sseEmitters)) { try { // 发送自定义事件名的消息,和前端监听对应 emitter.send(SseEmitter.event().name("message").data(content)); // 若需发送JSON:emitter.send(SseEmitter.event().name("message").data("{\"content\":\"" + content + "\"}", MediaType.APPLICATION_JSON)); } catch (IOException e) { sseEmitters.remove(emitter); } } return "消息已发送"; } }
2. 调整前端代码
优化事件监听逻辑,添加测试消息发送按钮,同时处理可能的解析错误:
<!DOCTYPE html> <html> <body> <h1>SSE实时消息测试</h1> <p>控制台将打印收到的消息:</p> <button onclick="sendTestMessage()">发送测试消息</button> <script> window.onload = function() { const eventSource = new EventSource("/message"); // 监听后端发送的"message"事件 eventSource.addEventListener("message", (event) => { console.log("收到消息:", event.data); // 若后端发送JSON,可启用以下解析逻辑 // try { // const message = JSON.parse(event.data); // console.log("解析后的消息:", message); // } catch (e) { // console.log("非JSON消息:", event.data); // } }); // 监听超时事件 eventSource.addEventListener("timeout", (event) => { console.log("连接超时:", event.data); }); eventSource.onerror = function (e) { console.error("连接出错:", e); // EventSource会自动重试连接,无需手动重连 } eventSource.addEventListener("open", (event) => { console.log('SSE连接已建立'); }); } // 发送测试消息的函数 function sendTestMessage() { fetch("/send-message", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: "content=这是一条测试消息" }).then(res => res.text()).then(msg => console.log(msg)); } </script> </body> </html>
3. 检查额外配置
- Spring Security拦截:如果项目启用了Spring Security,需允许SSE接口匿名访问:
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/message", "/send-message").permitAll() .anyRequest().authenticated(); }
- 异步超时配置:在
application.properties中确保异步请求超时足够长:
spring.mvc.async.request-timeout=1800000 # 30分钟,单位毫秒
内容的提问来源于stack exchange,提问作者ninakuup21
相关产品推荐
相关产品推荐

