You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于.NET 6 SignalR的离线通知内存存储重发方案咨询

.NET 6 SignalR 离线通知内存存储与重连补发实现方案

针对你的需求,我们可以通过内存存储服务实现离线通知暂存与重连补发,以下是具体实现步骤:

1. 创建离线通知内存存储服务

定义接口和线程安全的内存实现类,用ConcurrentDictionary处理多线程环境下的并发问题:

public interface IOfflineNotificationStore
{
    // 添加离线通知
    void AddNotification(string userId, NotificationModel notification);
    // 获取并清除用户的所有离线通知
    List<NotificationModel> GetAndClearNotifications(string userId);
}

public class InMemoryOfflineNotificationStore : IOfflineNotificationStore
{
    private readonly ConcurrentDictionary<string, List<NotificationModel>> _offlineNotifications = new();

    public void AddNotification(string userId, NotificationModel notification)
    {
        _offlineNotifications.AddOrUpdate(
            userId,
            new List<NotificationModel> { notification },
            (key, existingList) =>
            {
                existingList.Add(notification);
                return existingList;
            });
    }

    public List<NotificationModel> GetAndClearNotifications(string userId)
    {
        if (_offlineNotifications.TryRemove(userId, out var notifications))
        {
            return notifications;
        }
        return new List<NotificationModel>();
    }
}

2. 注册服务到依赖注入容器

在Program.cs中将存储服务注册为单例(确保全局共享内存数据):

builder.Services.AddSingleton<IOfflineNotificationStore, InMemoryOfflineNotificationStore>();
// 确保IUserConnectionManager已正确注册
builder.Services.AddSingleton<IUserConnectionManager, UserConnectionManager>();

3. 修改NotificationHub逻辑

注入存储服务

在Hub构造函数中新增IOfflineNotificationStore注入,同时移除原代码中未使用的_userConnections冗余字段:

public class NotificationHub : Hub<INotificationHub>
{
    private readonly IUserConnectionManager _userConnectionManager;
    private readonly IOfflineNotificationStore _offlineNotificationStore;

    public NotificationHub(IUserConnectionManager userConnectionManager, IOfflineNotificationStore offlineNotificationStore)
    {
        _userConnectionManager = userConnectionManager;
        _offlineNotificationStore = offlineNotificationStore;
    }

    // ... 其他原有代码
}

重写OnConnectedAsync,补发离线通知

用户连接成功后,立即取出并发送该用户的所有离线通知:

public override async Task OnConnectedAsync()
{
    var token = Context.GetHttpContext().Request.Headers["access_token"];

    if (!string.IsNullOrEmpty(token))
    {
        var handler = new JwtSecurityTokenHandler();
        var jsonToken = handler.ReadToken(token) as JwtSecurityToken;
        if (jsonToken != null)
        {
            string userId = jsonToken.Claims.FirstOrDefault(claim => claim.Type == "email")?.Value;
            if (!string.IsNullOrEmpty(userId))
            {
                // 保存用户连接
                _userConnectionManager.KeppUserConnection(userId, Context.ConnectionId);
                
                // 补发离线通知
                var offlineNotifications = _offlineNotificationStore.GetAndClearNotifications(userId);
                foreach (var notification in offlineNotifications)
                {
                    await Clients.Client(Context.ConnectionId).SendNotification(notification);
                }

                var response = new ServerResponse
                {
                    status = "Accepted",
                };
                await Clients.Client(Context.ConnectionId).ConnectionResponse(response);
            }
            else
            {
                var response = new ServerResponse
                {
                    status = "Error",
                    message = "无法获取用户信息"
                };
                await Clients.Client(Context.ConnectionId).ConnectionResponse(response);
                Context.Abort();
            }
        }
        else
        {
            var response = new ServerResponse
            {
                status = "Error",
                message = "Token无效"
            };
            await Clients.Client(Context.ConnectionId).ConnectionResponse(response);
            Context.Abort();
        }
    }
    else
    {
        var response = new ServerResponse
        {
            status = "Error",
            message = "Token no proporcionado"
        };
        await Clients.Client(Context.ConnectionId).ConnectionResponse(response);
        Context.Abort();
    }
}

修改SendNotificationToSpecificUser,存储离线通知

发送通知时,若用户无在线连接,则将通知存入离线存储:

public async Task SendNotificationToSpecificUser(NotificationModel message, List<string> target)
{
    foreach (var userId in target)
    {
        var connectionIds = _userConnectionManager.GetUserConnections(userId);
        if (connectionIds.Any())
        {
            // 用户在线,直接发送
            foreach (var connectionId in connectionIds)
            {
                await Clients.Client(connectionId).SendNotification(message);
            }
        }
        else
        {
            // 用户离线,存储通知
            _offlineNotificationStore.AddNotification(userId, message);
        }
    }
}

注意事项

  • 内存存储局限性:服务重启后离线通知会丢失,若需持久化可后续扩展为数据库存储,当前方案满足内存存储需求。
  • 线程安全:使用ConcurrentDictionary确保多线程环境下的操作安全,Hub实例为每次请求创建,但存储服务为单例。
  • 原代码中UserConnection方法可整合到OnConnectedAsync中,避免逻辑分散。

内容的提问来源于stack exchange,提问作者Elias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 09:45:00