Spring Boot中集成 SSE

目录

  1. SSE简介
  2. SSE原理
  3. SSE的使用场景
  4. [在Spring Boot中集成SSE](#在Spring Boot中集成SSE)
  5. 总结

SSE简介

服务器发送事件(Server-Sent Events,SSE)是一种在HTTP协议上实现的服务器推送技术。它允许服务器单向地将实时更新推送到客户端。与WebSocket不同,SSE是基于HTTP协议的简化实现,非常适合需要从服务器向客户端单向推送数据的场景。

SSE原理

SSE通过HTTP协议的一个长连接来实现服务器到客户端的单向数据流。客户端通过发送一个普通的HTTP请求来建立连接,服务器接收到请求后,保持连接不断开,并通过这个连接持续地发送事件。客户端使用JavaScript的EventSource API来处理这些事件。

SSE的使用场景

SSE适用于以下应用场景:

  • 实时通知:如邮件通知、系统消息推送。
  • 实时更新:如股票行情、新闻更新。
  • 监控和仪表盘:实时监控数据的展示。
  • 社交媒体更新:如实时评论和点赞。

在Spring Boot中集成SSE

创建Spring Boot项目

首先,使用Spring Initializr或IDE(如IntelliJ IDEA)创建一个新的Spring Boot项目,选择合适的Spring Boot版本(如2.5.x或3.x),并添加以下依赖:

  • Spring Web

添加依赖

pom.xml中添加Spring Web的依赖:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- 其他依赖 -->
</dependencies>

创建SSE控制器

创建一个控制器来处理SSE连接和事件推送。

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

@RestController
public class SseController {

    @GetMapping("/sse")
    public SseEmitter handleSse() {
        // 创建一个新的SseEmitter实例,超时时间为30秒
        SseEmitter emitter = new SseEmitter(30_000L);

        // 创建一个ScheduledExecutorService来定时发送事件
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

        // 每秒发送一个当前时间的消息
        executor.scheduleAtFixedRate(() -> {
            try {
                // 发送事件,事件名称为"message",数据为当前时间戳
                emitter.send(SseEmitter.event().name("message").data("Current Time: " + System.currentTimeMillis()));
            } catch (IOException e) {
                // 发送失败时完成该Emitter
                emitter.completeWithError(e);
            }
        }, 0, 1, TimeUnit.SECONDS);

        // 在30秒后完成该Emitter
        executor.schedule(() -> emitter.complete(), 30, TimeUnit.SECONDS);

        return emitter;
    }
}

前端使用SSE

在前端使用JavaScript的EventSource来接收服务器发送的事件:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>SSE Demo</title>
</head>
<body>
    <h1>SSE Demo</h1>
    <div id="messages"></div>
    <script>
        // 创建一个新的EventSource实例,连接到服务器的/sse端点
        const eventSource = new EventSource("/sse");

        // 当收到服务器发送的消息时,执行此函数
        eventSource.onmessage = function(event) {
            // 获取消息展示的div
            const messagesDiv = document.getElementById("messages");
            // 创建一个新的div元素来展示新消息
            const newMessage = document.createElement("div");
            newMessage.textContent = event.data; // 设置div的文本内容为事件数据
            messagesDiv.appendChild(newMessage); // 将新消息添加到消息展示div中
        };

        // 当发生错误时,执行此函数
        eventSource.onerror = function(error) {
            console.error("EventSource failed: ", error);
            eventSource.close(); // 关闭EventSource
        };
    </script>
</body>
</html>

详细案例:股票价格实时推送

假设我们需要实现一个股票价格实时推送的功能,服务器定期向客户端发送股票价格更新。

创建StockService类

模拟股票价格变化的服务类。

java 复制代码
import org.springframework.stereotype.Service;

import java.util.Random;

@Service
public class StockService {
    private Random random = new Random();

    // 模拟获取股票价格的方法
    public double getStockPrice(String symbol) {
        // 返回一个随机价格
        return 100 + (random.nextDouble() * 50);
    }
}
更新SseController类

使用StockService类来推送股票价格。

java 复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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 StockService stockService;

    // 使用构造器注入StockService
    public SseController(StockService stockService) {
        this.stockService = stockService;
    }

    @GetMapping("/sse/{symbol}")
    public SseEmitter handleSse(@PathVariable String symbol) {
        // 创建一个新的SseEmitter实例,超时时间为30秒
        SseEmitter emitter = new SseEmitter(30_000L);

        // 创建一个ScheduledExecutorService来定时发送事件
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

        // 每秒发送一个股票价格更新
        executor.scheduleAtFixedRate(() -> {
            try {
                // 获取股票价格
                double price = stockService.getStockPrice(symbol);
                // 发送事件,事件名称为"stock-price",数据为股票价格
                emitter.send(SseEmitter.event().name("stock-price").data("Stock Price of " + symbol + ": " + price));
            } catch (IOException e) {
                // 发送失败时完成该Emitter
                emitter.completeWithError(e);
            }
        }, 0, 1, TimeUnit.SECONDS);

        // 在30秒后完成该Emitter
        executor.schedule(() -> emitter.complete(), 30, TimeUnit.SECONDS);

        return emitter;
    }
}
更新前端代码

在前端展示股票价格更新。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Stock Price SSE Demo</title>
</head>
<body>
    <h1>Stock Price SSE Demo</h1>
    <input type="text" id="symbol" placeholder="Enter stock symbol">
    <button onclick="connect()">Connect</button>
    <div id="messages"></div>
    <script>
        let eventSource;

        function connect() {
            const symbol = document.getElementById("symbol").value;
            if (eventSource) {
                eventSource.close(); // 关闭已有的连接
            }
            // 创建一个新的EventSource实例,连接到服务器的/sse/{symbol}端点
            eventSource = new EventSource("/sse/" + symbol);

            // 当收到服务器发送的消息时,执行此函数
            eventSource.onmessage = function(event) {
                // 获取消息展示的div
                const messagesDiv = document.getElementById("messages");
                // 创建一个新的div元素来展示新消息
                const newMessage = document.createElement("div");
                newMessage.textContent = event.data; // 设置div的文本内容为事件数据
                messagesDiv.appendChild(newMessage); // 将新消息添加到消息展示div中
            };

            // 当发生错误时,执行此函数
            eventSource.onerror = function(error) {
                console.error("EventSource failed: ", error);
                eventSource.close(); // 关闭EventSource
            };
        }
    </script>
</body>
</html>
相关推荐
用户2986985301422 分钟前
.NET 文档自动化:Spire.Doc 设置奇偶页页眉/页脚的最佳实践
后端·c#·.net
码路飞1 小时前
GPT-5.3 Instant 终于学会好好说话了,顺手对比了下同天发布的 Gemini 3.1 Flash-Lite
java·javascript
序安InToo1 小时前
第6课|注释与代码风格
后端·操作系统·嵌入式
xyy1231 小时前
C#: Newtonsoft.Json 到 System.Text.Json 迁移避坑指南
后端
洋洋技术笔记1 小时前
Spring Boot Web MVC配置详解
spring boot·后端
JxWang051 小时前
VS Code 配置 Markdown 环境
后端
navms1 小时前
搞懂线程池,先把 Worker 机制啃明白
后端
JxWang051 小时前
离线数仓的优化及重构
后端
Nyarlathotep01131 小时前
gin01:初探gin的启动
后端·go
JxWang051 小时前
安卓手机配置通用多屏协同及自动化脚本
后端