使用 ASP.NET Core WebAPI 实现 Server-Sent Events(SSE)进行流式响应
2025-11-17 1775 0
什么是 Server-Sent Events(SSE)
Server-Sent Events(简称 SSE)是一种基于 HTTP 的单向通信机制,允许服务器不断地向客户端推送实时事件流。与 WebSocket 不同,SSE 是服务器到客户端的单向传输,但它使用标准的 HTTP 协议,并且客户端通过 EventSource API 来接收数据,这使得它在实现上非常简单且轻量。
在 SSE 中,服务器会将响应的 Content-Type 设置为 text/event-stream,然后以特定格式发送数据(通常是以 data: 开头),每条事件以两个换行符结束。客户端建立连接后可以自动重连,并支持事件 ID,这对断线重连很有帮助。
为什么在 ASP.NET Core WebAPI 中使用 SSE
在 WebAPI 中使用 SSE 有很多实用场景:
-
实时更新:比如监控仪表盘、系统状态通知、日志推送等。
-
逐字输出:类似 ChatGPT 的响应流式输出场景。
-
推送通知:无需轮询即可实时推送服务器端的信息。
-
资源节省:相比于轮询,SSE 连接更稳定、请求更少,服务器开销更低。
此外,ASP.NET Core 本身对异步编程、流式写入支持良好,非常适合用于 SSE 实现。
在 ASP.NET Core WebAPI 中实现 SSE
下面是一个基本示例,演示如何在 ASP.NET Core WebAPI 中搭建 SSE 端点。
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class SseController : ControllerBase
{
[HttpGet("stream")]
public async Task StreamEvents(CancellationToken cancellationToken)
{
Response.ContentType = "text/event-stream";
Response.Headers.Add("Cache-Control", "no-cache");
Response.Headers.Add("Connection", "keep-alive");
int counter = 0;
while (!cancellationToken.IsCancellationRequested)
{
counter++;
// 发送 ID(可选)
await Response.WriteAsync($"id: {counter}\n");
// 发送事件类型(可选),默认是 message 事件
// await Response.WriteAsync("event: message\n");
// 发送数据内容
await Response.WriteAsync($"data: {{\"msg\":\"当前计数 {counter}\"}}\n\n");
await Response.Body.FlushAsync();
// 等待一定时间再发送下一条
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
}
}
关键点说明:
-
Content-Type 设置:必须是
text/event-stream,这样客户端才会将其识别为 SSE 流。 -
Headers 管理:
Cache-Control: no-cache和Connection: keep-alive对于维持连接稳定非常重要。 -
事件格式:数据前缀
data:,每条事件以两个换行符\n\n结束。如果需要,可以加id:和event:字段。 -
Flush 写入:用
Response.Body.FlushAsync()保证数据即时发送,不被缓冲。 -
取消处理:通过
CancellationToken检查连接状态,客户端断开后可以及时终止循环。
客户端接收 SSE(浏览器示例)
前端使用原生 JavaScript(浏览器)接收 SSE 比较简单:
const source = new EventSource("/api/sse/stream");
source.onopen = () => {
console.log("连接已打开");
};
source.onmessage = (e) => {
// 标准消息
console.log("接收到消息:", e.data);
};
source.onerror = (e) => {
console.error("连接错误或已关闭", e);
};
source.addEventListener("message", (e) => {
console.log("自定义处理 message 事件:", e.data);
});
这里,EventSource 会自动尝试重连,并支持 Last-Event-ID(如果服务器发送了 id: 字段)。
连接管理与注意事项
在实际项目中,实现 SSE 时还需要考虑以下问题:
- 并发连接数:SSE 是基于普通 HTTP 长连接的。如果客户端数量非常多(例如上万),可能对服务器造成较大的连接压力。需要考虑水平扩展(如 Kubernetes + 多实例)或使用 WebSocket / SignalR 替代。
- 心跳 / 保活机制:如果长时间没有事件发送,可能连接会被中间网络设备(如负载均衡器)关闭。可以定期发送空 data 或者 ping 消息来保持连接。
- 断线重连策略:虽然浏览器会自动重连,但你可以通过
retry:字段设置客户端重连时间,或使用id:字段记录最后事件 ID 实现断线续传。 - CORS 支持:如果前端跨域访问 SSE 端点,需要在服务器中配置 CORS,确保响应中包含适当的
Access-Control-Allow-Origin和其它头。 - 资源释放:当连接关闭时,要及时释放资源(例如取消后台循环、关闭 Task 等),否则可能导致内存泄漏或资源浪费。
经典应用场景
-
Chat 消息流:用 SSE 实现聊天客户端逐条消息的推送。
-
进度条 / 任务状态:后台任务(如文件处理、数据分析)可以通过 SSE 向前端推送进度。
-
监控仪表盘:服务器状态、CPU / 内存 / 日志等实时展示。
-
新闻 / 通知推送:向用户推送新消息、提醒、公告等。
SSE 与 WebSocket 的对比
| 特性 | SSE | WebSocket |
|---|---|---|
| 通信方向 | 单向(服务器 → 客户端) | 双向 |
| 协议基础 | HTTP | WebSocket 协议(升级) |
| 实现复杂度 | 简单,使用标准 HTTP | 较复杂,需要处理双向通信 |
| 连接重连 | 浏览器自动重连 | 自行管理重连逻辑 |
| 适用场景 | 实时通知、单向数据流 | 聊天、游戏、实时协作等双向通信 |
根据项目需求选择合适技术。如果只需要服务器向客户端推送信息,且不需要高频的大量双向交互,SSE 通常是更轻量且简单的选择。
总结
-
SSE(Server-Sent Events)是基于 HTTP 的单向实时推送机制,非常适合服务器向客户端持续推送数据。
-
在 ASP.NET Core WebAPI 中实现 SSE 非常简单:只需设置
Content-Type、写入指定格式数据,并定期Flush即可。 -
实战中还需处理连接管理、并发压力、心跳、断线重连等问题。
-
对于适合场景(如进度推送、通知、日志流),SSE 是一个比轮询更高效、更简单的实现方式。