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

如何对C#开发的SignalR项目执行负载测试

SignalR(C#技术栈)负载测试落地实操方案

一、压测工具选型(避坑优先)

  • 首选微软原生技术栈组合:小规模调试用Microsoft.AspNetCore.SignalR.Client写自定义压测脚本,大规模量级压测用Crank(.NET官方压测工具,原生支持SignalR全协议,单机可稳定模拟上万长连接,不会出现压测端自身资源瓶颈导致结果失真的问题)。
  • 千万不要用普通HTTP压测工具直接请求Hub地址:SignalR建立连接后会升级为WebSocket/Server-Sent Events/Long Polling协议,普通HTTP请求只能测到握手接口的性能,完全模拟不了长连接通信场景。
  • 不推荐硬凑JMeter、Locust这类通用压测工具:除非你自行完整实现SignalR握手、心跳、消息序列化/反序列化、自动重连全逻辑,否则测出来的连接数、QPS、延迟数据没有任何参考价值。

二、实操步骤

1. 压测前前置配置

先把服务端、压测端的默认限制放开,避免配置问题导致压测结果不准:

  • 服务端调整Kestrel和SignalR基础配置,参考代码:
// Program.cs 配置片段
builder.WebHost.ConfigureKestrel(options =>
{
    // 放开最大并发连接数限制,默认值远达不到高并发场景要求
    options.Limits.MaxConcurrentConnections = 10000;
    options.Limits.MaxConcurrentUpgradedConnections = 10000;
    options.Limits.MaxRequestBodySize = 10 * 1024 * 1024;
});
builder.Services.AddSignalR(options =>
{
    options.HandshakeTimeout = TimeSpan.FromSeconds(15);
    options.KeepAliveInterval = TimeSpan.FromSeconds(15);
    // 压测前暂时关闭非业务必须的Hub过滤器,排除无关逻辑干扰
})
// 生产环境用什么序列化协议,压测就配什么协议,JSON和MessagePack的性能差距能到30%以上
.AddMessagePackProtocol();
  • 压测端做系统调优:Windows环境修改注册表调高TCP动态端口范围,Linux环境执行ulimit -n 65535放开文件句柄限制,避免压测端先出现端口耗尽、连接失败的问题。
  • 提前埋好核心监控指标:不用搭复杂的监控系统,用dotnet-counters就能抓服务端的当前连接数、消息处理耗时、GC频率、CPU/内存占用、TCP重传率,压测过程中实时盯指标,不要等压测完才看结果。

2. 压测脚本编写与场景覆盖

自定义压测脚本核心逻辑参考(直接用控制台程序就能跑,不需要额外框架):

// 压测核心逻辑示例
const int baseConnectionCount = 100; // 初始连接数,每次梯度递增
var connections = new List<HubConnection>();
long successConn = 0, failConn = 0;
var latencyBag = new ConcurrentBag<long>();

// 并行建立连接
Parallel.For(0, baseConnectionCount, async i =>
{
    try
    {
        var conn = new HubConnectionBuilder()
            .WithUrl("http://你的服务地址/hub/业务Hub名", options =>
            {
                // 带和生产一致的认证信息,不要开匿名压测
                options.AccessTokenProvider = () => Task.FromResult("你的测试JWT Token");
            })
            .WithAutomaticReconnect() // 和生产客户端保持一致的重连策略
            .Build();
        
        // 注册客户端消息监听,统计端到端延迟
        conn.On<long>("ReceiveTestMessage", sendTimestamp =>
        {
            latencyBag.Add(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() - sendTimestamp);
        });

        await conn.StartAsync();
        connections.Add(conn);
        Interlocked.Increment(ref successConn);
    }
    catch
    {
        Interlocked.Increment(ref failConn);
    }
});

// 梯度加压,不要一上来打满服务端
for (int qps = 100; qps <= 5000; qps += 500)
{
    // 每个QPS档位稳定压测1分钟
    for (int second = 0; second < 60; second++)
    {
        var sendTasks = Enumerable.Range(0, qps).Select(_ =>
        {
            var randomConn = connections[Random.Shared.Next(connections.Count)];
            return randomConn.InvokeAsync("SendTestMessage", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds());
        });
        await Task.WhenAll(sendTasks);
        await Task.Delay(1000);
    }
    // 每档压测完间隔30秒,等服务端资源回稳,记录当前档位的错误率、延迟、CPU/内存数据
    await Task.Delay(30000);
}

必须覆盖3类核心场景,缺一类上线都可能出问题:

  • 连接风暴场景:短时间内并发发起大量连接请求,测试服务端握手处理能力,这是SignalR最容易出瓶颈的点,很多人只测消息吞吐不测连接建立,峰值流量进来直接大面积握手超时。
  • 长连接稳持场景:把连接数拉到生产预估峰值的1.5倍,持续压4-8小时,检查是否有内存泄漏、连接意外断开、心跳失效的问题。
  • 消息吞吐场景:分别测试单播(点对点发消息)、组播(给指定分组发消息)、广播(给所有连接发消息)三种模式的性能,三种模式的吞吐差距能到10倍以上,不能只测广播场景。

3. 压测过程注意事项

  • 压测端和服务端必须分开部署,走和生产一致的网络环境,不要用localhost回环地址压测,否则测出来的性能会比真实环境高好几倍。
  • 加压梯度要缓,每次增加连接数/QPS后稳定跑3-5分钟再看指标,方便定位性能拐点。
  • 错误率阈值卡死:只要出现连续的握手失败、消息发送超时、消息丢失,立刻停止加压,此时已经达到服务端性能瓶颈。
  • 压测过程中不要修改服务端配置、重启服务,保证测试变量唯一。

三、结果判定参考标准

  • 连接建立成功率≥99.99%,无大面积握手超时问题。
  • 消息端到端P99延迟符合业务要求,一般实时交互场景要求低于200ms。
  • 压测过程中服务端无频繁Gen2 GC,内存占用稳定无持续上涨(持续上涨说明存在内存泄漏)。
  • 压测停止后1分钟内,服务端连接数、CPU、内存回落到正常水平,无连接残留、资源不释放问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 14:45:32