keep-alive,gzip,truncked
3 压缩
3.1 背景
在http请求(特别是移动端),如果请求的资源比较多,则网络的开销会比较大,用户体验较差。则可以开启数据的无损压缩,节省传输的流量,提升数据的加载性能。
3.2 压缩类型
1:压缩需要客户端,服务器端同时支持。在chrome中,请求默认会加上Accept-Encoding: gzip, deflate,客户端默认开启数据压缩。而tomcat默认关闭压缩,如果开启需要增加配置。
2:在请求时,需要通过header的Accept-Encoding: gzip, deflate 来告诉服务器客户端支持的压缩类型。
3:在返回时,http server会在返回的header中添加Content-Encoding: gzip 来告诉客户端数据的压缩方式。
4:压缩类型主要包含如下几种:
gzip 说明body采用GNU zip编码
compress 说明body采用Unix的文件压缩程序
deflate 说明body是用zlib的格式压缩的
identity 说明没有对实体进行编码。
其中 gzip, compress, 以及deflate编码都是无损压缩算法,不会导致信息损失。 gzip效率最高,使用较为广泛。
3.4 优点
减少流量,帮公司节省带宽及流量,帮用户节省流量
客户端(特别是移动端),加载速度变快,提升用户体验。
3.5 缺点
服务器端需要更多的cpu资源进行计算,会降低服务器的整体吞吐量
5 粘包,拆包
5.1背景
TCP是基于stream机制,其实就是一串没有边界的数据流。 这里主要面临两个问题:1:如何定义数据的边界 2:拆包和粘包的问题。HTTP协议是基于TCP,所以也会面临前面两个问题。
1:发送端发送数据,数据先通过网卡到服务端tcp的receive buffer中。服务端的上层应用如果需要读取数据,会申请一段业务buffer,调用JDK的IO接口,IO会将tcpreceive buffer的数据拷贝到业务的buffer里面。上层业务再通过设定的反序列化协议将业务buffer转换成对象进行业务处理。
2:服务端读取数据时,先申请一段业务buffer(大小一般是1k),通过调用JDK的channel.read(buffer) IO方法,IO会将tcp buffer的数据拷贝到业务buffer里面。返回值为读取字节的个数:如果返回值大于0,说明读取到了对应大小的数据;如果是0,表示没有读到数据,数据读取完成(可能业务buffer是满的,不能往里面写数据);如果是-1,代表tcp连接被关闭(一般处理是关闭到该连接)
5.3 粘包拆包说明
说明:假如服务端连续接收了4个包。 应用申请1k的buffer空间去读取tcp数据。读取的流程如下。
1:业务先申请1k大小的业务buffer,先调用JDK IO接口,会拷贝Receive Buffer的1k数据到业务的buffer里面。
2:每个包定义有边界。通过边界定义,读取到包1和包2分别进行反序列化的处理,转换为对象供上层应用处理。(解决粘包的问题)
3:如下图:在读取到包3的时候,由于把buffer读完还没有发现边界。便将包3(剩下的10个)的数据拷贝到buffer的最前端。然后再调用JDK IO接口,tcp receive buffer拷贝数据是从业务buffer的第10个位置进行拷贝赋值。拷贝完后再读取包3的数据,直到边界(解决拆包的问题)
4:然后读取包4,发现到边界后,并且数据没有可读的,则整个流程结束。
5.4 http解决方案:
1:请求行的边界是CRLF,如果读取到CRLF,则意味着请求行的信息已经读取完成。
2:Header的边界是CRLF,如果连续读取两个CRLF,则意味着header的信息读取完成。
3:body的长度是有Content-Length 来进行确定。如果没有Content-Length ,则是chunked协议(具体参考前面的trunked协议)。
相关流程分析:
只需要在netty的pipeLine中配置HttpRequestDecoder和HttpObjectAggregator。
1:如果把解析这块理解是一个黑盒的话,则输入是ByteBuf,输出是FullHttpRequest。通过该对象便可获取到所有与http协议有关的信息。
2:HttpRequestDecoder先通过RequestLine和Header解析成HttpRequest对象,传入到HttpObjectAggregator。然后再通过body解析出httpContent对象,传入到HttpObjectAggregator。当HttpObjectAggregator发现是LastHttpContent,则代表http协议解析完成,封装FullHttpRequest。
3:对于body内容的读取涉及到Content-Length和trunked两种方式。两种方式只是在解析协议时处理的不一致,最终输出是一致的。
6.3 response的流程处理
6.3.1实现
只需要在netty的pipeLine中配置HttpResponseEncoder
6.3.2原理
1:输入是FullHttpResponse对象,输出是ByteBuf。socket再将ByteBuf数据发送到访问端。
2:对FullHttpResponse按照http协议进行序列化。判断header里面是ContentLength还是Trunked,然后body按照相应的协议进行序列化。
压缩实现:
在HttpResponseEncoder之前加上 HttpContentCompressor 。response对象先进过HttpContentCompressor 压缩后,再经过HttpResponseEncoder进行序列化。
1:压缩主要是针对body进行压缩。http1.1不支持对header的压缩。
2:压缩后body的输出是trunked,而不是Content-length的形式。
6.4.2 Gzip格式
gzip压缩后主要包含三部分:
gzip头:主要存储的是gzip的压缩方式
deflate编码:内容采用的是deflate压缩算法
gzip尾:主要是采用CRC32算法对编码内容进行校验。
参考资料: