c# TestServer 在ASP.NET Core并发测试中的应用

来源:这里教程网 时间:2026-02-21 17:36:13 作者:

TestServer 本身不支持并发请求,必须配合 HttpClient 实例复用

ASP.NET Core 的

TestServer
是一个内存内服务器,它运行在当前进程,没有网络层开销。但它内部的
IServer
实现(
TestServer
)是单线程同步处理请求的——不是线程安全的,也不自动排队或并行化请求。直接在多线程中反复调用
server.CreateClient()
并发发请求,容易触发
ObjectDisposedException
或请求挂起。

正确做法是:创建一个

TestServer
实例,**复用同一个
HttpClient
实例**(由
TestServer.CreateClient()
返回),再通过该客户端发起并发请求。这个
HttpClient
内部会复用连接、支持 HTTP/1.1 管道化(虽默认禁用)和 HTTP/2 多路复用(取决于底层
SocketsHttpHandler
配置)。

TestServer
实例应全局生命周期(如 xUnit 的
IClassFixture
),避免重复构建中间件管道
每个测试方法里不要反复调用
CreateClient()
;若需不同配置(如带 token 的 client),可用
new HttpClient(handler) { BaseAddress = ... }
复用底层
HttpMessageHandler
并发请求数量不宜超过
ServicePointManager.DefaultConnectionLimit
(.NET 5+ 默认为
int.MaxValue
,但旧版默认 2)

并发测试时必须手动 await 所有 Task,不能只用 Task.Run + Wait()

常见错误是用

Task.Run(() => client.GetAsync("/api/values")).Wait()
混合同步阻塞和异步逻辑,极易引发死锁(尤其在 xUnit 默认无 SynchronizationContext 的上下文中反而少见,但在某些集成测试宿主里仍可能)。

真正并发要靠

Task.WhenAll
Parallel.ForEachAsync
(.NET 6+)驱动异步任务集合:

var tasks = Enumerable.Range(0, 100)
    .Select(_ => client.GetAsync("/api/values"))
    .ToArray();
await Task.WhenAll(tasks); // ✅ 正确:全部并发发起,统一等待完成
别用
Parallel.For
直接调用 async 方法——它不理解 async/await,会导致返回
void
任务丢失
若需控制并发度(比如限制同时最多 10 个请求),用
SemaphoreSlim
包裹请求逻辑,而不是依赖线程池
注意
HttpClient
的 DNS 缓存和连接复用行为:短时间高频请求下,连接不会立刻断开,实际压力更接近真实服务端负载

TestServer 并发瓶颈不在网络,而在中间件和依赖注入容器

因为

TestServer
完全绕过 Socket、TLS、Kestrel 请求队列等环节,真正的性能瓶颈往往出在你自己的代码里:

控制器中用了
Task.Run(...).Result
.Wait()
—— 会阻塞线程池线程,快速耗尽
ThreadPool.GetAvailableThreads()
注册了
Scoped
服务但未在请求范围内正确释放(比如 EF Core 的
DbContext
被意外提升为 Singleton)
使用了非线程安全的静态缓存(如
static Dictionary
)且未加锁
日志组件(如 Serilog 的
ConsoleSink
)在高并发下成为 I/O 瓶颈

验证方式:把被测服务换成空

app.Use((ctx, next) => next());
,再压测。如果此时并发吞吐突增,说明瓶颈确实在业务中间件中。

替换 TestServer 前,先确认你真需要“并发测试”而非“负载模拟”

TestServer
的定位是**功能与集成验证**,不是压测工具。它无法反映真实网络延迟、TLS 握手开销、反向代理行为、连接中断重试等场景。如果你发现:

并发 100 请求就超时,但生产环境 Kestrel 能扛 5000+ QPS 测试结果与
curl -s -w "\n%{http_code}\n" http://localhost:5000/api/test
差距巨大
想测熔断、限流、降级策略(这些通常在网关或基础设施层)

那就该换方案了:用

dotnet run
启动真实 Kestrel,再用
 bombardier
hey
Artillery
发压。或者,在 CI 中部署临时容器跑真实服务 +
HttpClient
远程调用。

TestServer 的并发测试,只适合回答一个问题:“我的 Controller 和 Service 在多个请求同时进入时,会不会状态错乱、抛异常、漏 Dispose?”——它不是性能报告生成器。

相关推荐

热文推荐