Skip to content
DAILY QUOTE

“ ”

全称Server Send Events: 是 HTML5 标准中定义的一种基于 HTTP 的服务器向客户端单向推送实时数据的协议。其核心特性包括:

  • 单向通信:服务器主动推送数据至客户端,客户端无需轮询;
  • 自动重连:连接中断后客户端自动尝试恢复;
  • 文本流式传输:数据格式为 text/event-stream,支持纯文本消息;
  • 事件类型支持:可自定义事件名称(如 event: update)。

经典的 HTTP 请求: 前端发一个请求,Tomcat 里的线程去查 MySQL、查 Redis,组装好一个 JSON,然后 return

  • 特点:一问一答,过河拆桥。回答完立刻断开连接。
  • 痛点:如果后端处理要花 20 秒,前端就得干等 20 秒,页面看着像死机了一样。

轮询 Polling: 解决干等,前端写定时器,每隔一段时间请求一次后端。

  • 痛点:太浪费服务器资源了!你的 Nginx 和 Tomcat 会被这种无意义的重复请求直接打死。

WebSocket: 双向的高级通道。前端和后端接通后,双方可以随时互相发消息。

  • 特点:全双工,适合做多人在线聊天室、对战小游戏。
  • 痛点:太重了,它是一个全新的协议,Nginx 还需要专门去配置协议升级,而且维护长连接对服务器端资源消耗很大。

SSE 依然是普通的 HTTP 请求,唯一不同的是,正常的 HTTP 响应头里写的是 Content-Type: application/json;而 SSE 的响应头是 Content-Type: text/event-stream

适用范围:

示例: 实现打字机效果

后端:写一个普通的 Controller,但返回类型必须是 SseEmitter

java
@RestController  
@RequestMapping("/api")  
@CrossOrigin  
public class SseDemoController {  
    @GetMapping(value = "/stream", produces = "text/event-stream;charset=UTF-8")  
    public SseEmitter stream(){  
        //1.建立连接,0L表示永不过时  
        SseEmitter emitter=new SseEmitter(0L);  
        //2.模拟大模型思考与吐字的过程,不能阻塞主线程  
        new Thread(()->{  
            try {  
                String[] words={"你","好",",","这","里","是","教","程","实","例"};  
                for(String word:words){  
                    Thread.sleep(500);  
                    emitter.send(word);  
                }  
                //3.数据发送完毕,告诉前端关掉水管  
                emitter.complete();  
            } catch (InterruptedException e) {  
                throw new RuntimeException(e);  
            } catch (IOException e) {  
                //发生异常,通知前端出错了  
                emitter.completeWithError(e);  
            }  
        }).start();  
        //4.立即把水管返回给前端  
        return emitter;  
    }  
}

前端:浏览器原生的 EventSource 对象就是专门用来接收 SSE 的

html
<!DOCTYPE html>  
<html lang="zh">  
<head>  
    <meta charset="UTF-8">  
    <title>SSE 打字机测试</title>  
    <style>        #chat-box {  
            width: 400px;  
            height: 200px;  
            border: 2px solid #333;  
            padding: 10px;  
            font-size: 20px;  
            background-color: #f9f9f9;  
        }  
    </style>  
</head>  
<body>  
  
<h2>AI 回答区:</h2>  
<div id="chat-box"></div>  
<button onclick="startChat()">问一下大模型</button>  
  
<script>  
    function startChat() {  
        const chatBox = document.getElementById('chat-box');  
        chatBox.innerText = ''; // 清空历史记录  
  
        // 1. 拨通后端的 SSE 接口  
        // 替换成Spring Boot真实的启动端口  
        const eventSource = new EventSource('http://localhost:8081/api/stream');  
        // 2. 监听消息:后端只要调用 emitter.send(),这里就会被触发  
        eventSource.onmessage = function(event) {  
            // event.data 就是后端传过来的那个字  
            chatBox.innerText += event.data;  
        };  
        // 3. 监听结束/错误:后端调用 emitter.complete() 时触发  
        eventSource.onerror = function(event) {  
            console.log("数据接收完毕或发生异常,断开连接");  
            // 极其重要:SSE 默认有断线重连机制,必须手动 close 才能真正掐断  
            eventSource.close();  
        };  
    }  
</script>  
  
</body>  
</html>