C#批量并行发起HTTP GET请求触发Too many requests错误解决方案咨询
修复方案
核心优化逻辑
原有实现每批次100个请求全量并发触发了API服务端的限流规则,以下从控制并发度、错误重试两个维度提供可直接落地的修改方案:
1. 调整并发控制逻辑(替换原有固定批次方案)
使用SemaphoreSlim精准控制全局最大并发请求数,相比固定批次逻辑资源利用率更高,不会出现短时间请求突增的情况:
private static async Task<List<MetadataModel>> GetAssetMetadataBulk(List<string> assetHashes) { List<MetadataModel> resultsList = new(); // 最大并发数,可根据API实际限流规则调整,建议从10~20开始逐步上调到不触发限流的最大值 int maxParallelism = 20; using SemaphoreSlim semaphore = new SemaphoreSlim(maxParallelism, maxParallelism); var tasks = assetHashes.Select(async asset => { await semaphore.WaitAsync(); try { return await EndpointProcessor<MetadataModel>.LoadAddress($"assets/{asset}"); } finally { semaphore.Release(); // 可选:如果仍触发限流,可在这里加10~50ms的短延迟进一步拉平请求速率 // await Task.Delay(20); } }); resultsList.AddRange(await Task.WhenAll(tasks)); return resultsList; }
2. 新增指数退避重试逻辑
针对无Retry-After头的429错误,实现固定规则的指数退避重试,避免偶发限流直接导致任务失败:
class EndpointProcessor<T> { // 最大重试次数,建议不超过5次 private const int MaxRetryCount = 3; // 重试初始延迟(毫秒),指数退避下第二次重试延迟为2倍、第三次为4倍 private const int InitialRetryDelay = 1000; public static async Task<T> LoadAddress(string url) { for (int retry = 0; retry < MaxRetryCount; retry++) { using (HttpResponseMessage response = await ApiHelper.apiClient.GetAsync(url)) { if (response.IsSuccessStatusCode) { T result = await response.Content.ReadAsAsync<T>(); return result; } // 仅针对429限流错误触发重试 if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests) { await Task.Delay(InitialRetryDelay * (int)Math.Pow(2, retry)); continue; } // 其他业务错误直接抛出 throw new Exception(response.ReasonPhrase); } } // 重试耗尽仍失败抛出异常 throw new Exception($"请求{url}多次触发限流,已终止重试"); } }
调优说明
- 优先调整
maxParallelism参数,找到API允许的最高并发值,可在不触发限流的前提下保证最优性能 - 如果API限流规则同时限制单位时间总请求数,可开启
SemaphoreSlim释放后的短延迟配置,进一步平滑请求速率
内容的提问来源于stack exchange,提问作者ARK
相关产品推荐
相关产品推荐

