.NET 6 API请求发送至接收间3秒延迟的优化问询
问题描述
我正在对自身API的流程耗时进行测量,编写了一段可同时调用API n次的简单代码:
private HttpClient _httpClient { get; } = new HttpClient(); [TestCase] public async Task Perf_ExecuteAuthThreads() { await Parallel.ForEachAsync(Enumerable.Range(0, NB_THREADS).ToList(), new ParallelOptions { MaxDegreeOfParallelism = NB_THREADS }, async (item, cancellationToken) => await CreateAuthThread(item)); } private async Task CreateAuthThread(int i) { string tokenAuth = string.Empty; DateTime startDate = DateTime.Now; var stopWatchGlobal = new Stopwatch(); var stopWatchDetails = new Stopwatch(); stopWatchGlobal.Restart(); try { stopWatchDetails.Restart(); using var request = new HttpRequestMessage(HttpMethod.Post, RequestFactory.GetRoute(RequestFactory.RequestType.AUTH)); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Content = JsonContent.Create(RequestFactory.CreateRequest(RequestFactory.RequestType.AUTH)); // Authenticate Method using HttpResponseMessage response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); response.EnsureSuccessStatusCode(); stopWatchDetails.Stop(); if (response.IsSuccessStatusCode) { tokenAuth = (await response.Content.ReadFromJsonAsync<Token>())!.JwtToken; } else { string responseAsString = await response.Content.ReadAsStringAsync(); //Assert.Fail("Internal server Error : {0}", responseAsString); } } catch { /* DO NOTHING */ } stopWatchGlobal.Stop(); // Get business transaction Guid var transactionGuid = new JwtSecurityToken(jwtEncodedString: tokenAuth).Claims.FirstOrDefault(x => x.Type == Constants.JWT_CLAIMS_TRANSACTION_GUID)?.Value ?? null; // Thread Id;TransactionGuid;Begin Date;End Date;Auth Duration;Business Duration;Result Business;Total Duration; Console.WriteLine($"\"{i}\";\"{transactionGuid}\";\"{startDate.ToString("MM/dd/yyyy hh:mm:ss.fff tt")}\";\"{DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff tt")}\";\"{stopWatchDetails.ElapsedMilliseconds}\";\"{(tokenAuth != string.Empty ? true : false)}\";\"{stopWatchGlobal.ElapsedMilliseconds}\""); }
在本地计算机与服务器时钟同步的前提下,我发现API请求从发送到被API接收之间存在约3秒的延迟,但API响应到本地调用结束之间不存在该延迟。我知晓传输层会产生耗时,但3秒的时长过于高昂,且我认为这并非通信初始化的“突发时间”。我的API及测试代码均基于.NET 6,使用TLS连接(HTTPS),请求与响应均为常规JSON格式,数据量不大。请问是否有办法缩短该延迟?
解决方案建议
1. 优化HttpClient连接配置
默认HttpClient的连接池设置可能无法适配高并发场景,可通过SocketsHttpHandler调整:
var handler = new SocketsHttpHandler { MaxConnectionsPerServer = NB_THREADS, // 匹配并发数,避免连接排队 PooledConnectionLifetime = TimeSpan.FromMinutes(5), // 定期回收旧连接,避免资源泄漏 PooledConnectionIdleTimeout = TimeSpan.FromMinutes(2), EnableTlsSessionResumption = true // 启用TLS会话复用,减少重复握手耗时 }; private HttpClient _httpClient { get; } = new HttpClient(handler);
如果不需要自动重定向,可添加AllowAutoRedirect = false减少不必要的处理步骤。
2. 排查TLS握手瓶颈
3秒延迟大概率和TLS握手过程相关:
- 强制TLS版本:显式指定优先使用TLS 1.3/1.2,避免版本协商耗时:
handler.SslOptions = new SslClientAuthenticationOptions { EnabledSslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12 }; - 验证证书信任链:若使用自签名证书,默认的证书验证可能产生延迟,可临时禁用验证(仅测试用)确认:
若延迟消失,需配置正确的证书信任链。handler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true;
3. 精准定位延迟节点
当前计时逻辑无法区分网络传输与服务器处理的耗时,需补充日志:
- 在API端添加请求到达时间、响应开始时间的日志,和本地
startDate对比,确认延迟是否真的发生在网络阶段。 - 启用.NET的
System.Net.Http日志类别,查看连接建立、TLS握手的详细耗时,定位具体瓶颈。
4. 优化并行调用逻辑
- 调整并发数:如果
NB_THREADS过大,可能导致本地端口耗尽或服务器限流,建议从10、20开始逐步增加,找到最优并发值。 - 替换并行方式:I/O密集型操作可改用
Task.WhenAll,减少线程池调度开销:[TestCase] public async Task Perf_ExecuteAuthThreads() { var tasks = Enumerable.Range(0, NB_THREADS).Select(i => CreateAuthThread(i)); await Task.WhenAll(tasks); }
5. 排查网络环境问题
- DNS解析测试:用
nslookup测试API域名的解析耗时,若延迟高,可在本地hosts文件绑定域名与IP,跳过DNS解析。 - 单请求验证:先测试单个请求的耗时,确认是否仅并发场景出现延迟,排除服务器端限流或队列问题。
- 防火墙/代理检查:本地或服务器的防火墙、代理是否存在流量过滤规则,导致请求被延迟处理。
内容的提问来源于stack exchange,提问作者BaptX
相关产品推荐
相关产品推荐

