跳到主要内容

IGeekFan.AspNetCore.SignalR.FreeRedis

SignalR 的 Redis Backplane,支持多节点实时消息广播。

功能概览

功能说明
消息广播多节点实时消息同步
WebSocket 扩展支持横向扩展
Redis Backplane基于 FreeRedis 的消息中继

安装

dotnet add package IGeekFan.AspNetCore.SignalR.FreeRedis

快速开始

场景:即时聊天系统

1. 配置 Redis

{
"ConnectionStrings": {
"Redis": "127.0.0.1:6379,password=***,defaultDatabase=1"
}
}

2. 注册服务

using FreeRedis;
using IGeekFan.AspNetCore.SignalR.FreeRedis;

var builder = WebApplication.CreateBuilder(args);

// 注册 Redis 客户端
var redis = new RedisClient(builder.Configuration.GetConnectionString("Redis"));
builder.Services.AddSingleton(redis);

// 注册 SignalR + Redis Backplane
builder.Services
.AddSignalR()
.AddFreeRedis(builder.Configuration.GetConnectionString("Redis"));

var app = builder.Build();

app.MapHub<ChatHub>("/hubs/chat");
app.Run();

3. 创建 Hub

public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}

public async Task SendToGroup(string group, string message)
{
await Clients.Group(group).SendAsync("ReceiveMessage", message);
}

public async Task JoinGroup(string group)
{
await Groups.AddToGroupAsync(Context.ConnectionId, group);
await Clients.Group(group).SendAsync("SystemMessage", $"{Context.User.Identity.Name} 加入了群组");
}

public async Task LeaveGroup(string group)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, group);
await Clients.Group(group).SendAsync("SystemMessage", $"{Context.User.Identity.Name} 离开了群组");
}
}

场景:实时通知系统

public class NotificationHub : Hub
{
private readonly INotificationService _notificationService;

public NotificationHub(INotificationService notificationService)
{
_notificationService = notificationService;
}

public async Task SendNotification(string userId, string title, string message)
{
// 发送给特定用户
await Clients.User(userId).SendAsync("ReceiveNotification", new
{
Title = title,
Message = message,
Timestamp = DateTime.Now
});
}

public async Task BroadcastNotification(string title, string message)
{
// 广播给所有连接的客户端
await Clients.All.SendAsync("ReceiveNotification", new
{
Title = title,
Message = message,
Timestamp = DateTime.Now
});
}
}

高级用法

客户端分组

// 加入组
await Groups.AddToGroupAsync(Context.ConnectionId, "admins");

// 离开组
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "admins");

// 向组内发送消息
await Clients.Group("admins").SendAsync("ReceiveMessage", "Admin only message");

用户特定消息

// 向特定用户发送消息
await Clients.User("user-id-123").SendAsync("ReceiveMessage", "Hello!");

// 向特定连接发送消息
await Clients.Client("connection-id-456").SendAsync("ReceiveMessage", "Hello!");

连接生命周期

public class ConnectionHub : Hub
{
public override async Task OnConnectedAsync()
{
// 客户端连接时
await Clients.All.SendAsync("SystemMessage", $"{Context.User.Identity.Name} 已上线");
await base.OnConnectedAsync();
}

public override async Task OnDisconnectedAsync(Exception? exception)
{
// 客户端断开时
await Clients.All.SendAsync("SystemMessage", $"{Context.User.Identity.Name} 已离线");
await base.OnDisconnectedAsync(exception);
}
}

前端集成

JavaScript 客户端

import * as signalR from "@microsoft/signalr";

const connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/chat")
.withAutomaticReconnect()
.configureLogging(signalR.LogLevel.Information)
.build();

// 监听消息
connection.on("ReceiveMessage", (user, message) => {
console.log(`${user}: ${message}`);
});

// 启动连接
connection.start().catch(err => console.error(err));

// 发送消息
connection.invoke("SendMessage", "User1", "Hello everyone!");

TypeScript 客户端

import { HubConnectionBuilder, LogLevel } from "@microsoft/signalr";

interface Notification {
Title: string;
Message: string;
Timestamp: Date;
}

class SignalRService {
private connection: signalR.HubConnection;

constructor() {
this.connection = new HubConnectionBuilder()
.withUrl("/hubs/notification")
.withAutomaticReconnect()
.configureLogging(LogLevel.Information)
.build();
}

async start(): Promise<void> {
await this.connection.start();
}

onNotification(callback: (notification: Notification) => void): void {
this.connection.on("ReceiveNotification", callback);
}

async sendNotification(userId: string, title: string, message: string): Promise<void> {
await this.connection.invoke("SendNotification", userId, title, message);
}
}

运维建议

建议说明
独立 Redis DB实时业务分配独立 Redis DB
监控连接数关注 WebSocket 连接数
监控广播延迟监控消息广播延迟
配置超时配置连接超时和重试策略
使用 TLS生产环境启用 TLS 加密

故障排查

问题解决方案
连接失败检查 Redis 连接字符串
消息丢失检查 Redis 持久化配置
性能问题检查 Redis 内存和连接数
认证失败检查 SignalR 认证配置

相关文档