6.13Server-Sent Events
随着人工智能聊天机器人 ChatGPT 的快速流行,其用户界面中模拟打字机效果的对话设计给人留下了深刻印象。这种生动逼真的交互体验,实际上是通过一种称为“服务器发送事件”(Server-Sent Events, SSE)的技术实现的。
Server-Sent Events 单向通信#
随着人工智能聊天机器人 ChatGPT 的快速流行,其用户界面中模拟打字机效果的对话设计给人留下了深刻印象。这种生动逼真的交互体验,实际上是通过一种称为“服务器发送事件”(Server-Sent Events, SSE)的技术实现的。
Server-Sent Events 是一种允许服务器主动向客户端(通常是浏览器)发送实时更新数据的通信技术。与传统的客户端请求-服务器响应模式不同,SSE 实现了服务器到客户端的单向、异步通信,从而无需客户端不断轮询服务器以获取最新数据。 这种技术极大地减轻了服务器的负担,并提高了数据传输的效率和实时性。
Server-Sent Events 的应用场景:
- 实时通知:可以用来实现实时的消息提醒或通知系统,如社交网络上的新消息提示或邮件到达通知。
- 数据流更新:对于需要持续更新的数据,如股票价格、天气信息或体育比赛结果,
SSE能够提供即时的数据更新。 - 进度报告:在执行耗时较长的任务时,比如文件上传或复杂计算过程中,
SSE可以用来向客户端报告任务的进度。 - 日志和监控:在开发和运维领域,
SSE可用于实时显示日志文件的变化或监控系统的健康状态。
以下示例展示了如何使用 Server-Sent Events 向服务器获取数据:
await httpRemoteService.ServerSentEventsAsync("https://localhost:7044/HttpRemote/Events" // 接收到数据时的操作 , async (data, token) => { Console.WriteLine(data.Data); await Task.CompletedTask; }, cancellationToken: cancellationToken);// 使用构建器模式await httpRemoteService.SendAsync(HttpRequestBuilder .ServerSentEvents("https://localhost:7044/HttpRemote/Events" // 接收到数据时的操作 , async (data, token) => { Console.WriteLine(data.Data); await Task.CompletedTask; }), cancellationToken: cancellationToken);Server-Sent Events 也支持以 IAsyncEnumerable<ServerSentEventsData> 的方式消费数据,让你可以使用 await foreach 来迭代每个轮询响应:
await foreach (var data in httpRemoteService.ServerSentEventsAsAsyncEnumerable("https://localhost:7044/HttpRemote/Events", cancellationToken: cancellationToken)){ Console.WriteLine(data.Data);}// 使用构建器模式await foreach (var data in httpRemoteService.SendAsAsyncEnumerable(HttpRequestBuilder.ServerSentEvents("https://localhost:7044/HttpRemote/Events"), cancellationToken)){ Console.WriteLine(data.Data);}data 参数的类型为 ServerSentEventsData,包含以下属性:
- 属性:
Event:事件类型(string类型)。Data:消息(string类型)。RawLine:原始消息行(string类型)。Id:事件ID(string类型)。Retry:重新连接的时间(以毫秒为单位的int类型)。CustomFields:自定义的字段数据(IReadOnlyCollection<KeyValuePair<string, string>>类型)。
您还可以监听连接成功和发送异常时的事件:
await httpRemoteService.ServerSentEventsAsync("https://localhost:7044/HttpRemote/Events" // 接收到数据时的操作 , async (data, token) => { Console.WriteLine(data.Data); await Task.CompletedTask; }, builder => builder // 连接打开时操作 .SetOnOpen(() => { Console.WriteLine("连接成功。"); }) // 连接未打开时操作 .SetOnError((ex) => { Console.WriteLine("连接错误。" + ex.Message); }), cancellationToken: cancellationToken);// 使用构建器模式await httpRemoteService.SendAsync(HttpRequestBuilder .ServerSentEvents("https://localhost:7044/HttpRemote/Events" // 接收到数据时的操作 , async (data, token) => { Console.WriteLine(data.Data); await Task.CompletedTask; }) // 连接打开时操作 .SetOnOpen(() => { Console.WriteLine("连接成功。"); }) // 连接未打开时操作 .SetOnError((ex) => { Console.WriteLine("连接错误。" + ex.Message); }), cancellationToken: cancellationToken);Server-Sent Events 特别适合那些需要服务器向客户端发送更新,但客户端不需要频繁向服务器发送请求的应用场景。无论是用于实时更新数据、提供进度报告还是实现简单的通知系统,SSE 都是一个值得考虑的选择。
HttpServerSentEventsBuilder 构建器#
HttpServerSentEventsBuilder 构建器是框架提供专门用来接收服务器 Server-Sent Events 推送事件所需的各项设置。HttpServerSentEventsBuilder 的构造函数是私有的,因此无法直接使用 new 关键字进行实例化,不过,框架提供了 HttpRequestBuilder.ServerSentEvents 的多个静态重载方法创建 HttpServerSentEventsBuilder 的实例。
HttpRequestBuilder.ServerSentEvents(requestUri, onMessage, configure); // 默认 GET 请求HttpRequestBuilder.ServerSentEvents(httpMethod, requestUri, onMessage, configure);HttpRequestBuilder.ServerSentEvents(requestUri, configure); // 默认 GET 请求HttpRequestBuilder.ServerSentEvents(httpMethod, requestUri, configure);此外,HttpServerSentEventsBuilder 包含以下配置功能:
// 默认为 GET 请求HttpRequestBuilder.ServerSentEvents("https://localhost:7044/HttpRemote/Events" , async (data, token) => { Console.WriteLine(data.Data); await Task.CompletedTask; }) // 设置默认重新连接的间隔时间,默认为 2 秒 .SetDefaultRetryInterval(2000) // 单位毫秒 // 设置最大重试次数,默认为 100 次 .SetMaxRetries(500) // 设置用于在与事件源的连接打开时的操作 .SetOnOpen(() => {}) // 设置用于在从事件源接收到数据时的操作 // .SetOnMessage(async (data, token) => {}) // 可通过初始时传入 // 设置用于在事件源连接未能打开时的操作 .SetOnError(exception => {}) // 设置 Server-Sent Events 事件处理程序 .SetEventHandler<CustomServerSentEventsEventHandler>() .SetEventHandler(typeof(CustomServerSentEventsEventHandler)) // 设置是否自动修正请求方法 // 若为 true 则当请求为 GET 或 HEAD 且包含请求内容时,自动将方法改为 POST;默认值为 true .SetAutoCorrectMethod(true) // 设置 HttpRequestBuilder 实例 .With(builder => {})); // 支持更多扩展在通过 HttpRequestBuilder.ServerSentEvents 方法成功构建 HttpServerSentEventsBuilder 实例后,您可以利用 Send、SendAsync 或 SendAsAsyncEnumerable 来执行发送操作。
httpRemoteService.Send(httpServerSentEventsBuilder, cancellationToken);await httpRemoteService.SendAsync(httpServerSentEventsBuilder, cancellationToken);await foreach (var data in httpRemoteService.SendAsAsyncEnumerable(httpServerSentEventsBuilder, cancellationToken)){ // 处理每个数据}Server-Sent Events 事件处理程序#
IHttpServerSentEventsEventHandler 接口允许您定义接收服务器 Server-Sent Events 推送事件的预处理操作。通过实现该接口,您可以创建自定义的 Server-Sent Events 事件处理程序,例如 CustomServerSentEventsEventHandler 类:
public class CustomServerSentEventsEventHandler : IHttpServerSentEventsEventHandler{ // 用于在与事件源的连接打开时的操作 void OnOpen(); // 用于在从事件源接收到数据时的操作 Task OnMessageAsync(ServerSentEventsData serverSentEventsData, CancellationToken cancellationToken); // 用于在事件源连接未能打开时的操作 void OnError(Exception exception);}要在应用程序中启用此处理程序,请在 Startup.cs 或 Program.cs 文件中注册 CustomServerSentEventsEventHandler 服务:
services.TryAddSingleton<CustomServerSentEventsEventHandler>();接下来,您可以在构建 HTTP 请求时指定此处理程序:
HttpRequestBuilder.ServerSentEvents("https://localhost:7044/HttpRemote/Events" , async (data, token) => { Console.WriteLine(data.Data); await Task.CompletedTask; }) .SetEventHandler<CustomServerSentEventsEventHandler>();HttpRequestBuilder.ServerSentEvents("https://localhost:7044/HttpRemote/Events" , async (data, token) => { Console.WriteLine(data.Data); await Task.CompletedTask; }) .SetEventHandler(typeof(CustomServerSentEventsEventHandler)); // 使用类型的方式