可以通过 http://code.google.com/p/urllib3/ 下载相关库和资料。
先列出使用方法:
# coding=utf8
import urllib3
import datetime
import time
import urllib
#创建连接特定主机的连接池
http_pool = urllib3.HTTPConnectionPool('ent.qq.com')
#获取开始时间
strStart = time.strftime('%X %x %Z')
for i in range(0,100,1):
print i
#组合URL字符串
url = 'http://ent.qq.com/a/20111216/%06d.htm' % i
print url
#开始同步获取内容
r = http_pool.urlopen('GET',url,redirect=False)
print r.status,r.headers,len(r.data)
#打印时间
print 'start time : ',strStart
print 'end time : ',time.strftime('%X %x %Z')
比较简单:先建立连接池http_pool,然后连续获取同一host('ent.qq.com')的URL资源。
通过wireshark抓取包:
所有http://ent.qq.com/a/20111216/******.htm对应的src port都是13136,可见端口重用了
根据urllib3的文档应该采用了keep-alive特性,并且所有repond的connection字段都是keep-alive.
那这个连接池怎么实现的呢?
def urlopen(self, method, url, body=None, headers=None, retries=3,
redirect=True, assert_same_host=True):
# 去掉很多条件判断语句
try:
# 获取连接
conn = self._get_conn()
# 组合Request
self.num_requests += 1
conn.request(method, url, body=body, headers=headers)
# 设置超时
conn.sock.settimeout(self.timeout)
httplib_response = conn.getresponse()
# ...
...
# 解析HTTPRespond
response = HTTPResponse.from_httplib(httplib_response)
# 把当前的连接放入队列,以供重用
self._put_conn(conn)
except
# 出错处理
...
# 重定向处理,这里是递归尽兴的
if (redirect and
response.status in [301, 302, 303, 307] and
'location' in response.headers): # Redirect, retry
log.info("Redirecting %s -> %s" %
(url, response.headers.get('location')))
return self.urlopen(method, response.headers.get('location'), body,
headers, retries - 1, redirect,
assert_same_host)
# 返回结果
return response通过上面简化的代码可见,首先获取连接,然后构建Request,尽兴请求,之后获取Respond。
这里需要注意的是,每次建立连接是通过调用_get_conn
建立完连接后都调用_put_conn方法放入连接池里,相关代码如下:
def _new_conn(self):
# 新建连接
return HTTPConnection(host=self.host, port=self.port)
def _get_conn(self, timeout=None):
# 从pool尝试获取连接
conn = None
try:
conn = self.pool.get(block=self.block, timeout=timeout)
# 判断连接是否已经建立了呢?
if conn and conn.sock and select([conn.sock], [], [], 0.0)[0]:
# Either data is buffered (bad), or the connection is dropped.
log.warning("Connection pool detected dropped "
"connection, resetting: %s" % self.host)
conn.close()
except Empty, e:
pass # Oh well, we'll create a new connection then
# 如果队列为空,或者队列中的连接被断开了,那么新建一个连接在同一个端口
return conn or self._new_conn()
def _put_conn(self, conn):
# 把当前连接放入队列里,当然这个对列的默认最大元素大小为1,如果超过此大小,则被丢弃
try:
self.pool.put(conn, block=False)
except Full, e:
# This should never happen if self.block == True
log.warning("HttpConnectionPool is full, discarding connection: %s"
% self.host)
通过上述POOL和普通的urllib库进行测试性能,连续获取同一个域名的不同网页,速度没有明显提升,原因可能是服务器离本地比较近,而POOL的主要优化是减少TCP握手次数和慢启动次数,没有很好的体现出来。对于性能测试方面的建议,不知有什么好的方法?
还有人提到,是否在urllib3里要提供连接池的池,即能实现访问不同网站时,自动为每个host建立一个池,即HTTPOcean :)