文章目录

      • 1. 背景
      • 2. 原因
      • 3. 解决方式
      • 4. 知识点
        • 4.1 Fiddler
        • 4.2 Http
        • 4.3 CDN
      • 5.附录
        • 5.1 请求&回包头部信息

1. 背景

使用Okhttp的框架,在七牛服务器上进行文件下载,发现一个奇怪的问题,在进行 Http 进行下载的时候,Response 的回包里面,偶尔会出现没有 Content-Length 的情况,导致无法准确的获取下载进度。

在查找问题的过程中,较深入的了解Http下载的流程和fiddler抓包工具的使用,并在与七牛的运维确定问题后,给出了我认为较合理的解决方案。

2. 原因

造成没有 Content-Length 的原因,是由于七牛的服务器没有返回这个字段,七牛方面给出的回复如下:

这边排查当请求头带有 Accept-encoding: gzip 时,第一次请求到某个节点时,会没有 content-length 头,第二次请求到相同节点,就会带这个头了。

当 CDN 节点需要做 gzip 压缩时,为加快第一次响应速度,会在做完 gzip 压缩后直接先响应内容,再计算 content-length ,这样第二次访问到该节点的时候,会把上次的 gzip 压缩内容和 content-length 都返回。

所以原因总结要点如下:

  1. 七牛服务器默认会对文本格式的文件进行 gzip 压缩,也只有文本格式的文件会出现返回的 Response 里面没有 Content-Length 的问题。(目前与官方确定的是只有文本文件会进行压缩,并不确定是不是其他文件就真的没有)
  2. 之所以出现有时候有 Content-Length ,有时候没有,是因为数据来源是不同的 CDN 导致的。只有第二次从 CDN 取到相同数据的时候才有 Content-Length ,虽然我们访问的域名没有变,但是数据来源的 CDN 可能变了,所以导致了第二次获取的 Content-Length 还是没有。

3. 解决方式

由于是使用的七牛的存储,不可能要求对方必须返回 Content-Length ,所以我们可以通过下面这些手段来实现进度更新或者规避为空的情况:

必须使用Content-Length(不推荐)

在发送的请求头部,在发送请求时,添加头部 (“Accept-Encoding”,“identity”) 。这么处理会告诉服务器不要走压缩的逻辑,可能会造成数据传输量增大。

不使用Content-Length

由于问题出在文件大小上面,所以可以在下载之前先获取文件大小信息,而不是使用 Content-Length。

不使用Progress

如果实在不方便获取文件大小,在txt文件不太大的情况下,压缩后的问价大小没多少,所以可以不使用Progress

4. 知识点

4.1 Fiddler

本次使用 Fiddler 对手机发出去的请求进行抓包,该工具的安装及结合手机的使用教程如下:

Fiddler 安装下载及结合手机使用教程

Fiddler 使用教程及字段解释

4.2 Http

在这次事件中,对于Http在下载时的方式有了一定的了解,下载方式是单线程、非断点续传的常规下载,请求过程的头部信息的内容如下(具体头部信息参考附录5.1):

  • C->S: 建立http长连接,其中head存在头部信息 Connection: Keep-Alive
  • S->C: 根据合适的CDN以数据流的形式返回数据,其中head使用头部信息Connection: Keep-Alive来标记是长连接,回包的头部中,如果CDN上存储有这个文件,返回Content-Length,如果没有,则分片返回,其中头部没有Content-Length,但是多了Content-Encoding: gzip、Transfer-Encoding: chunked

下载的具体解析,可以参考下面这篇文章:

HTTP协议头部与Keep-Alive模式详解

4.3 CDN

CDN全称是Content Delivery Network,即内容分发网络,其功能包括:内容存储、内容分发和负载均衡等。主要作用是减少服务器负担,降低网络带宽的阻塞,提高访问的响应速度。

将一次网络请求看成是一次去购物的经历,我们去了一家分店买某件东西,如果这家店有我们的东西,我们就直接拿了回家,如果没有,我们就需要等待店员去总店(或者附近别的店)去拿,然后我们再从分店拿回去。

下面是不含CDN与含CDN的请求路径对比:


此处不深究CND的实现原理,对CDN的介绍,可以参考下面文章:

CDN学习笔记

5.附录

5.1 请求&回包头部信息

请求头部:
GET /Fu37K5kksCvSg8iQ-ln2TjlqKXpz?e=1542791189&token=BDlsCI9C9xGl-aBysVoFl7-eu9c2j5JLredsogNl:lHHPRK4Mk3o1xPjZc93rRlMaQkA= HTTP/1.1
Host: dn-kdoss.qbox.me
Connection: Keep-Alive
Accept-Encoding: gzip
User-Agent: okhttp/3.6.0


正常情况下的回包头部:

HTTP/1.1 200 OK
Server: Tengine
Content-Type: text/plain
Connection: keep-alive
Date: Wed, 21 Nov 2018 08:45:21 GMT
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Log, X-Reqid
Access-Control-Max-Age: 2592000
Cache-Control: public, max-age=31536000
Content-Disposition: inline; filename="Fu37K5kksCvSg8iQ-ln2TjlqKXpz"; filename*=utf-8' 'Fu37K5kksCvSg8iQ-ln2TjlqKXpz
Content-Transfer-Encoding: binary
Etag: "Fu37K5kksCvSg8iQ-ln2TjlqKXpz.gz"
Last-Modified: Mon, 15 Oct 2018 09:35:49 GMT
Vary: Accept-Encoding
X-Log: mc.g;jjh0EBD;mc.g;IO:25
X-M-Log: QNM:jjh1526;SRCPROXY:jjh1535;SRC:28;SRCPROXY:28;QNM3:44
X-M-Reqid: v1IAAPqytsdFF2kV
X-Private: 1
X-Qiniu-Zone: 0
X-Qnm-Cache: Miss
X-Reqid: A0oAALtgOy5VF2kV
X-Svr: IO
Via: cache40.l2st3-1[242,200-0,M], cache3.l2st3-1[269,0], cache91260[580,200-0,M], cache171260[582,0]
Age: 0
Ali-Swift-Global-Savetime: 1542790000
X-Cache: MISS TCP_MISS dirn:-2:-2
X-Swift-SaveTime: Wed, 21 Nov 2018 08:46:40 GMT
X-Swift-CacheTime: 86400
Timing-Allow-Origin: *
EagleId: 77939ca515427899997314719e
Content-Length: 1038998


异常情况下的回包头部:

HTTP/1.1 200 OK
Server: Tengine
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alive
Date: Wed, 21 Nov 2018 08:51:20 GMT
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Log, X-Reqid
Access-Control-Max-Age: 2592000
Cache-Control: public, max-age=31536000
Content-Disposition: inline; filename="Fu37K5kksCvSg8iQ-ln2TjlqKXpz"; filename*=utf-8' 'Fu37K5kksCvSg8iQ-ln2TjlqKXpz
Content-Encoding: gzip
Content-Transfer-Encoding: binary
Etag: "Fu37K5kksCvSg8iQ-ln2TjlqKXpz.gz"
Last-Modified: Mon, 15 Oct 2018 09:35:49 GMT
Vary: Accept-Encoding
X-Log: mc.g;IO:18
X-M-Log: QNM:xs451;SRCPROXY:xs1753;SRC:22;SRCPROXY:22;QNM3:36
X-M-Reqid: 9FAAAH5NGeCnF2kV
X-Private: 1
X-Qiniu-Zone: 0
X-Qnm-Cache: Miss
X-Reqid: BGkAAGptit-nF2kV
X-Svr: IO
Via: cache40.l2et2-2[113,200-0,M], cache39.l2et2-2[115,0], cache141260[233,200-0,M], cache171260[235,0]
Age: 0
Ali-Swift-Global-Savetime: 1542790357
X-Cache: MISS TCP_MISS dirn:-2:-2
X-Swift-SaveTime: Wed, 21 Nov 2018 08:52:37 GMT
X-Swift-CacheTime: 86400
Timing-Allow-Origin: *
EagleId: 77939ca515427903569903531e

更多推荐

Response 的头部没有 Content-Length