关于 http 1.1 的 Keep-Alive 的持久连接 以及客户端和服务器端的处理

关于 http 1.1 的 Keep-Alive 有以下几点要注意:

  1. default 行为
  2. 可设置timeout 和 max request 数: Keep-Alive: timeout=5, max=1000

那么若是 Keep-Alive 的长连接, 如何区分一个 request payload 数据是不是已经结束呢?

server端:

以 tomcat8 为例: 在 tomcat8 的代码里面,
org.apache.coyote.http11.Http11Processor 的 prepareRequest() 方法里,
可以看到处理 HTTP header: transfer-encoding 的逻辑: 根据是 identity 或者 chunked
来使用不同的 filter 类来判断 payload 有没有结束, 所以在 servelet 的 service() 或 doPost
方法里, 拿到 inputStream 的时候, 已经不用担心 payload 结束的问题. 具体的 Filter 类在 package
org.apache.coyote.http11.filters里面, 比如 处理 input 的就有:
ChunkedInputFilter, IdentityInputFilter.

client 端

以 apache httpClient 5 为例: 因为 Client 端不像服务器端有过滤处理, 所以 client 端要自己处理,
所以在 httpClient 的代码里面, 就有专门处理 payload 是否结束的代码. 有个类:
DefaultContentLengthStrategy, 它有个方法 determineLength(final HttpMessage
message), 用来判断是使用 chunked 还是 content-length. 然后根据这个返回结果,
使用不同的解码器或者使用不同的 Stream 来封装 payload. stream 如:
org.apache.hc.core5.http.impl.io.ChunkedInputStream 和
org.apache.hc.core5.http.impl.io.IdentityInputStream; 解码器如:
org.apache.hc.core5.http.impl.nio.LengthDelimitedDecoder 和
org.apache.hc.core5.http.impl.nio.ChunkDecoder

关于 http 1.1 header Transfer-Encoding 的用法

关于 Transfer-Encoding 在官方文档中有以下几个点要注意:

  1. 它是 Hop to Hop 的 header, 也就是中间代理可以改的, 并不是 end to end 的 header;
  2. 它是传输编码, 并不是 content-encoding, content-encoding 是 end to end;
  3. 可能的值有: chunked, compress, deflate, gzip, identity. 可以是多个, 用逗号隔开;
  4. 如果是 chunked, 因为预先不知道内容长度, 所以 Content-Length 是不需要的;
  5. 如果有 chunked, chunked 必须在逗号隔开的最后一个;
  6. HTTP/1.1 应用程序必须能够编码/解码 chunked;
  7. identity 代表原样传输, 没有编码, 必须永远被接受;
  8. 在 request 的 header 中的 TE, 其实是 Accepted-Transfer-Encoding 的缩写;

比较容易混淆, 官方文档有没有说明的是:
尽管 Transfer-Encoding 是一个 response header, 可是在 request 的 header 中也可以使用这个 header, 并且服务端都能处理. 参考下面这 2 处文章:

  • 在 Wiki https://en.wikipedia.org/wiki/Chunked_transfer_encoding 中的 Format 一节中, 有写到:

If a Transfer-Encoding field with a value of "chunked" is specified in
an HTTP message (either a request sent by a client or the response
from the server)

  • 在 http://www.oracle.com/technetwork/systems/chunking-155634.html 中的写到:

Chunking is most often used by the server for responses, but clients
can also chunk large requests

另外在 Tomcat8 的实现中, org.apache.coyote.http11.Http11Processor 的类的 prepareRequest() 方法中, 我们可以看到具体的对于 Transfer-Encoding 的处理方法. 其中包含对于 request 使用 chunked 处理.

使用 Curl 测试 content-length 和 payload 的数据量不一致的问题:

curl -X POST http://localhost:8080/post --header "Content-Type:application/json" --header "content-length: 2" --header "transfer-encoding:abc" --data "{"

更多内容, 参考: rfc7230, RFC2616
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding

关于 Java Socket 和 TCP 连接的相关问题

这个问答解释了 Socket 和 TCP 的 level 问题.
https://stackoverflow.com/questions/10240694/java-socket-api-how-to-tell-if-a-connection-has-been-closed

这篇文章介绍了 Socket close() 和 TCP 的 FIN, RST 的相关问题.
https://docs.oracle.com/javase/8/docs/technotes/guides/net/articles/connection_release.html

查看 JDK 源代码: SocketInputStream 和 SocketOutputStream 的 read 和 write 方法, 可以看到只有在 read 和 write 的时候才能知道这个 tcp 连接的状态.

对于 NIO 可以更快的知道这个消息.

HttpClient 库的介绍中, 有关于如何关掉 idle 和 expired 的连接问题:
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d5e373

tomcat 连接数

the crux of the solution is to avoid too much queuing of requests at multiple points (OS and tomcat threads) and fail fast (return http status 503) as soon the application’s maximum capacity is reached

refer:
https://blog.insightdatascience.com/learning-about-the-http-connection-keep-alive-header-7ebe0efa209d
https://medium.com/netflix-techblog/tuning-tomcat-for-a-high-throughput-fail-fast-system-e4d7b2fc163f
https://stackoverflow.com/questions/20763999/how-does-http-keep-alive-work