Webman 如何在控制器中完成 SSE 实时流式输出?
今天我们来聊一聊 SSE(Server-Sent Events,服务器发送事件),这是一种非常适合实时数据推送的技术。与 WebSocket 不同,SSE 只支持服务器到客户端的单向通信。
SSE 的核心是建立一个持久化的 HTTP 长连接,让服务器可以随时向客户端推送数据,而无需客户端频繁发起请求。它是一种 “服务器到客户端” 的单向通信模式,与 WebSocket 的双向通信形成互补。
其优势在于原生支持自动重连、API 简单(EventSource 接口)、服务器实现成本低,适合无需客户端回传数据的场景。与 WebSocket 相比,SSE 更专注于单向推送,实现和维护成本更低,是实时通知、日志展示等场景的理想选择。
服务和客户端

本质上,它采用流式方法实现,客户端向服务器发起连接请求并保持连接打开。然后服务器主动向客户端推送消息。使用服务器发送事件(SSE)从服务器发送到客户端的数据必须是 UTF-8 编码的,返回的内容类型是 text/event-stream 。
例如,在使用 ChatGPT 时,当你向它提问时,你会看到它逐字显示答案。实际上,ChatGPT 在计算的同时主动“推送”预计算数据给你,利用 SSE 技术边计算边返回数据,从而避免了可能导致直接页面关闭的长时间接口等待。
关键特性
- 单向通信:仅支持服务器向客户端推送数据,客户端无法通过 SSE 向服务器发送数据(需配合 HTTP 或 WebSocket 实现双向交互)
- 长连接:一次连接建立后持续保持,避免短连接的反复握手开销;
- 自动重连:连接意外断开时,客户端会自动重试连接(默认间隔 3 秒);
- 文本流:数据以 UTF-8 文本流格式传输,二进制数据需编码后发送。
代码示例
这里客户端代码为了解决访问跨域问题,直接使用 webman 视图完成客户端页面。模板是使用的:think-template。
服务端
<?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)
*/
publicfunction client(Request $request): Response
{
return view('stream/client', ['name' => '码云笔记']);
}
/**
* @desc server
* @param Request $request
* @return Response
* @author Tinywan(ShaoBo Wan)
*/
publicfunction server(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);
}
}
必须设置的 HTTP 响应头
Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive
可选但强烈推荐:
X-Accel-Buffering: no # 关闭 Nginx 等代理的缓冲(非常重要)
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 | 忽略 |
数据推送:基于规范的文本流格式
服务器通过 HTTP 长连接持续向客户端发送 “事件”,每个事件需遵循严格的文本格式,以\n\n 作为结束符。
格式 1:默认事件(客户端通过 onmessage 接收)
data: 您有一条新消息\n data: 内容:Hello World\n\n # 多行 data 会合并为一条消息
格式 2:自定义事件名(客户端通过 addEventListener 接收)
event: orderStatus # 事件名(如“订单状态更新”) id: 10086 # 事件 ID(用于重连时定位断点) data: 订单已发货\n\n
格式 3:心跳保活(客户端忽略,仅维持连接)
: 这是注释,无实际数据,防止长连接超时\n\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
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 协议
结语
SSE(Server-Sent Events)凭借其单向通信、长连接、自动重连及文本流传输等特性,在实时数据推送场景中展现出独特优势。相较于 WebSocket,SSE 无需处理复杂的双向通信逻辑,降低了开发成本与服务器资源消耗,尤其适合无需客户端回传数据的业务场景。其原生支持的 EventSource 接口与简洁的协议规范,使得开发者能够快速实现实时通知、日志流展示、AI 流式回复等功能。从社交平台的消息提醒到金融行情的实时更新,从运维监控的指标追踪到在线协作的状态同步,SSE 以轻量级、高兼容性的方式满足了中低频、小数据量、长连接的实时需求。对于追求简单高效、资源优化的实时应用开发,SSE 无疑是一个值得优先考虑的技术方案,能够在保障用户体验的同时,显著提升系统的可维护性与扩展性。
以上关于Webman 如何在控制器中完成 SSE 实时流式输出?的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » Webman 如何在控制器中完成 SSE 实时流式输出?
微信
支付宝