【问题标题】:No response when request takes 5 minutes or longer请求需要 5 分钟或更长时间时无响应
【发布时间】:2026-02-23 23:30:01
【问题描述】:

我遇到了一个非常奇怪的问题——当对特定 Web 服务器的请求需要 5 分钟或更长时间时,HttpClient 的 SendAsync 永远不会返回。

这是我尝试从中获取响应的示例 WebApi 控制器方法

        [HttpGet]
        [Route("api/Entity/Ping")]
        public async Task<HttpResponseMessage> Ping([FromUri] int time)
        {
            await Task.Delay(TimeSpan.FromMinutes(time));
            var bytes = Enumerable.Repeat((byte)42, 100_000_000).ToArray();

            HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
            response.Content = new ByteArrayContent(bytes);
            response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
            response.Content.Headers.ContentDisposition.FileName = "result.bin";
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");

            return response;
        }

这是发送请求的代码

            using (var client = HttpClientFactory.Create(handler))
            {
                client.Timeout = TimeSpan.FromMinutes(10);
                var url = "http://problem-server/WebApp/api/Entity/Ping?time=5";
                var request = new HttpRequestMessage
                {
                    Method = HttpMethod.Get,
                    RequestUri = new Uri(url)
                };
                var response = await client.SendAsync(
                    request,
                    HttpCompletionOption.ResponseHeadersRead,
                    default);

                var stream = await response.Content.ReadAsStreamAsync();
                if (response.IsSuccessStatusCode)
                    return stream;

                return default;
            }

如您所见,一切都非常简单,应该可以正常工作。但它不会,并且 SendAsync 调用只会永远挂起(10 分钟)。 同时当 [time] 参数小于 5 时有效。 此外,当您在浏览器中打开 URL 时,它会在处理 5 分钟后成功下载 result.bin 文件,因此该方法有效。

首先我认为这是由于死锁。 但是使用旧 WebRequest 类对同一 URL 的同步请求也会挂起

            var url = "http://problem-server/WebApp/api/Entity/Ping?time=5";
            var request = WebRequest.Create(url);
            request.Timeout = (int)TimeSpan.FromMinutes(10).TotalMilliseconds;
            var response = request.GetResponse();
            var stream = response.GetResponseStream();
            if (stream != null)
                return stream;

            return default;

接下来,我将 WebApp 文件夹复制到另一台服务器,我们称之为 ok-server。 修改了 http 客户端和 Web 请求方法中的 URL。 而且,神奇的是,一切正常 - 在 [time] 分钟后收到响应。

所以问题出在问题服务器上。 但是如何调试\调查它 - IIS 请求跟踪或日志“说”请求在 [time] 分钟后成功完成并发送了响应。

problem-server 和 ok-server 两台机器都有 IIS 8.5 和 Windows Server 2012 R2。 Web Api 使用 .NET Framework 4.5。 (我还尝试将 .NET Core 3.1 与托管在 IIS 上的 ASP.NET Core 一起用于 Web Api - 结果是一样的)

你能帮我找出这个问题的原因吗? 也许,我需要查看全局机器配置或网络设置。

我现在真的迷路了。

更新

problem_server 和 ok_server 位于不同的网段。 问题服务器 IP 是 192.168.114.100 和 ok_server IP 是 192.150.0.15。 为了诊断可能的网络错误配置,我决定从 IP 段中的机器向问题服务器发送请求。 这是从 192.168.114.125 机器执行测试客户端时的结果

我的工作站位于另一个 IP 段 - 192.135.9/24。也许在 192.150.0/24 和 192.135.9/24 段之间有一些路由器设置允许对 ok_server 的请求成功。

【问题讨论】:

    标签: c# iis asp.net-web-api asp.net-core-webapi httpclient


    【解决方案1】:

    我真的建议您不要在 API 控制器中执行五分钟的延迟。它会给你更多的悲伤而不是它的价值。例如,当 IIS 重新启动您的 AppPool 时,它将等待最多 90 秒来处理请求。在这些自主重启期间,此请求将被中止。

    问题服务器可能将 TCP KeepAlive 设置为 Microsoft 推荐的(但不是默认的)5 分钟值。由于 HttpClient 默认不实现 TCP keepalives,因此问题服务器 OS 很可能在响应发送给客户端之前断开 TCP 套接字,因为客户端无法响应问题服务器 OS 发送的 keepalive。

    您可以通过编辑 HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters\ 子项,在问题服务器的操作系统级别调整 TCP KeepAlive 设置,如 here 所述。

    或者,您可以在通过configuring the ServicePoint 发送请求之前将客户端配置为支持 TCP keepalive。如果客户端和服务器之间存在网络设备,例如状态防火墙,则高频保持活动设置可能有助于保持连接打开。

    var sp = ServicePointManager.FindServicePoint(new Uri(url));
     sp.SetTcpKeepAlive(true, 6000, 3000);
    

    【讨论】:

    • 感谢您的建议。我尝试使用 ServicePointManager 设置 KeepAlive,但没有效果。
    • 如果您在两台机器上运行数据包捕获,您是否看到两台机器上的响应流量?您可以直接在问题服务器上运行客户端代码吗?这些测试可能会提供更多见解。
    • 是的,我可以直接在问题服务器上运行客户端代码。一切正常 - 5 分钟后收到回复。我已要求系统管理员在请求执行期间捕获流量。
    • 可能有防火墙将套接字标记为陈旧,然后不允许响应流量,因为防火墙认为没有现有连接。尝试将 keepalive 设置为非常低的值,看看这是否有助于网络设备保持套接字标记为活动状态。 sp.SetTcpKeepAlive(true, 6000, 3000);
    • 哇。有效。将与系统管理员讨论此发现。谢谢