毁三观到三观回正:简单的 echo 咋就这么慢呢?
毁三观的图:
三观回正的图:
用了这么久HTTP, 你是否了解Content-Length和Transfer-Encoding ?
由 Content-Length 导致的问题引发的一系列思考:
前段时间开发 API 网关, 使用postman调试时出现了超时的情况, 经排查确定是请求数据被处理后Content-Length与实际不一致导致的问题, 故有此文.
前言
Content-Length,HTTP消息长度,用十进制数字表示的八位字节的数目,一般情况下,很多工作都被框架完成,我们很少去关注这部分内容,但少数情况下发生了Content-Length 与实际消息长度不一致,程序可能会发生比较奇怪的异常,如:
- 无响应直到超时
- 请求被截断, 而且下一个请求解析出现错乱
什么是 Content-Length
Content-Length 是 HTTP 消息长度,用十进制数字表示的八位字节的数目,是 Headers 中常见的一个字段,Content-Length 应该是精确的,否则就会导致异常(特别地, HTTP1.0 中这个字段可有可无)。
Content-Length 首部指示出报文中实体主体的字节大小,这个大小是包含了所有内容编码的,比如,对文本文件进行了 gzip 压缩的话,Content-Length 首部指的就是压缩后的大小而不是原始大小。
Content-Length 是如何工作的
Content-Length 使用十进制的数字表示了消息的长度,服务端/客户端通过它来得知后续要读取消息的长度。
如果这个长度不正确,会发生如下情况:
- Content-Length > 实际长度
如果 Content-Length 比实际的长度大,服务端/客户端读取到消息结尾后,会等待下一个字节,自然会无响应直到超时,同样地,在响应消息中 Content-Length 超过实际长度也是一样的效果 - Content-Length < 实际长度
如果这个长度小于实际长度,首次请求的消息会被截取,比如参数为 param=piaoruiqing, Content-Length 为 10,那么这次请求的消息会被截取为:param=piao
不确定 Content-Length 的值怎么办
Content-Length 首部指示出报文中实体主体的字节大小,但如在请求处理完成前无法获取消息长度,我们就无法明确指定 Content-Length,此时应该使用 Transfer-Encoding: chunked。
什么是 Transfer-Encoding: chunked
数据以一系列分块的形式进行发送,Content-Length 首部在这种情况下不被发送,在每一个分块的开头需要添加当前分块的长度,以十六进制的形式表示,后面紧跟着 \r\n,之后是分块本身,后面也是 \r\n,终止块是一个常规的分块,不同之处在于其长度为 0。
结语
- Content-Length 如果存在且生效,必须是正确的,否则会发生异常(大于实际值会超时,小于实际值会截断并可能导致后续的数据解析混乱)
- 如果报文中包含 Transfer-Encoding: chunked 首部,那么 Content-Length 将被忽略。
----------------------------------------
要计算 Content-Length 响应头就必须缓冲所有响应体数据,因为是先发送 Content-Length 响应头,然后再发送任何的响应体数据。这是一个鸡和蛋的问题。所以对于大响应来说,开销很大。既然使用了 chunked 编码,就不应再设置 Content-Length 响应头了。HTTP 1.0 协议是不支持 chunked 编码的,所以在没有 Content-Length 响应头时以连接断开作为响应体的末尾。
尽量使用 HTTP 1.1;毕竟现在连 nginx 的 proxy 模块也支持 HTTP 1.1 了。
nginx开启gzip后无法输出content-length问题请教
------------------------
在 nginx 中,如果采用 gzip,如果是 keep alive,则必然是 chunked 模式。
Response与Transfer-Encoding:chunked、Content-Length、Content-Encoding:gzip
-----------------------------
- WEB 性能调优:gzip 与 chunked
- How to Enable & Configure Gzip Compression on Nginx
- 如何为 Nginx 配置 keep-alive 超时时间?
- nginx 的 keepalive
- nginx 与 http 学习笔记
-------------------------------------
gzip on; gzip_disable "MSIE [1-6]\."; gzip_min_length 1k; #gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json; gzip_types text/plain text/css application/x-javascript text/xml application/xml application/xml+rss text/javascript application/json 'text/plain; charset=UTF-8' 'text/css; charset=UTF-8' 'application/x-javascript; charset=UTF-8' 'text/xml; charset=UTF-8' 'application/xml; charset=UTF-8' 'application/xml+rss; charset=UTF-8' 'text/javascript; charset=UTF-8' 'application/json; charset=UTF-8' 'application/json;charset=UTF-8'; chunked_transfer_encoding off;
见证代码逻辑:gzip 开启了 + chunked 关闭了 => keep alive 失效了:
nginx 中开启 keepalive 后应答反而为 close 的原因
------------------
在 upstream 显示配置 keepalive_requests 也有利于性能提升
http://nginx.org/en/docs/http/ngx_http_upstream_module.html
----------------------------------
为什么打开 gzip、关闭 chunked 会关闭连接?
因为 Nginx 会采用即时压缩(On-The-Fly Compression),整个压缩过程在内存中流式完成。也就是说,Nginx 不会等文件 GZip 完成再返回响应,而是边压缩边响应,这样可以显著提高 TTFB(Time To First Byte,首字节时间,WEB 性能优化重要指标)。这样唯一的问题是,Nginx 开始返回响应时,它无法知道将要传输的文件最终有多大,也就是无法给出 Content-Length 这个响应头部。
对于 TCP 持久连接上的 HTTP 报文,客户端需要一种机制来准确判断结束位置,所以HTTP Server不启用持久连接(关闭连接的方式)。
所以合理的方式是打开 gzip、chunked。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论