1

我有一个 dotnet 框架应用程序,它对运行 Apache 的远程服务器执行 POST api 请求。它间歇性地失败并出现以下错误:

The underlying connection was closed: A connection that was expected to be kept alive was closed by the server. 

当通过保活 TLS 连接完成对服务器的第二次请求时,这种情况会发生,因此在负载较重的生产系统中发生的频率更高,而在开发环境中发生的频率较低或根本不发生。

我们尝试过:

  • 禁用Expect: 100-Continue标头行为 ( ServicePoint.Expect100Continue = false)
  • 启用 TCP 保持活动 ( ServicePoint.SetTcpKeepAlive())

禁用 HTTP keep-alive 似乎可以解决该问题。( HttpWebRequest.KeepAlive = false)

有没有办法在不禁用http keep-alive的情况下解决这个问题?

4

1 回答 1

0

Apache 设置KeepAliveTimeout默认为 5 秒不活动,然后将关闭空闲的保活连接。(https://httpd.apache.org/docs/2.4/mod/core.html#keepalivetimeout

这会导致以下情况:

  1. dotnet 打开与 apache 的连接并发出 POST
  2. apache 返回 200 OK。
  3. 连接处于“空闲”状态,等待另一个请求。
  4. 2s 后 dotnet 打开一个新的 HttpWebRequest 并在其上调用 GetRequestStream() 准备写入请求。由于池中有空闲连接,因此使用该连接。
  5. 5s ( KeepAliveTimeout) 后,apache 发送一个 FIN 数据包关闭底层连接。
  6. (比如说)30 秒后 dotnet 尝试写入流,该流尝试使用现已失效的套接字并立即失败The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.

这在大型 POST 调用(例如,调用 SOAP API)中尤其成问题,其中形成有效负载可能需要大量时间。

可能的解决方案是:

  1. HttpWebRequest.GetRequestStream()在开始发送数据之前不要调用。
  2. 禁用保持活动 ( HttpWebRequest.KeepAlive = false)。但是请注意,如果您的应用程序中的任何其他线程正在使用 keep-alive,那么就会出现问题(上面的两个请求可以在完全不同的线程中)
  3. 最强大的解决方案似乎是实现应用程序级重试。

请注意,这种行为(“将流锁定到套接字”)似乎只发生在 dotnet 框架中,而不是 dotnet 5/core。

于 2021-01-03T22:59:33.560 回答