找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2352

积分

0

好友

316

主题
发表于 12 小时前 | 查看: 0| 回复: 0

引言

SSE(Server-Sent Events,服务器发送事件)是一种非常适合实时数据推送的技术。与 WebSocket 不同,SSE 只支持服务器到客户端的单向通信。

其核心是建立一个持久化的 HTTP 长连接,让服务器可以随时向客户端推送数据,而无需客户端频繁发起请求。它是一种“服务器到客户端”的单向通信模式,与 WebSocket 的双向通信形成互补。

SSE 的优势在于原生支持自动重连、API 简单(EventSource接口)、服务器实现成本低,适合无需客户端回传数据的场景。与 WebSocket 相比,SSE 更专注于单向推送,实现和维护成本更低,是实时通知、日志展示等场景的理想选择。

服务和客户端

SSE 客户端与服务器通信序列图

本质上,它采用流式方法实现,客户端向服务器发起连接请求并保持连接打开。然后服务器主动向客户端推送消息。使用服务器发送事件(SSE)从服务器发送到客户端的数据必须是 UTF-8 编码的,返回的内容类型是 text/event-stream

例如,在使用 ChatGPT 时,当你向它提问时,你会看到它逐字显示答案。实际上,ChatGPT 在计算的同时主动“推送”预计算数据给你,利用 SSE 技术边计算边返回数据,从而避免了可能导致直接页面关闭的长时间接口等待。

关键特性

  • 单向通信:仅支持服务器向客户端推送数据,客户端无法通过 SSE 向服务器发送数据(需配合 HTTP 或 WebSocket 实现双向交互)
  • 长连接:一次连接建立后持续保持,避免短连接的反复握手开销;
  • 自动重连:连接意外断开时,客户端会自动重试连接(默认间隔 3 秒);
  • 文本流:数据以 UTF-8 文本流格式传输,二进制数据需编码后发送。

代码示例

这里客户端代码为了解决访问跨域问题,直接使用webman视图完成客户端页面。模板是使用的:think-template

如何使用可以看这里哦!
https://www.workerman.net/doc/webman/view.html

服务端

<?php
/**
 * @desc 控制器中使用SSE
 * @author Tinywan(ShaoBo Wan)
 */
declare(strict_types=1);

namespace app\controller;

use support\Request;
use support\Response;
use Workerman\Connection\TcpConnection;
use Workerman\Protocols\Http\ServerSentEvents;
use Workerman\Timer;

class StreamController
{
/**
     * @desc client
     * @param Request $request
     * @return Response
     * @author Tinywan(ShaoBo Wan)
     */
publicfunctionclient(Request $request): Response
{
return view('stream/client', ['name' => '开源技术小栈']);
    }

/**
     * @desc server
     * @param Request $request
     * @return Response
     * @author Tinywan(ShaoBo Wan)
     */
publicfunctionserver(Request $request): Response
{
        $connection = $request->connection;
        $id = Timer::add(1, function()use($connection, &$id){
if ($connection->getStatus() !== TcpConnection::STATUS_ESTABLISHED) {
                Timer::del($id);
            }
            $connection->send(new ServerSentEvents([
'event' => 'message',
'data' => date('Y-m-d H:i:s').' 服务端发送数据',
'id' => 2026
            ]));
        });
        $header = [
'Content-Type' => 'text/event-stream',
'Cache-Control' => 'no-cache',
'Connection' => 'keep-alive',
        ];
return response('', 200, $header);
    }
}

Step1. 必须设置的 HTTP 响应头

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

可选但强烈推荐:

X-Accel-Buffering: no          # 关闭 Nginx 等代理的缓冲(非常重要)

Step2. SSE 消息的基本格式规则

每条消息由若干字段组成,每个字段独占一行,字段之间用换行符分隔,整条消息以两个连续换行符(\n\n)结束。

字段写法 作用说明 是否必须 示例 客户端如何获取
data: 消息正文内容(最核心字段) 必须 data: {"text": "Hello"}\n event.data
event: 自定义事件类型(不写默认为 message 可选 event: token\n addEventListener('token', ...)
id: 消息唯一标识,用于断线重连续传 推荐 id: 12345\n event.lastEventId
retry: 建议客户端重连间隔(毫秒),断开后客户端会参考此值自动重连 可选 retry: 5000\n 自动处理
:(冒号+空格) 注释行,客户端会完全忽略,常用于调试、心跳、日志 可选 : 这是一条调试信息\n 忽略

Step3. 数据推送:基于规范的文本流格式

服务器通过 HTTP 长连接持续向客户端发送 “事件”,每个事件需遵循严格的文本格式,以\n\n作为结束符。

格式 1:默认事件(客户端通过 onmessage 接收)

data: 您有一条新消息
data: 内容:Hello World\n  # 多行 data 会合并为一条消息

格式 2:自定义事件名(客户端通过 addEventListener 接收)

event: orderStatus  # 事件名(如“订单状态更新”)
id: 10086           # 事件 ID(用于重连时定位断点)
data: 订单已发货\n

格式 3:心跳保活(客户端忽略,仅维持连接)

: 这是注释,无实际数据,防止长连接超时\n

客户端

客户端请求

// 客户端创建 SSE 连接(仅支持 GET 方法)
const sse = new EventSource('/service/stream');

浏览器会自动发送 HTTP 请求,携带特殊头信息(如:Accept: text/event-stream)。

全部伪代码

<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>客户端 SSE 输出</title>
<style>
body {
font-family: system-ui, sans-serif;
margin: 20px;
background: #fafafa;
        }
#output {
white-space: pre-wrap;
word-wrap: break-word;
background: white;
border: 1px solid #ddd;
padding: 16px;
min-height: 80px;
line-height: 1.5;
font-family: Consolas, "Courier New", monospace;
overflow-y: auto;
        }
</style>
</head>
<body>

<h3>客户端 SSE 输出</h3>
<div id="output"> SSE 连接中...</div>

<script>
const output = document.getElementById('output');

const es = new EventSource('http://127.0.0.1:8787/stream/server');

    es.onmessage = function(e) {
        output.textContent += e.data + '\n';
        output.scrollTop = output.scrollHeight;
    };

    es.onopen = function() {
        output.textContent = '';  // 清空“连接中...”文字
    };
</script>

</body>
</html>

客户端:http://127.0.0.1:8787/stream/client

输出页面内容如下所示:

Web端SSE实时日志输出截图

SSE 典型应用场景

SSE 最适合服务器主动、单向、实时推送数据的场景,尤其在不需要客户端频繁上行实现简单资源占用低的情况下表现出色。以下是主要实际应用场景:

  • AI 智能助手 / 大模型流式生成
    支持“打字机”效果,逐 token / 逐句实时输出回答,显著降低用户感知等待时间,提升交互自然感和沉浸感。典型如 ChatGPT、DeepSeek、Claude 等产品的流式回复界面。
  • 实时通知与消息提醒
    用于推送新评论、点赞、私信、系统公告、好友在线状态等即时通知。服务器在事件发生时立即推送,避免客户端轮询浪费资源。常见于社交平台、论坛、电商订单状态更新。
  • 任务执行日志与进度实时展示
    后端任务(如模型训练、文件处理、部署、CI/CD 流水线、数据导入)的日志或进度,按行或按块实时追加到前端。用户看到类似终端 tail -f 的追随效果,常用于运维后台、开发工具、训练平台。
  • 实时新闻 / 资讯 / 内容 feed
    推送突发新闻、热点更新、推荐内容。服务器有新资讯时立即广播给订阅用户,适合新闻客户端、聚合阅读器、社区动态墙。
  • 股票 / 加密货币 / 金融行情实时更新
    推送最新股价、K 线增量、指数变动、外汇汇率等。客户端无需轮询,每秒或每变动推送一次,适用于交易终端、行情看板、投资 App。
  • 服务器 / 应用监控仪表盘
    实时展示 CPU、内存、磁盘、网络流量、QPS、错误率等指标,每几秒推送一次最新数值或时间序列点,用于绘制曲线图或警报触发。常见于运维监控系统、Prometheus + Grafana 风格的实时面板。
  • 多人在线协作状态同步
    在线文档、白板、表格、代码编辑器中推送“他人正在编辑”“光标位置”“内容变更”等提示。服务器广播操作状态给所有协作者,实现低延迟协同感知。
  • 直播 / 赛事比分 / 位置跟踪
    推送比分变化、直播弹幕增量、司机/骑手实时位置更新、比赛事件(如进球、犯规)。适合读-only 的实时数据流场景,如体育 App、外卖地图。

SSE 特别适用的业务特征

  • 数据方向:服务器 → 客户端 单向推送(客户端基本只订阅,不需频繁发送)
  • 更新频率:中低频(事件驱动,或每几秒~几十秒一次)
  • 数据体量:单次推送较小(几字节~几 KB 的文本/JSON)
  • 连接时长:较长(分钟~数小时)
  • 对可靠性要求:中等(丢失个别监控点或通知通常可接受)
  • 开发复杂度偏好:希望比 WebSocket 更简单、直接用 HTTP 协议

通过以上在 PHP Webman框架中的实践,你可以快速为应用添加轻量级的实时推送能力。无论是构建监控面板还是实现类似AI对话的高并发流式响应,SSE都是一个值得考虑的优雅方案。如果你有更多关于实时通信或Webman框架的问题,欢迎在技术社区交流探讨,例如在 云栈社区 的相关板块分享你的实践。




上一篇:从项目复盘到认知升级:架构师的高阶复盘实践框架与工具
下一篇:嵌入式存储新方案:FlashDB超轻量数据库技术解析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-24 18:56 , Processed in 0.378334 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表