🔹 一、SSE 是什么?
✅ 定义:
SSE(Server-Sent Events) 是一种 基于 HTTP 的、单向的、服务器向客户端推送数据的技术。
简单说:服务器"主动发消息"给浏览器,客户端接收即可。
🔹 二、SSE 的核心特性
| 特性 | 说明 |
|---|---|
| 单向通信 | 只能由服务器 → 客户端推送数据,客户端无法通过 SSE 向服务器发送消息(除非结合其他方式如 AJAX) |
| 基于 HTTP/1.1 | 使用普通的 HTTP 协议,支持长连接(持久连接) |
| 自动重连机制 | 浏览器会自动尝试重新连接,如果连接断开了(比如网络波动) |
| 文本流格式 | 数据以 text/event-stream MIME 类型传输,格式为纯文本,支持 data:, event:, id:, retry: 等字段 |
| 兼容性良好 | 现代浏览器(Chrome、Firefox、Safari、Edge)均支持,但不支持 IE |
🔹 三、SSE 的典型应用场景
| 场景 | 说明 |
|---|---|
| 实时通知(如系统告警、订单状态更新) | 服务器有新事件,立刻推送给前端 |
| 股票行情实时刷新 | 每秒更新一次价格 |
| 日志流实时查看 | 服务器端输出日志,前端实时显示 |
| 在线直播评论(轻量级) | 比如弹幕,不需要客户端发消息 |
| 心跳检测与在线状态 | 服务器定期发送心跳包,判断客户端是否在线 |
🔹 四、SSE 与 WebSocket(ws/wss)的对比
| 对比维度 | SSE | WebSocket (ws/wss) |
|---|---|---|
| 通信方向 | 单向(服务端 → 客户端) | 双向(客户端 ↔ 服务端) |
| 协议基础 | HTTP/1.1(基于 HTTP) | 独立协议(升级为 WebSocket 协议) |
| 是否支持加密 | ✅ 可通过 HTTPS 使用(https://) |
✅ wss:// 完全加密 |
| 连接方式 | 长连接,但基于 HTTP | 独立连接,更高效 |
| 自动重连 | ✅ 浏览器内置支持 | ❌ 需手动实现 |
| 数据格式 | 文本流(text/event-stream) |
二进制或文本(JSON、Protobuf 等) |
| 适用场景 | 服务器推送数据,客户端接收 | 实时双向通信(聊天、游戏、协作) |
| 浏览器兼容性 | Chrome/Firefox/Safari/Edge(IE 不支持) | 所有现代浏览器都支持 |
| 开发复杂度 | ⭐ 简单,适合快速集成 | ⭐⭐⭐ 较复杂,需处理连接管理、心跳、错误处理等 |
🔹 五、SSE 与 WebSocket 区别
在说区别前,先说说它们之间的共性
共性
-
都用于实现实时通信
- 无论是 SSE 还是 WebSocket,都是为了解决传统 HTTP 请求/响应模式的"延迟"问题。
-
都支持长连接(持久连接)
- 虽然底层机制不同,但都能保持连接打开,实现"服务器主动通知"。
-
都常用于现代 Web 应用中
- 比如:实时聊天、监控面板、通知系统、在线协作工具等。
区别:
| 关键差异 | SSE | WebSocket |
|---|---|---|
| 是否双向 | ❌ 否 | ✅ 是 |
| 是否基于 HTTP | ✅ 是 | ❌ 否(升级协议) |
| 是否适合复杂交互 | ❌ 一般 | ✅ 优秀 |
🔹 六、如何选择?
✅ 如果只需要"服务器推消息给客户端",选 SSE(简单、安全、易用);
✅ 如果需要"客户端和服务端来回发消息",比如聊天、游戏、协同编辑,选 WebSocket。
🔹 七、应用场景举例
场景:实时股票行情推送
| 方案 | 说明 |
|---|---|
| SSE | 服务器每秒推送一次行情数据(如价格、涨跌幅),客户端只需监听即可。代码简单,无需维持双向连接。✅ 推荐 |
| WebSocket | 客户端连接后,服务器持续推送行情数据。虽然也能实现,但"只推不收"时,WebSocket 显得"杀鸡用牛刀"。⚠️ 不推荐用于单向推送 |
✅ Flask实现的SSE示例
python
import json
import time
from flask import Flask, Response
app = Flask(__name__)
# 模拟实时推送数据
def generate_events():
"""生成 SSE 数据流"""
counter = 0
while True:
# 每隔 2 秒发送一次消息
time.sleep(2)
counter += 1
message = {
"event": "time_update",
"id": str(counter),
"data": f"当前时间: {time.strftime('%Y-%m-%d %H:%M:%S')}",
"retry": 5000 # 重连间隔 5 秒
}
# 以 text/event-stream 格式输出
yield f"data: {json.dumps(message)}\n\n"
@app.route('/stream')
def stream():
"""SSE 接口:返回事件流"""
return Response(generate_events(), mimetype='text/event-stream')
@app.route('/')
def index():
"""返回前端 HTML 客户端页面"""
return '''
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>SSE 实时消息</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
#log { border: 1px solid #ccc; padding: 10px; height: 400px; overflow-y: scroll; background: #f9f9f9; }
.msg { margin: 8px 0; padding: 6px; background: #e3f2fd; border-left: 4px solid #2196f3; }
</style>
</head>
<body>
<h2>🚀 实时消息推送(SSE)</h2>
<div id="log"></div>
<script>
const log = document.getElementById('log');
// 创建 EventSource 实例,连接到 /stream
const eventSource = new EventSource('/stream');
// 监听 message 事件(来自服务器的 data 字段)
eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
const msgDiv = document.createElement('div');
msgDiv.className = 'msg';
msgDiv.textContent = data.data;
log.appendChild(msgDiv);
log.scrollTop = log.scrollHeight; // 自动滚动到底部
};
// 监听错误事件
eventSource.onerror = function(event) {
console.error('SSE 错误:', event);
alert('与服务器的连接中断,请刷新页面重试。');
};
</script>
</body>
</html>'''
if __name__ == '__main__':
print("🚀 启动 Flask SSE 服务...")
print("访问 http://localhost:5000 查看实时消息")
app.run(debug=True, host='localhost', port=5000)
Tips
- Response(..., mimetype='text/event-stream') 必须设置 MIME 类型为 text/event-stream,否则浏览器不识别
- yield f"data: {json.dumps(...)}\n\n" 每条消息必须以 data: 开头,结尾必须有两个换行符 \n\n
- eventSource.onmessage 客户端监听来自服务器的消息(对应 data: 字段)
- retry: 5000 可选字段,表示客户端在断开后等待 5 秒重连
- 自动重连 浏览器自动实现,无需手动处理
功能扩展建议
- 推送 JSON 数据 用 json.dumps() 包裹数据
- 多种事件类型(如 event: alert) 使用 event: xxx 字段区分不同事件
- 支持多个客户端 Flask 本身支持并发连接,每个客户端独立流
- 用 Nginx 反向代理 配置 proxy_buffering off; 避免缓冲问题
- 添加认证/权限控制 在 @app.route('/stream') 前加 @login_required 等装饰器