一、SignalR核心概念
1.1 SignalR架构原理
SignalR是ASP.NET Core提供的实时通信库,其核心机制包括:
- 自动传输选择:在WebSocket、Server-Sent Events和长轮询间自动选择最佳传输方式
- 连接管理:内置连接生命周期管理
- 消息总线:支持横向扩展的消息分发系统
- 中心(Hub):高级抽象层,简化客户端-服务器通信模式
1.2 适用场景分析
场景类型 | 典型应用 | SignalR优势 |
---|---|---|
即时通讯 | 聊天应用 | 低延迟、双向通信 |
实时监控 | 仪表盘 | 服务端主动推送 |
协同编辑 | 在线文档 | 状态同步高效 |
游戏交互 | 多人在线游戏 | 高频率消息交换 |
通知系统 | 订单状态更新 | 定向消息推送 |
二、服务端实现
2.1 基础服务配置
// Program.cs配置
var builder = WebApplication.CreateBuilder(args);
// 添加SignalR服务
builder.Services.AddSignalR(options => {
options.EnableDetailedErrors = true;
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
});
var app = builder.Build();
// 配置Hub路由
app.MapHub<ChatHub>("/hubs/chat");
app.MapHub<NotificationHub>("/hubs/notifications");
app.Run();
2.2 Hub类实现范例
public class ChatHub : Hub
{
private readonly IMessageService _messageService;
public ChatHub(IMessageService messageService)
{
_messageService = messageService;
}
// 客户端调用方法
public async Task SendMessage(string user, string message)
{
// 保存消息到数据库
var msg = await _messageService.SaveMessageAsync(user, message);
// 广播给所有客户端
await Clients.All.SendAsync("ReceiveMessage", msg.User, msg.Content, msg.Timestamp);
// 仅发送给特定组
await Clients.Group("VIP").SendAsync("VIPMessage", msg);
}
// 连接生命周期方法
public override async Task OnConnectedAsync()
{
await Groups.AddToGroupAsync(Context.ConnectionId, "General");
await base.OnConnectedAsync();
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, "General");
await base.OnDisconnectedAsync(exception);
}
}
三、客户端开发
3.1 JavaScript客户端
// 建立连接
const connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/chat")
.configureLogging(signalR.LogLevel.Information)
.build();
// 注册接收方法
connection.on("ReceiveMessage", (user, message, timestamp) => {
const msg = `${user}: ${message} (${new Date(timestamp).toLocaleTimeString()})`;
appendMessage(msg);
});
// 启动连接
async function startConnection() {
try {
await connection.start();
console.log("SignalR Connected.");
} catch (err) {
console.log(err);
setTimeout(startConnection, 5000);
}
}
// 调用服务端方法
async function sendMessage() {
const user = document.getElementById("userInput").value;
const message = document.getElementById("messageInput").value;
try {
await connection.invoke("SendMessage", user, message);
} catch (err) {
console.error(err);
}
}
3.2 .NET客户端
var connection = new HubConnectionBuilder()
.WithUrl("https://localhost:5001/hubs/chat")
.WithAutomaticReconnect(new[] {
TimeSpan.Zero,
TimeSpan.FromSeconds(2),
TimeSpan.FromSeconds(10),
TimeSpan.FromSeconds(30)
})
.ConfigureLogging(logging => {
logging.SetMinimumLevel(LogLevel.Debug);
})
.Build();
// 注册消息处理器
connection.On<string, string>("ReceiveMessage", (user, message) => {
Console.WriteLine($"{user}: {message}");
});
// 连接管理
async Task StartConnectionAsync()
{
try
{
await connection.StartAsync();
}
catch (Exception ex)
{
Console.WriteLine($"连接失败: {ex.Message}");
await Task.Delay(1000);
await StartConnectionAsync();
}
}
// 调用服务端方法
await connection.InvokeAsync("SendMessage", "user1", "Hello from .NET client");
四、高级功能实现
4.1 用户身份集成
// 认证Hub
[Authorize]
public class AuthHub : Hub
{
public override async Task OnConnectedAsync()
{
var userId = Context.User?.FindFirstValue(ClaimTypes.NameIdentifier);
await Groups.AddToGroupAsync(Context.ConnectionId, $"user-{userId}");
await base.OnConnectedAsync();
}
public async Task SendPrivateMessage(string targetUserId, string message)
{
var sender = Context.User?.Identity?.Name;
await Clients.Group($"user-{targetUserId}")
.SendAsync("ReceivePrivateMessage", sender, message);
}
}
// 带认证的JavaScript客户端
const connection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/auth", {
accessTokenFactory: () => {
return getAuthToken(); // 返回当前用户的JWT
}
})
.build();
4.2 横向扩展方案
// 使用Redis作为背板
builder.Services.AddSignalR()
.AddStackExchangeRedis("localhost:6379", options => {
options.Configuration.ChannelPrefix = "MyApp_";
});
// Azure SignalR服务
builder.Services.AddSignalR()
.AddAzureSignalR("Endpoint=https://<resource>.service.signalr.net;AccessKey=<access-key>;");
4.3 流式传输
// 服务端流方法
public async IAsyncEnumerable<int> CounterStream(int count)
{
for (var i = 0; i < count; i++)
{
yield return i;
await Task.Delay(1000);
}
}
// 客户端调用
const streamResult = connection.stream("CounterStream", 10);
streamResult.subscribe({
next: (item) => console.log(item),
complete: () => console.log("Stream completed"),
error: (err) => console.error(err)
});
五、性能优化策略
5.1 消息压缩配置
services.AddSignalR()
.AddMessagePackProtocol(options => {
options.FormatterResolvers = new List<MessagePack.IFormatterResolver>()
{
MessagePack.Resolvers.StandardResolver.Instance
};
});
5.2 连接过滤
public class CustomFilter : IHubFilter
{
public async ValueTask<object?> InvokeMethodAsync(
HubInvocationContext invocationContext,
Func<HubInvocationContext, ValueTask<object?>> next)
{
// 前置处理
if (invocationContext.HubMethodName == "SendMessage")
{
var message = invocationContext.HubMethodArguments[1] as string;
if (string.IsNullOrEmpty(message))
throw new HubException("消息不能为空");
}
var result = await next(invocationContext);
// 后置处理
Console.WriteLine($"方法 {invocationContext.HubMethodName} 被调用");
return result;
}
}
// 注册全局过滤器
services.AddSignalR(options => {
options.AddFilter<CustomFilter>();
});
六、安全防护机制
6.1 跨域策略
// 配置CORS
builder.Services.AddCors(options => {
options.AddPolicy("SignalRPolicy", builder => {
builder.WithOrigins("https://example.com")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
app.UseCors("SignalRPolicy");
6.2 防伪造保护
services.AddSignalR(options => {
options.HandshakeTimeout = TimeSpan.FromSeconds(15);
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
});
// 连接令牌验证
public class TokenValidationHub : Hub
{
public override async Task OnConnectedAsync()
{
var token = Context.GetHttpContext()?.Request.Query["access_token"];
if (!ValidateToken(token))
{
Context.Abort();
return;
}
await base.OnConnectedAsync();
}
}
七、监控与诊断
7.1 健康检查集成
// 添加SignalR健康检查
builder.Services.AddHealthChecks()
.AddSignalRHub("https://localhost:5001/hubs/chat", "chat-hub");
// 客户端检测连接状态
connection.onclose((error) => {
if (connection.state === signalR.HubConnectionState.Disconnected) {
attemptReconnect();
}
});
7.2 日志记录配置
// 服务端日志
builder.Services.AddSignalR()
.AddHubOptions<ChatHub>(options => {
options.EnableDetailedErrors = true;
options.ClientTimeoutInterval = TimeSpan.FromMinutes(1);
});
// 客户端日志(.NET)
var connection = new HubConnectionBuilder()
.WithUrl("...")
.ConfigureLogging(logging => {
logging.AddConsole();
logging.SetMinimumLevel(LogLevel.Trace);
})
.Build();
八、实战案例:实时协作白板
8.1 服务端实现
public class WhiteboardHub : Hub
{
private static readonly Dictionary<string, List<DrawAction>> _boards = new();
public async Task JoinBoard(string boardId)
{
await Groups.AddToGroupAsync(Context.ConnectionId, boardId);
if (_boards.TryGetValue(boardId, out var actions))
{
// 同步现有绘制动作
await Clients.Caller.SendAsync("SyncBoard", actions);
}
else
{
_boards[boardId] = new List<DrawAction>();
}
}
public async Task SendDrawAction(string boardId, DrawAction action)
{
if (_boards.ContainsKey(boardId))
{
_boards[boardId].Add(action);
await Clients.OthersInGroup(boardId).SendAsync("ReceiveDrawAction", action);
}
}
}
public record DrawAction(Point Start, Point End, string Color, int LineWidth);
8.2 客户端实现
// 连接白板Hub
const boardConnection = new signalR.HubConnectionBuilder()
.withUrl("/hubs/whiteboard")
.build();
boardConnection.on("ReceiveDrawAction", (action) => {
drawOnCanvas(action);
});
boardConnection.on("SyncBoard", (actions) => {
clearCanvas();
actions.forEach(drawOnCanvas);
});
async function joinBoard(boardId) {
await boardConnection.invoke("JoinBoard", boardId);
}
function sendDrawAction(action) {
boardConnection.invoke("SendDrawAction", currentBoardId, action)
.catch(err => console.error(err));
}
九、性能基准与优化
9.1 负载测试指标
指标 | 推荐值 | 优化方向 |
---|---|---|
连接建立时间 | <500ms | 减少握手数据 |
消息延迟 | <100ms | 使用二进制协议 |
并发连接数 | 10K+/节点 | 横向扩展 |
内存占用 | <1GB/10K连接 | 调优GC策略 |
9.2 配置调优
builder.Services.AddSignalR(options => {
// 传输控制
options.Transports = HttpTransportType.WebSockets | HttpTransportType.LongPolling;
// 消息缓冲区
options.MaximumReceiveMessageSize = 64 * 1024; // 64KB
// 流控制
options.StreamBufferCapacity = 10;
// KeepAlive设置
options.KeepAliveInterval = TimeSpan.FromSeconds(15);
options.ClientTimeoutInterval = TimeSpan.FromMinutes(2);
});
十、常见问题解决方案
10.1 连接问题排查
- WebSocket无法建立:
- 检查服务器是否支持WebSocket
- 验证代理服务器配置
- 确保CORS策略正确
- 间歇性断开连接:
// 自动重连策略
connection.onclose(async () => {
await new Promise(resolve => setTimeout(resolve, 1000));
startConnection();
});
10.2 性能问题处理
- 高延迟:
- 启用消息压缩
- 使用MessagePack替代JSON
- 就近部署SignalR服务器
- 高内存占用:
- 限制消息大小
- 优化Hub方法实现
- 增加服务器节点
10.3 生产环境建议
- 部署架构:
- 使用Azure SignalR服务或Redis背板实现横向扩展
- 为SignalR配置独立子域(如signalr.example.com)
- 监控指标:
- 活跃连接数
- 消息吞吐量
- 错误率
- 平均延迟
- 灾难恢复:
- 多区域部署
- 连接状态备份
- 优雅降级方案
通过本指南介绍的技术方案,开发者可以构建出高性能、可靠的实时通信系统。SignalR的强大功能结合.NET平台的稳定性,使其成为企业级实时应用的首选解决方案。