如何在C#(.NET7.0)中高效执行100+个API请求
批量发起POST请求的正确实现方案(.NET 7)
现有方案的问题
- 测试一:一次性创建所有请求任务并等待完成,无并发限制。瞬间发起数千请求会导致目标API过载、本地端口耗尽,最终触发超时;且每次请求新建
HttpClient,会造成大量资源浪费和连接池问题。 - 测试二:循环中逐个
await任务,本质是串行执行,完全未利用并发优势,耗时自然过长。 - 测试三:
Parallel.ForEach不兼容异步lambda,传入asynclambda会导致方法直接返回,任务后台执行但无法等待完成,无法保证所有请求执行完毕。
正确实现步骤
1. 复用HttpClient(关键优化)
每次请求新建HttpClient会耗尽套接字资源,推荐用.NET内置的IHttpClientFactory管理客户端生命周期,或使用静态HttpClient实例(注意静态实例的配置一致性)。
2. 控制并发数
即使数据库允许无限连接,目标API和本地网络也无法承受数千请求同时发起,必须限制并发量(建议从20-50开始测试,根据实际情况调整)。可使用SemaphoreSlim或.NET 6+新增的Parallel.ForEachAsync。
3. 正确序列化JSON
手动拼接JSON易出现转义错误,改用System.Text.Json自动序列化,避免手动处理字符串转义。
代码实现
修正后的Execute方法(复用HttpClient+正确序列化)
// 在依赖注入容器中注册IHttpClientFactory后注入 private readonly IHttpClientFactory _httpClientFactory; public SyntaxExecutor(IHttpClientFactory httpClientFactory) { _httpClientFactory = httpClientFactory; } public async Task<string> Execute(SyntaxCommand command) { var client = _httpClientFactory.CreateClient(); client.Timeout = TimeSpan.FromMinutes(10); // 使用System.Text.Json自动序列化,避免手动拼接错误 var requestBody = new { syntax = command.Syntax, replacementVariableId = command.VariableId, hierarchyId = command.HierarchyId }; var jsonContent = JsonContent.Create(requestBody); try { var response = await client.PostAsync("https://localhost:44346/SyntaxExecutor", jsonContent); response.EnsureSuccessStatusCode(); // 主动触发非2xx状态码的异常 var responseBody = await response.Content.ReadAsStringAsync(); return responseBody; } catch (Exception ex) { File.AppendAllText(@"D:\API-LOAD-Tests\logs-api.txt", $"{DateTime.Now}: {ex.Message}{Environment.NewLine}"); return null; } }
方案一:使用SemaphoreSlim控制并发
var syntaxCommands = GetCommands(); var semaphore = new SemaphoreSlim(initialCount: 30); // 限制同时发起30个请求 var tasks = new List<Task<string>>(); foreach (var cmd in syntaxCommands) { await semaphore.WaitAsync(); tasks.Add(Task.Run(async () => { try { return await _syntaxExecutor.Execute(cmd); } finally { semaphore.Release(); } })); } var testResult = await Task.WhenAll(tasks);
方案二:使用Parallel.ForEachAsync(.NET 6+推荐)
var syntaxCommands = GetCommands(); var options = new ParallelOptions { MaxDegreeOfParallelism = 30 // 控制并发数 }; var results = new List<string>(); await Parallel.ForEachAsync(syntaxCommands, options, async (cmd, token) => { var result = await _syntaxExecutor.Execute(cmd); lock (results) // 线程安全添加结果 { results.Add(result); } });
额外注意事项
- 并发数不要设置过高,建议从20开始测试,逐步调整到目标API能承受的最大值。
- 日志中添加时间戳,方便排查超时或错误的时间点。
- 若目标API有速率限制,需根据限制调整并发数或添加重试机制。
内容的提问来源于stack exchange,提问作者user98
相关产品推荐
相关产品推荐

