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 功能,实现服务器向客户端的实时数据推送。

相关推荐
hqxstudying23 分钟前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·32 分钟前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
martinzh1 小时前
Spring AI 项目介绍
后端
Bug退退退1232 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠2 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github
前端付豪2 小时前
20、用 Python + API 打造终端天气预报工具(支持城市查询、天气图标、美化输出🧊
后端·python
爱学习的小学渣2 小时前
关系型数据库
后端
武子康2 小时前
大数据-33 HBase 整体架构 HMaster HRegion
大数据·后端·hbase
前端付豪2 小时前
19、用 Python + OpenAI 构建一个命令行 AI 问答助手
后端·python
凌览2 小时前
斩获 27k Star,一款开源的网站统计工具
前端·javascript·后端