返回

使用 ASP.NET Core WebAPI 实现 Server-Sent Events(SSE)进行流式响应

2025-11-17 ASP.NET Core WebAPI SSE 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-cacheConnection: 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 是一个比轮询更高效、更简单的实现方式。

顶部