关闭urllib2连接
我正在使用urllib2从ftp和http服务器加载文件。
有些服务器每个IP只能支持一个连接。问题是,urllib2并不会立即关闭连接。看看这个示例程序。
from urllib2 import urlopen
from time import sleep
url = 'ftp://user:pass@host/big_file.ext'
def load_file(url):
f = urlopen(url)
loaded = 0
while True:
data = f.read(1024)
if data == '':
break
loaded += len(data)
f.close()
#sleep(1)
print('loaded {0}'.format(loaded))
load_file(url)
load_file(url)
这段代码从一个只支持一个连接的ftp服务器加载两个文件(这里两个文件是相同的)。这会打印出以下日志:
loaded 463675266
Traceback (most recent call last):
File "conection_test.py", line 20, in <module>
load_file(url)
File "conection_test.py", line 7, in load_file
f = urlopen(url)
File "/usr/lib/python2.6/urllib2.py", line 126, in urlopen
return _opener.open(url, data, timeout)
File "/usr/lib/python2.6/urllib2.py", line 391, in open
response = self._open(req, data)
File "/usr/lib/python2.6/urllib2.py", line 409, in _open
'_open', req)
File "/usr/lib/python2.6/urllib2.py", line 369, in _call_chain
result = func(*args)
File "/usr/lib/python2.6/urllib2.py", line 1331, in ftp_open
fw = self.connect_ftp(user, passwd, host, port, dirs, req.timeout)
File "/usr/lib/python2.6/urllib2.py", line 1352, in connect_ftp
fw = ftpwrapper(user, passwd, host, port, dirs, timeout)
File "/usr/lib/python2.6/urllib.py", line 854, in __init__
self.init()
File "/usr/lib/python2.6/urllib.py", line 860, in init
self.ftp.connect(self.host, self.port, self.timeout)
File "/usr/lib/python2.6/ftplib.py", line 134, in connect
self.welcome = self.getresp()
File "/usr/lib/python2.6/ftplib.py", line 216, in getresp
raise error_temp, resp
urllib2.URLError: <urlopen error ftp error: 421 There are too many connections from your internet address.>
所以第一个文件加载成功,而第二个文件失败了,因为第一个连接还没有关闭。
但是当我在f.close()
之后加上sleep(1)
,就不会出现这个错误:
loaded 463675266
loaded 463675266
有没有什么办法可以强制关闭连接,这样第二个下载就不会失败呢?
4 个回答
3
关于Python 2.7.1,urllib2确实会泄露一个文件描述符:https://bugs.pypy.org/issue867
3
Biggie: 我觉得这是因为连接没有被 shutdown()。
注意,close() 是释放与连接相关的资源,但不一定会立刻关闭连接。如果你想及时关闭连接,应该在 close() 之前先调用 shutdown()。
你可以在 f.close() 之前试试这样:
import socket
f.fp._sock.fp._sock.shutdown(socket.SHUT_RDWR)
(没错,如果这样有效,那就不是最好的做法,但你会知道问题出在哪里。)
4
问题的根本原因确实是文件描述符泄漏。我们发现,使用jython时,这个问题比使用cpython时更明显。
有位同事提出了这个解决方案:
fdurl = urllib2.urlopen(req,timeout=self.timeout) realsock = fdurl.fp._sock.fp._sock** # we want to close the "real" socket later req = urllib2.Request(url, header) try: fdurl = urllib2.urlopen(req,timeout=self.timeout) except urllib2.URLError,e: print "urlopen exception", e realsock.close() fdurl.close()
这个修复方法虽然不太优雅,但能解决问题,不再出现“打开的连接太多”的情况。