为何浏览器访问JSON REST API比控制台应用快?
哈哈,这个坑我刚入行的时候也踩过!你的WebRequest测试耗时比Chrome高,主要是这几个原因,咱们一个个来解决:
你用的DateTime.Now精度其实只有10-15ms左右,刚好和Chrome显示的耗时差不多,这就导致你的计时结果误差极大,完全没法反映真实的请求耗时。
赶紧换成Stopwatch类,它是专门用来高精度计时的,精度能到纳秒级:
var r = WebRequest.Create("https://myapi.com/stuff"); var stopwatch = Stopwatch.StartNew(); using (var response = r.GetResponse()) { // 这里必须读取完整响应内容,否则计时会不准确 using (var reader = new StreamReader(response.GetResponseStream())) { _ = reader.ReadToEnd(); } } stopwatch.Stop(); Console.WriteLine($"实际耗时:{stopwatch.ElapsedMilliseconds}ms");
注意哦,原来的代码里没读取响应内容,Chrome的计时是到完全接收响应的,你不读内容的话,WebRequest可能还没拿到完整响应就停止计时了,这也是误差的来源之一。
Chrome会自动复用HTTP长连接(Keep-Alive),第一次请求后后续请求就不用再做TCP三次握手、SSL握手了;而你的控制台程序如果是单次测试或者每次新建WebRequest,大概率每次都要重新建立连接,这部分握手时间会大幅拉高总耗时。
可以提前配置连接复用:
// 设置目标API的连接数上限,启用长连接复用 var servicePoint = ServicePointManager.FindServicePoint(new Uri("https://myapi.com/stuff")); servicePoint.ConnectionLimit = 10; servicePoint.MaxIdleTime = 30000; // 连接空闲30秒再关闭
Chrome会自动发送Accept-Encoding: gzip, deflate请求头,服务器返回压缩后的响应,传输的数据量能减少70%以上;但WebRequest默认不会处理压缩,你得手动配置:
var r = WebRequest.Create("https://myapi.com/stuff"); // 告诉服务器你接受压缩格式 r.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip, deflate"); using (var response = r.GetResponse()) { Stream responseStream = response.GetResponseStream(); // 根据响应头的编码格式解压 if (response.ContentEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) >= 0) { responseStream = new GZipStream(responseStream, CompressionMode.Decompress); } else if (response.ContentEncoding.IndexOf("deflate", StringComparison.OrdinalIgnoreCase) >= 0) { responseStream = new DeflateStream(responseStream, CompressionMode.Decompress); } // 读取完整内容 using (var reader = new StreamReader(responseStream)) { _ = reader.ReadToEnd(); } }
Chrome会缓存DNS解析结果,下次请求直接用缓存;而你的控制台程序每次运行可能都要重新解析DNS,这部分时间也会被算进总耗时里。可以设置DNS缓存超时:
// 设置DNS缓存1小时,避免重复解析 ServicePointManager.DnsRefreshTimeout = 3600000;
Chrome会自动选择最优的TLS版本(比如TLS 1.3),握手速度更快;而WebRequest默认可能用了较旧的TLS版本,导致握手时间变长。可以手动指定:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls13;
按照上面的方法调整后,你再测试看看,耗时应该就和Chrome的结果差不多啦!
内容的提问来源于stack exchange,提问作者TheCatWhisperer

