.NET Core 6中并行调用ASP.NET Core Web API端点的最优方案
.NET Core 6批量调用端点的高效实现方案
你的思路是对的,Task.WhenAll确实可以用来并行处理多个请求,大幅缩短总耗时,但直接发起18000个并发请求会引发资源耗尽、目标服务器过载等问题,需要结合并发控制来优化。
核心问题分析
直接创建18000个Task并执行Task.WhenAll会导致:
- 本地Socket端口快速耗尽(系统可用临时端口数量有限)
- 目标端点承受远超其处理能力的请求,触发限流、超时甚至崩溃
- 大量并发请求可能导致本地线程池过载,降低整体性能
优化实现方案
通过SemaphoreSlim控制并发请求数量,配合Task.WhenAll实现高效批量调用,同时复用HttpClient避免资源浪费。
代码示例
using System.Net.Http; using System.Threading; using System.Collections.Generic; using System.Linq; // 配置参数 const int TotalRequests = 18000; const int MaxConcurrentRequests = 100; // 根据目标服务器性能调整 var targetEndpoint = "https://your-api-endpoint.com/target"; // 复用HttpClient(ASP.NET Core中推荐通过IHttpClientFactory依赖注入获取) using var httpClient = new HttpClient(); var semaphore = new SemaphoreSlim(MaxConcurrentRequests); var requestResults = new List<RequestResult>(TotalRequests); // 生成所有请求任务 var tasks = Enumerable.Range(0, TotalRequests).Select(async requestId => { await semaphore.WaitAsync(); try { var timer = System.Diagnostics.Stopwatch.StartNew(); var response = await httpClient.GetAsync(targetEndpoint); timer.Stop(); var responseContent = await response.Content.ReadAsStringAsync(); requestResults.Add(new RequestResult { RequestId = requestId, IsSuccess = response.IsSuccessStatusCode, StatusCode = response.StatusCode, ResponseContent = responseContent, ElapsedMs = timer.ElapsedMilliseconds }); } catch (Exception ex) { requestResults.Add(new RequestResult { RequestId = requestId, IsSuccess = false, ErrorMsg = ex.Message }); } finally { semaphore.Release(); } }); // 等待所有请求完成 await Task.WhenAll(tasks); // 处理并记录结果(示例:输出到控制台,实际可写入日志/数据库) foreach (var result in requestResults) { var logMsg = result.IsSuccess ? $"请求{result.RequestId} 成功 | 耗时: {result.ElapsedMs}ms | 状态码: {result.StatusCode}" : $"请求{result.RequestId} 失败 | 错误: {result.ErrorMsg}"; Console.WriteLine(logMsg); } // 结果存储模型 public class RequestResult { public int RequestId { get; set; } public bool IsSuccess { get; set; } public System.Net.HttpStatusCode? StatusCode { get; set; } public string? ResponseContent { get; set; } public long? ElapsedMs { get; set; } public string? ErrorMsg { get; set; } }
关键注意事项
- 复用HttpClient:不要每次请求都创建新的
HttpClient,避免Socket资源泄漏,ASP.NET Core中优先使用IHttpClientFactory - 调整并发数:根据目标服务器的QPS限制和本地系统配置,测试出最优的
MaxConcurrentRequests值(比如50-200之间) - 异常处理:每个请求单独捕获异常,确保单个请求失败不会影响其他任务执行
- 结果记录:建议使用专业日志框架(如Serilog、NLog)代替控制台输出,方便后续分析请求成功率、响应耗时等数据
内容的提问来源于stack exchange,提问作者Andrew Duffy
相关产品推荐
相关产品推荐

