Spring Boot 应用中实现基本的 SSE 功能

SSE 技术简介

SSE(Server-Sent Events)是一种允许服务器主动向客户端推送数据的技术。它基于 HTTP 长连接,使用简单,特别适合实时数据更新场景,如股票行情、新闻推送等。与 WebSocket 相比,SSE 更轻量级,且只支持服务器到客户端的单向通信。

Spring Boot 集成 SSE

下面我将介绍如何在 Spring Boot 中实现 SSE 功能:

1. 添加依赖

首先在 pom.xml 中添加 Spring Web 依赖:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
2. 创建 SSE 控制器

以下是一个简单的 SSE 控制器示例,它可以定时向客户端推送消息:

java 复制代码
package com.example.sse.demo.controller;

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.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

@RestController
public class SseController {

    // 创建一个单线程的调度器
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    @GetMapping(path = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamSseMvc() {
        SseEmitter emitter = new SseEmitter(60_000L); // 设置超时时间为60秒

        // 启动定时任务,每秒发送一次消息
        scheduler.scheduleAtFixedRate(() -> {
            try {
                // 发送数据
                emitter.send(SseEmitter.event()
                        .id(String.valueOf(System.currentTimeMillis()))
                        .name("message")
                        .data("Hello, SSE! Time: " + System.currentTimeMillis()));
            } catch (IOException e) {
                // 发生异常时,完成发射器
                emitter.completeWithError(e);
            }
        }, 0, 1, TimeUnit.SECONDS);

        // 设置完成回调
        emitter.onCompletion(() -> System.out.println("SSE connection completed"));
        // 设置超时回调
        emitter.onTimeout(() -> {
            System.out.println("SSE connection timed out");
            emitter.complete();
        });
        // 设置错误回调
        emitter.onError((ex) -> {
            System.out.println("SSE connection error: " + ex.getMessage());
            emitter.completeWithError(ex);
        });

        return emitter;
    }
}
3. 创建客户端页面

创建一个简单的 HTML 页面来接收服务器推送的消息:

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <title>SSE Demo</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }
        #messages {
            border: 1px solid #ccc;
            padding: 10px;
            margin-top: 10px;
            height: 200px;
            overflow-y: auto;
        }
    </style>
</head>
<body>
    <h1>SSE Demo</h1>
    <button onclick="startSse()">Start SSE</button>
    <button onclick="stopSse()">Stop SSE</button>
    
    <div id="messages"></div>

    <script>
        let eventSource;

        function startSse() {
            // 创建 EventSource 实例连接到服务器
            eventSource = new EventSource('/sse');
            
            // 监听 message 事件
            eventSource.onmessage = function(event) {
                const messagesDiv = document.getElementById('messages');
                const newMessage = document.createElement('div');
                newMessage.textContent = `[${new Date().toLocaleTimeString()}] ${event.data}`;
                messagesDiv.appendChild(newMessage);
            };
            
            // 监听错误事件
            eventSource.onerror = function(error) {
                console.error('EventSource failed:', error);
                const messagesDiv = document.getElementById('messages');
                const errorMessage = document.createElement('div');
                errorMessage.textContent = `Error: ${error}`;
                errorMessage.style.color = 'red';
                messagesDiv.appendChild(errorMessage);
                
                // 关闭连接
                eventSource.close();
            };
        }

        function stopSse() {
            if (eventSource) {
                eventSource.close();
                const messagesDiv = document.getElementById('messages');
                const statusMessage = document.createElement('div');
                statusMessage.textContent = 'Connection closed';
                statusMessage.style.color = 'blue';
                messagesDiv.appendChild(statusMessage);
            }
        }
    </script>
</body>
</html>

运行和测试

  1. 启动 Spring Boot 应用
  2. 访问客户端页面(例如:http://localhost:8080/index.html
  3. 点击 "Start SSE" 按钮开始接收服务器推送的消息
  4. 点击 "Stop SSE" 按钮停止接收消息

关键技术点说明

  1. 服务器端

    • 使用 SseEmitter 处理 SSE 连接
    • 设置适当的超时时间
    • 实现错误处理和连接关闭逻辑
    • 使用 MediaType.TEXT_EVENT_STREAM_VALUE 指定响应类型
  2. 客户端

    • 使用 EventSource 对象连接到 SSE 服务器
    • 监听 message 事件接收服务器推送的数据
    • 监听 error 事件处理连接错误
    • 使用 close() 方法关闭连接

注意事项

  1. 生产环境中应考虑使用连接池和更健壮的错误处理机制
  2. 长时间运行的 SSE 连接可能需要处理网络中断和重连问题
  3. 考虑添加身份验证和授权机制保护 SSE 端点
  4. 对于大量并发连接,应评估服务器性能和资源消耗

通过以上步骤,你可以在 Spring Boot 应用中实现基本的 SSE 功能,实现服务器向客户端的实时数据推送。

相关推荐
初次攀爬者1 分钟前
Kafka 基础介绍
spring boot·kafka·消息队列
华仔啊1 分钟前
Stream 代码越写越难看?JDFrame 让 Java 逻辑回归优雅
java·后端
ray_liang9 分钟前
用六边形架构与整洁架构对比是伪命题?
java·架构
用户8307196840829 分钟前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
哈密瓜的眉毛美12 分钟前
零基础学Java|第五篇:进制转换与位运算、原码反码补码
后端
开心就好202537 分钟前
免 Xcode 的 iOS 开发新选择?聊聊一款更轻量的 iOS 开发 IDE kxapp 快蝎
后端·ios
Java编程爱好者40 分钟前
为什么国内大厂纷纷”弃坑”MySQL,转投PostgreSQL阵营?
后端
神奇小汤圆1 小时前
金三银四Java面试题及答案汇总(2026持续更新)
后端
Ray Liang1 小时前
用六边形架构与整洁架构对比是伪命题?
java·python·c#·架构设计
颜酱1 小时前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法