【问题标题】:HTTP Expect Continue performanceHTTP 期望继续性能
【发布时间】:2015-05-01 07:31:30
【问题描述】:

在几个 Internet 资源中,我看到了一个一般性建议,即禁用发送 Expect: 100-continue HTTP 标头,以便在客户端实际上不发送大型正文时提高性能。

但是,测试以下代码表明,发送标头会使总时间平均减少约 50 毫秒。

var hc = new HttpClient();
hc.DefaultRequestHeaders.ExpectContinue = ?;
hc.BaseAddress = new Uri("http://XXX/api/");
var r = new HttpRequestMessage(HttpMethod.Post, new Uri("YYY", UriKind.Relative))
{
    Content = new StringContent("{}", Encoding.UTF8, @"application/json")
};


var tt = hc.SendAsync(r).Result;
tt.Content.ReadAsStringAsync().Result.Dump();
hc.Dispose();

这里是请求的 WireShark 转储,Expect: 100-continue

  1 0.000000000    ss.ss.ss.176          dd.dd.dd.150         TCP      66     54515→80 [SYN] Seq=0 Win=8192 Len=0 MSS=1260 WS=4 SACK_PERM=1
  2 0.342137000    dd.dd.dd.150         ss.ss.ss.176          TCP      66     80→54515 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1380 WS=1 SACK_PERM=1
  3 0.342687000    ss.ss.ss.176          dd.dd.dd.150         TCP      54     54515→80 [ACK] Seq=1 Ack=1 Win=66780 Len=0
  4 *REF*          ss.ss.ss.176          dd.dd.dd.150         HTTP     272    POST /XXX/api/YYY HTTP/1.1 
  5 0.361158000    dd.dd.dd.150         ss.ss.ss.176          HTTP     79     HTTP/1.1 100 Continue 
  6 0.361846000    ss.ss.ss.176          dd.dd.dd.150         TCP      56     54515→80 [PSH, ACK] Seq=219 Ack=26 Win=66752 Len=2
  7 0.705497000    dd.dd.dd.150         ss.ss.ss.176          HTTP     461    HTTP/1.1 200 OK  (application/json)
  8 0.726029000    ss.ss.ss.176          dd.dd.dd.150         TCP      54     54515→80 [FIN, ACK] Seq=221 Ack=433 Win=66348 Len=0
  9 1.067923000    dd.dd.dd.150         ss.ss.ss.176          TCP      54     80→54515 [FIN, ACK] Seq=433 Ack=222 Win=65535 Len=0
 10 1.068466000    ss.ss.ss.176          dd.dd.dd.150         TCP      54     54515→80 [ACK] Seq=222 Ack=434 Win=66348 Len=0

不带header的相同请求:

 11 9.300455000    ss.ss.ss.176          dd.dd.dd.150         TCP      66     54516→80 [SYN] Seq=0 Win=8192 Len=0 MSS=1260 WS=4 SACK_PERM=1
 12 9.640626000    dd.dd.dd.150         ss.ss.ss.176          TCP      66     80→54516 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1380 WS=1 SACK_PERM=1
 13 9.641393000    ss.ss.ss.176          dd.dd.dd.150         TCP      54     54516→80 [ACK] Seq=1 Ack=1 Win=66780 Len=0
 14 *REF*          ss.ss.ss.176          dd.dd.dd.150         HTTP     250    POST /XXX/api/YYY HTTP/1.1 
 15 0.406794000    dd.dd.dd.150         ss.ss.ss.176          TCP      54     80→54516 [ACK] Seq=1 Ack=197 Win=65535 Len=0
 16 0.406963000    ss.ss.ss.176          dd.dd.dd.150         TCP      56     54516→80 [PSH, ACK] Seq=197 Ack=1 Win=66780 Len=2
 17 0.749589000    dd.dd.dd.150         ss.ss.ss.176          HTTP     461    HTTP/1.1 200 OK  (application/json)
 18 0.769053000    ss.ss.ss.176          dd.dd.dd.150         TCP      54     54516→80 [FIN, ACK] Seq=199 Ack=408 Win=66372 Len=0
 19 1.109276000    dd.dd.dd.150         ss.ss.ss.176          TCP      54     80→54516 [FIN, ACK] Seq=408 Ack=200 Win=65535 Len=0
 20 1.109742000    ss.ss.ss.176          dd.dd.dd.150         TCP      54     54516→80 [ACK] Seq=200 Ack=409 Win=66372 Len=0

IIS 7.5、IIS 8.0 收到相同的结果

问题是:

  1. 是什么让带有 Expect 标头的请求执行得更快,而理论上会发生相反的情况?
  2. POST 请求的主体是否总是包含在单独的 TCP 数据包中(我只查看了几个示例,这是真的)?这里我的意思是为什么转储中第 14 行的 TCP 数据包不包含第 16 行 TCP 数据包中发送的数据(POST 正文)?

【问题讨论】:

  • 如果没有看到捕获中的数据,很难回答第一个问题。看起来实际的 POST 数据分别包含在数据包 4 和 14 中,因为它们的大小为 250+ 字节。数据包 15 和 16 有点可疑 - 没有理由额外往返。在一般层面上,不 - HTTP 客户端通常不会将 POST 数据与元数据分离到请求中的不同 TCP 数据包中。
  • 还有一个观察:如果禁用 Nagle 算法 (ServicePointManager.UseNagleAlgorithm = false),那么提供 Expect 标头或不提供几乎没有区别。但 POST 正文仍被推送到不同的 TCP 数据包中。
  • HttpWebRequest 和 .NET Framework 也有同样令人讨厌的问题

标签: c# performance http iis http-headers


【解决方案1】:

解决了与TcpClient 相同的问题

var tcpClient = new TcpClient();

var uri = new Uri(httpAddress);

tcpClient.Connect(uri.Host, uri.Port);

string httpResponse = null;

using (NetworkStream networkStream = tcpClient.GetStream())
{
    var httpRequestBuilder = new StringBuilder();

    httpRequestBuilder.AppendLine("POST / HTTP/1.1");
    httpRequestBuilder.Append("Host: ").AppendLine(uri.Host);
    httpRequestBuilder.AppendLine("Content-Type: application/json");
    httpRequestBuilder.Append("Content-Length: ").AppendLine(postJsonBody.Length.ToString());
    httpRequestBuilder.AppendLine();
    httpRequestBuilder.AppendLine(postJsonBody);

    var httpRequest = httpRequestBuilder.ToString();
    var requestBytes = Encoding.UTF8.GetBytes(httpRequest);

    // sending request as one package
    // (without PSH)
    networkStream.Write(requestBytes, 0, requestBytes.Length);

    // reading response
    using (var sr = new StreamReader(networkStream, Encoding.UTF8))
    {
         httpResponse = sr.ReadToEnd();
    }
 }

 tcpClient.Close();

【讨论】:

    【解决方案2】:

    我遇到了同样的问题(延迟约 50 毫秒)。我猜这是HttpClient .NET Framework 实现的一个错误。

    我使用 .NET Core 2.1 进行了一些测试,我能够删除 Continue 消息而不会降低性能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-20
      • 1970-01-01
      • 1970-01-01
      • 2013-07-27
      • 2014-10-28
      • 1970-01-01
      • 2017-04-29
      相关资源
      最近更新 更多