web服务Content-Length、Transfer-Encoding与flush方法

1.http中 Transfer-Encoding = chunked 与 Content-Length的区别

参考1
参考2
参考3

1.1Content-Length

根据HTTP协议的规范,在HTTP的响应报文中一般使用content-length定义body的长度,这个大小是包含了所有内容编码的。比如对文本文件进行了gzip压缩的话,Content-Length首部就是压缩后的大小,而不是原始大小。

当浏览器读取报文的时候会根据content-length来开启buffer接收数据,这个content-length必须如实准确反应HTTP body报文的长度,否则将会出现报文不完整或者pendding的情况。

现在使用content-length将会产生两个问题:

  1. 不能边读取一边渲染,导致整个画面需要在数据加载后才能呈现,速度慢不说,加载突兀,如果在数据包传输不完整,将会导致整个画面无法展示。
  2. 服务器必须计算完content-length的值才能开始传输。

1.2Transfer-Encoding = chunked分块编码

Transfer-Encoding = chunked 分块编码把HTTP报文分割成若干个大小已知的块,块之间是紧挨着发送的,这样就不需要在发送之前知道整个报文的大小了。(也意味着不需要写回Content-Length首部了)。

Transfer-Encoding = chunked 告诉浏览器这是以 chunked 编码方式来传输的报文,使得浏览器边加载内容边渲染,完全不担心HTTP数据包的大小。由于边加载边渲染,在渲染上不用等待,也不需要校验数据是否完整,在体验效果上会优于content-length,而且加载和传输的速度更快,还有小避免pending(数据等待)的问题。

1.3springboot框架如何选择Content-Length、Transfer-Encoding

要回答这个问题就要搞清楚spring boot在回复报文的时候发生那些流程,下面我们来简单回顾下从return语句到HTTP报文发生了什么(注意不是使用resp.getOutputStream().write()输出响应报文)。

1、首先API在return 一个Object交给框架,框架接收到后将会将Object序列化成Json String。
2、Json String将会扔给Servlet框架,Servlet收到Json String后将会写到Response中,然后数据流到传输层传输到客户端中。
那么Servlet将Json String写到Response中,使用content-length还是Transfer-Encoding = chunked来定义Header头呢?

经过测试发现,Spring Boot会根据具体的情况来决定使用content-length 或者 Transfer-Encoding = chunked,

如果API 函数直接return String类型的数据,那么Spring Boot默认使用 content-length 来定义 Header头,

如果return的是类型是一个Object,那么默认使用Transfer-Encoding = chunked 进行分块传输。

2.flush方法与Content-Length、Transfer-Encoding

参考1
参考2

在servlet项目中,通常我们使用resp.getOutputStream().write()输出响应报文,那么通常我们需要再finally方法中使用flush方法与close方法

2.1为什么要flush?

image.png
如上图所示,WEB服务器通过输出流向客户端响应了一个300字节的信息,但是,这时的输出流有一个1024字节的缓冲区。所以,输出流就一直等着WEB服务器继续向客户端响应信 息,当WEB服务器的响应信息把输出流中的缓冲区填满时,这时,输出流才向WEB客户端响应消息。

为了解决这种尴尬的局面,flush()方法出现了。flush()方法可以强迫输出流(或缓冲的流)发送数据,即使此时缓冲区还没有填满,以此来打破这种死锁的状态。

当我们使用输出流发送数据时,当数据不能填满输出流的缓冲区时,这时,数据就会被存储在输出流的缓冲区中。如果,我们这个时候调用关闭(close)输出流,存储在输出流的缓冲区中的数据就会丢失。所以说,关闭(close)输出流时,应先刷新(flush)换冲的输出流,话句话说就是:“迫使所有缓冲的输出数据被写出到底层输出流中”。

2.2flush方法与Content-Length、Transfer-Encoding的关系

根据上面描述,我们可以知道,通过resp.getOutputStream()可以得到OutputStream对象,接下来调用OutputStream对象的wirte方法时,先往内部buffer里头写数据,而不是直接输出到客户端。Response Header 的 Content-Length 其实就是计算了buffer的数据长度。那他什么时候输出到客户端呢?有几种情况:

  1. OutputStream对象的属性autoFlush为true,那么当buffer(默认大小是8 * 1024)的数据满了,Tomcat会自动向客户端flush一次数据,之后buffer就被重置了。必然Content-Length就拿不到了。所以这个时候Repsonse Header就成了Tansfer-Encoding:chunked。

  2. OutputStream对象的属性autoFlush为false,如果数据超出了buffer的容量,这个时候会抛出异常IOException。

  3. 如果数据在buffer的容量范围之内,那么Content-Length可以被计算,头信息就会带上Content-Length。

  4. 如果手动调用了OutputStream对象的flush(),那么buffer中的数据立即会被输出到客户端,这个时候响应数据其实还未传输完毕,所以这种传输也可以看做分块传输了。Repsonse Header自然是Tansfer-Encoding:chunked而不是Content-Length。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享