ASP.NET Core 中的服务器发送事件不适用于实时推送
推送通知是现代Web应用程序中一个非常重要的功能,它可以在不需要用户不断检查变化的情况下实时更新信息。Server-Sent Events(SSE)是一种简单的单向通信方式,它允许服务器通过HTTP持续发送数据到客户端。与WebSockets不同,SSE只允许服务器向客户端发送消息,特别适合发送通知、股票价格更新或实时比分等场景。
在本文中,我们将学习如何在ASP.NET Core中设置SSE,以便向客户端发送实时推送通知。
一. 为什么使用Server-Sent Events(SSE)?
SSE 是一种简单且有效的实时通信技术,适用于各种场景。以下是选择SSE的几个主要原因:
1. 使用简单
SSE基于标准的HTTP协议,因此它与防火墙和代理服务器高度兼容,不需要额外的设置。
2. 单向通信
SSE非常适合需要从服务器向客户端单向发送数据的场景,例如推送通知、数据更新等,而不需要双向通信。
3. 自动重连
如果连接中断,SSE会自动尝试重新连接,保证数据传输的连续性。
4. 适用于多客户端
SSE在需要同时向多个客户端发送更新时非常高效,节省了带宽和资源。
二. 在ASP.NET Core中设置SSE
下面我们将逐步介绍如何在ASP.NET Core中构建一个基于SSE的通知服务。
1. 创建新的ASP.NET Core项目
首先,如果你还没有一个ASP.NET Core Web API项目,创建一个新的项目。
dotnet new webapi -n SSEPushNotification
2. 创建SSE订阅端点
在这一部分,我们将在服务器端设置一个SSE端点,供客户端连接。服务器将通过该端点向客户端发送通知。
public static class NotificationSubscriptionEndpoints
{
public static void MapNotificationSubscriptionEndpoints(this WebApplication app)
{
// 设置SSE订阅端点,客户端将通过该端点订阅通知
app.MapGet("api/notifications/subscribe/{appId}/{userId}",
async ([FromRoute] string appId, [FromRoute] string userId,
IServerSentEventService sseService, HttpContext context) =>
{
// 检查appId和userId是否有效
if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(userId))
{
// 如果无效,返回400错误
context.Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}
// 向客户端订阅通知
await sseService.SubscribeToNotificationsAsync(appId, userId, context);
});
}
}
代码说明:
-
MapGet
: 定义一个GET端点,允许客户端订阅通知。 -
SubscribeToNotificationsAsync
: 通过IServerSentEventService
服务将客户端添加到通知订阅列表。 -
使用 appId
和userId
来区分不同的应用和用户。
3. 发送通知给客户端
要向已订阅的客户端发送通知,我们需要通过正确的客户端SSE流发送消息。
public static class NotificationSendEndpoints
{
public static void MapNotificationSendEndpoints(this WebApplication app)
{
// 设置发送通知的端点
app.MapPost("api/notifications/send/{appId}/{userId}",
async ([FromRoute] string appId, [FromRoute] string userId,
IServerSentEventService sseService, HttpContext context) =>
{
// 检查appId和userId是否有效
if (string.IsNullOrWhiteSpace(appId) || string.IsNullOrWhiteSpace(userId))
{
// 如果无效,返回400错误
context.Response.StatusCode = StatusCodes.Status400BadRequest;
return;
}
// 这里你可以从请求体读取消息
var message = "validating the SSE"; // 示例消息
// 发送消息给订阅的客户端
await sseService.SendNotificationToClientsAsync(appId, userId, message);
});
}
}
代码说明:
-
MapPost
: 定义一个POST端点,用于向客户端发送消息。 -
SendNotificationToClientsAsync
: 将消息发送给已订阅的客户端。
4. 保持SSE连接活跃
为了保持SSE连接处于活跃状态,服务器可以每隔一段时间发送一个 "ping" 消息,以防止代理服务器或负载均衡器导致的连接超时。
// 每隔10秒发送一次ping消息,以保持连接活跃
await Task.Delay(10000);
// 发送ping消息给客户端
await SendMessageToClient(connectionId, "ping");
代码说明:
-
Task.Delay(10000)
: 延迟10秒发送 "ping" 消息,保持SSE连接活跃。
5. 设置客户端:Angular
在客户端,我们使用Angular应用来接收来自ASP.NET Core SSE端点的通知。我们将创建一个组件,订阅特定用户的通知流,并实时显示接收到的消息。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-notifications',
templateUrl: './notifications.component.html',
styleUrls: ['./notifications.component.css']
})
export class NotificationsComponent implements OnInit {
notifications: string[] = [];
constructor() { }
ngOnInit(): void {
// 订阅SSE推送通知
const eventSource = new EventSource('http://localhost:5168/api/notifications/subscribe/appId/userId');
// 当接收到消息时,将其添加到通知列表中
eventSource.onmessage = (event) => {
this.notifications.push(event.data);
};
}
}
代码说明:
-
EventSource
: 用于建立与服务器的SSE连接。 -
onmessage
: 监听服务器发送的消息,并将消息添加到notifications
列表中。
6. 负载均衡环境中的连接管理
在负载均衡环境中,SSE连接可能被定向到不同的服务器。为确保一切正常工作,有以下两种方案:
-
Sticky Sessions: 确保负载均衡器支持粘性会话,这样客户端始终连接到相同的服务器实例。 -
集中式消息存储: 使用RabbitMQ或SQL Server等集中式消息存储,每个服务器实例都可以从该中心存储中监听消息并发送给连接的客户端。
三. 结论
Server-Sent Events(SSE)提供了一种简单且高效的方式来从服务器向客户端发送通知和实时更新。在本文中,我们使用ASP.NET Core和Angular构建了一个基于SSE的通知系统。SSE非常适合从服务器到客户端的单向通信,如通知、实时更新等。