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

.NET6环境下如何使用SQL Server实现SignalR横向扩展?

SQL Server作为.NET6 SignalR横向扩展背板的实现方案

首先明确:完全可行,虽然官方没有提供适配ASP.NET Core(.NET6)的Microsoft.AspNetSignalR.SqlServer包(该包仅针对.NET Framework),但有两种可靠的实现方式:

方法一:使用社区维护的SQL Server SignalR背板包

社区已针对ASP.NET Core SignalR推出适配SQL Server的背板包,步骤简单直接:

  1. 安装NuGet包
dotnet add package AspNetCore.SignalR.SqlServer
  1. 在Program.cs中配置SignalR使用SQL Server背板
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSignalR()
    .AddSqlServer(options =>
    {
        options.ConnectionString = builder.Configuration.GetConnectionString("SignalRBackplane");
        // 可选配置:自定义表名、Schema,默认会自动创建所需表结构
        options.TableName = "SignalRMessageQueue";
        options.SchemaName = "signalr";
    });

var app = builder.Build();

app.MapHub<ChatHub>("/chathub"); // 替换成你的Hub类

app.Run();
  1. 数据库准备
    包会自动在首次运行时创建消息队列表;若需手动初始化,可查看NuGet包的内容文件获取内置SQL脚本,确保应用账户拥有数据库读写及DDL权限(自动建表时需要)。

方法二:自定义SQL Server消息总线(适合定制需求)

如果不想依赖第三方包,可自行实现IMessageBus接口,基于SQL Server实现服务器间消息传递:

  1. 创建消息存储表
CREATE TABLE dbo.SignalRMessages (
    Id INT IDENTITY(1,1) PRIMARY KEY,
    Payload NVARCHAR(MAX) NOT NULL,
    CreatedAt DATETIME2 DEFAULT GETUTCDATE() NOT NULL,
    Processed BIT DEFAULT 0 NOT NULL
)
  1. 实现IMessageBus核心逻辑
public class SqlSignalRMessageBus : IMessageBus
{
    private readonly string _connString;
    private readonly ILogger<SqlSignalRMessageBus> _logger;
    private readonly Timer _pollingTimer;

    public SqlSignalRMessageBus(IConfiguration config, ILogger<SqlSignalRMessageBus> logger)
    {
        _connString = config.GetConnectionString("SignalRBackplane");
        _logger = logger;
        // 启动轮询,读取未处理的消息
        _pollingTimer = new Timer(PollForMessages, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(500));
    }

    public Task PublishAsync(Message message, CancellationToken cancellationToken = default)
    {
        // 将消息序列化后写入数据库
        var payload = JsonSerializer.Serialize(message);
        using var conn = new SqlConnection(_connString);
        conn.Open();
        var cmd = new SqlCommand(
            "INSERT INTO SignalRMessages (Payload) VALUES (@payload)", 
            conn);
        cmd.Parameters.AddWithValue("@payload", payload);
        return cmd.ExecuteNonQueryAsync(cancellationToken);
    }

    private void PollForMessages(object state)
    {
        // 读取并处理未处理的消息,用UPDLOCK+READPAST避免并发冲突
        using var conn = new SqlConnection(_connString);
        conn.Open();
        var cmd = new SqlCommand(
            "SELECT TOP 10 Id, Payload FROM SignalRMessages WHERE Processed = 0 WITH (UPDLOCK, READPAST)", 
            conn);
        using var reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            var id = reader.GetInt32(0);
            var payload = reader.GetString(1);
            var message = JsonSerializer.Deserialize<Message>(payload);
            
            // 对接SignalR的消息分发逻辑,将消息广播给当前实例的连接
            // ...

            // 标记消息为已处理
            using var updateCmd = new SqlCommand(
                "UPDATE SignalRMessages SET Processed = 1 WHERE Id = @id", 
                conn);
            updateCmd.Parameters.AddWithValue("@id", id);
            updateCmd.ExecuteNonQuery();
        }
    }

    // 实现IMessageBus的其他接口方法,此处省略...
}
  1. 注册自定义消息总线
builder.Services.AddSignalR()
    .AddMessageBus(sp =>
    {
        var config = sp.GetRequiredService<IConfiguration>();
        var logger = sp.GetRequiredService<ILogger<SqlSignalRMessageBus>>();
        return new SqlSignalRMessageBus(config, logger);
    });

关键注意事项

  • 性能:SQL Server作为消息背板的吞吐量和延迟表现不如Redis,高并发场景优先考虑官方推荐的Redis方案。
  • 消息清理:定期清理已处理的旧消息,可通过SQL代理作业或应用定时任务实现,避免表数据膨胀。
  • 并发控制:自定义实现时需使用UPDLOCK、READPAST等锁机制,避免消息重复处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 07:25:24