Monday, May 5, 2008

http处理压缩数据

前段时间在上海做代理商接入系统的时候,就考虑过http压缩的问题。 主要是这样, 所有代理商和中央奖池之间的交互数据都是xml格式的,可能一个请求数据就达到200k,在高峰时期,十一家代理商同时发送请求,网络的压力很大。 如果压缩数据,至少可以节省60%的流量,同时也加快了传输速度。不过在上海的时候是考虑在自定义的协议里面添加压缩协定,没想到其实http协议早就支持数据压缩的, 可见,理解http协议还是很重要的。
下面用一段python代码来作示例:

服务器不会为你发送压缩数据,除非你告诉服务器你可以处理压缩数据。

例 11.14. 告诉服务器你想获得压缩数据

>>> import urllib2, httplib
>>> httplib.HTTPConnection.debuglevel = 1
>>> request = urllib2.Request('http://diveintomark.org/xml/atom.xml')
>>> request.add_header('Accept-encoding', 'gzip') 1
>>> opener = urllib2.build_opener()
>>> f = opener.open(request)
connect: (diveintomark.org, 80)
send: '
GET /xml/atom.xml HTTP/1.0
Host: diveintomark.org
User-agent: Python-urllib/2.1
Accept-encoding: gzip
2
'
reply: 'HTTP/1.1 200 OK\r\n'
header: Date: Thu, 15 Apr 2004 22:24:39 GMT
header: Server: Apache/2.0.49 (Debian GNU/Linux)
header: Last-Modified: Thu, 15 Apr 2004 19:45:21 GMT
header: ETag: "e842a-3e53-55d97640"
header: Accept-Ranges: bytes
header: Vary: Accept-Encoding
header: Content-Encoding: gzip
3
header: Content-Length: 6289 4
header: Connection: close
header: Content-Type: application/atom+xml
1 这是关键:一创建了 Request 对象,就添加一个 Accept-encoding 头信息告诉服务器你能接受 gzip 压缩数据。gzip 是你使用的压缩算法的名称。理论上你可以使用其它的压缩算法,但是 gzip 是 web 服务器上使用率高达 99% 的一种。
2 这是你的头信息传越网络线路的过程。
3 这是服务器的返回信息:Content-Encoding: gzip 头信息意味着你要回得的数据已经被 gzip 压缩了。
4 Content-Length 头信息是已压缩数据的长度,并非解压缩数据的长度。一会儿你会看到,实际的解压缩数据长度为 15955,因此 gzip 压缩节省了 60% 以上的网络带宽!

例 11.15. 解压缩数据

>>> compresseddata = f.read()                              1
>>> len(compresseddata)
6289
>>> import StringIO
>>> compressedstream = StringIO.StringIO(compresseddata) 2
>>> import gzip
>>> gzipper = gzip.GzipFile(fileobj=compressedstream) 3
>>> data = gzipper.read() 4
>>> print data 5






<-- rest of feed omitted for brevity -->

>>> len(data)
15955
1 继续上面的例子,f 是一个从 URL 开启器返回的类文件对象。使用它的 read() 方法将正常地获得非压缩数据,但是因为这个数据已经被 gzip 压缩过,所以这只是获得你想要的最终数据的第一步。
2 好吧,只是先得有点儿凌乱的步骤。Python 有一个 gzip 模块,它能读取 (当然也能写入) 磁盘上的 gzip 压缩文件。但是磁盘上还没有文件,只在内存里有一个 gzip 压缩缓冲区,并且你不想仅仅为了解压缩而写出一个临时文件。那么怎么做来从内存数据 (compresseddata) 创建类文件对象呢?这需要使用 StringIO 模块。你首次看到 StringIO 模块是在上一章,但现在你会发现它的另一种用法。
3 现在你可以创建 GzipFile 的一个实例,并且告诉它其中的 “文件” 是一个类文件对象 compressedstream
4 这是做所有工作的一行:从 GzipFile 中 “读取” 将会解压缩数据。感到奇妙吗?是的,它确实解压缩了数据。gzipper 是一个类文件对象,它代表一个 gzip 压缩文件。尽管这个 “文件” 并非一个磁盘上的真实文件;但 gzipper 还是从你用 StringIO 包装了压缩数据的类文件对象中 “读取” 数据,而它仅仅是内存中的变量 compresseddata。压缩的数据来自哪呢?最初你从远程 HTTP 服务器下载它,通过从用 urllib2.build_opener 创建的类文件对象中 “读取”。令人吃惊吧,这就是所有的步骤。链条上的每一步都完全不知道上一步在造假。
5 看看吧,实际的数据 (实际为 15955 bytes)。

No comments: