Bootstrap

Spring + SSE

1. 简介

Server-Sent Events(简称SSE)是一种允许服务器主动向客户端(如浏览器)推送信息的技术。它是HTML5规范的一部分,提供了一种单向通信机制,使得服务器可以实时向客户端发送数据更新。

关键点:

  1. 基本概念

    1. SSE基于HTTP协议,通过创建一个到服务器的持久连接来实现数据的实时推送。

    2. 客户端通过JavaScript的EventSource接口与服务器建立连接,并监听服务器发送的事件。

  2. 特点

    1. 单向通信:SSE只支持服务器到客户端的单向通信,如果需要双向通信,则可能需要使用WebSocket。

    2. 简单易用:相比于WebSocket,SSE的使用更简单,对服务器端的改动也较小。

    3. 断线重连:SSE默认支持断线重连,当连接中断时,客户端会自动尝试重新连接。

  3. 使用场景

    1. 实时消息通知,如新闻更新、股票价格变动等。

    2. 实时数据更新,如仪表板数据显示、在线协作工具等。

    3. 聊天室或社交媒体的实时更新。

  4. 客户端API

    1. EventSource对象用于创建和服务器的连接,并监听服务器发送的事件。

    2. onmessage事件处理器用于处理服务器发送的数据。

    3. onerror事件处理器用于处理连接错误。

  5. 服务器端实现

    1. 服务器需要设置响应头Content-Type: text/event-stream,以表明发送的是事件流。

    2. 服务器可以按照规定的格式发送数据,包括dataeventidretry字段。

  6. 数据格式

    1. SSE数据流是由多个消息组成,每个消息由多个字段组成,字段之间用换行符分隔。

    2. 例如:data: some text\n\n表示一个数据字段,event: usermessage\ndata: {"username": "bobby", "age": "22"}\n\n表示一个命名事件。

  7. 注意事项

    1. 为了保证数据的完整性,服务器发送的消息应该包含id字段,以便客户端在重连时可以从最后一个接收到的事件ID继续接收数据。

    2. SSE连接可能会受到HTTP/1.1的6个连接限制,但HTTP/2可以协商更高的连接数(默认100)。

  8. 兼容性

    1. SSE在现代浏览器中得到了广泛支持,但IE和Edge浏览器不支持SSE。

2. 基础示例

后端(Spring Boot)

添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

创建SSE控制器:创建一个控制器来处理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.ExecutorService;
import java.util.concurrent.Executors;

@RestController
public class SseController {

    private final ExecutorService executor = Executors.newCachedThreadPool();

    @GetMapping(value = "/stream-sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamSse() {
        SseEmitter emitter = new SseEmitter();
        executor.execute(() -> {
            try {
                for (int i = 0; true; i++) {
                    Thread.sleep(1000); // 每秒发送一次
                    emitter.send(SseEmitter.event().data("SSE - " + i));
                }
            } catch (IOException | InterruptedException e) {
                emitter.completeWithError(e);
            }
        });
        return emitter;
    }
}

前端(React)

创建SSE逻辑:在React组件中,使用Axios订阅SSE事件。

import React, { useEffect, useState } from 'react';
import axios from 'axios';

function App() {
    const [messages, setMessages] = useState([]);

    useEffect(() => {
        const source = new EventSource('http://localhost:8080/stream-sse');

        source.onmessage = (event) => {
            const newMessage = event.data;
            setMessages((prevMessages) => [...prevMessages, newMessage]);
        };

        source.onerror = (error) => {
            console.error('EventSource failed:', error);
            source.close();
        };

        return () => {
            source.close();
        };
    }, []);

    return (
        <div>
            <h1>SSE Messages</h1>
            <ul>
                {messages.map((message, index) => (
                    <li key={index}>{message}</li>
                ))}
            </ul>
        </div>
    );
}

export default App;

工作流程

  1. 前端创建 EventSource 实例: 在React组件中,当组件挂载时(即在 useEffect 钩子中),你会创建一个 EventSource 实例,指向后端的 /stream-sse 端点。

const source = new EventSource('http://localhost:8080/stream-sse');
  1. 自动触发后端处理器: 当 EventSource 实例被创建时,浏览器会自动向后端发送一个GET请求到 /stream-sse。这会触发你在Spring后端定义的 streamSse() 方法。

  2. 后端发送事件: 后端的 streamSse() 方法会创建一个 SseEmitter 实例,并在一个线程中不断发送事件。每当有新的数据需要发送时,后端就会通过 emitter.send() 方法将数据推送到前端。

  3. 前端接收事件: 前端会通过 source.onmessage 事件处理器接收后端发送的消息,并更新组件的状态以显示这些消息。

注:相较于WebSocket来说,SSE更轻量级,但是只支持服务端向客户端发送消息。

;