进程在urllib2套接字重置时挂起

时间:2014-11-12 18:23:47

标签: python python-2.7

我们有一个服务器程序偶尔挂在read来电 获得连接重置时urllib2套接字,如下所示:

Traceback (most recent call last):
  File "run.py", line 112, in fetch_stuff
    raw = response.read()
  File "/usr/lib/python2.7/socket.py", line 351, in read
    data = self._sock.recv(rbufsize)
  File "/usr/lib/python2.7/httplib.py", line 573, in read
    s = self.fp.read(amt)
  File "/usr/lib/python2.7/socket.py", line 380, in read
    data = self._sock.recv(left)
error: [Errno 104] Connection reset by peer

编辑:挂起我的意思是程序没有崩溃,几个小时后仍然处于活动状态,但是,在打印出一条错误消息之后,它似乎仍然停滞不前。

但是,AFAIK代码处理库句柄之外 例外正确:

for i in range(retries):
    try:
        response = urllib2.urlopen(url)
        raw = response.read() # fails here
        ...
    except urllib2.HTTPError as e:
        logging.error("HTTP Error for url=%s (code=%s, message=%s, headers=%s)" % (url, e.code, e.msg, e.hdrs))
    except Exception as e:
        logging.exception(e)
else:
    logging.error(('Connection failed after {} tries').format(retries))
    sys.exit(0)

我无法理解为什么这会导致整个过程无法继续 进展。我们现在正尝试将timeout参数设置为urlopen, 但我怀疑这会解决问题。

所以,既然我到目前为止找不到有用的链接(except maybe this answer),是否有(明显的)修复,如果我们使用另一个库,...?

另外,实际发生了什么?我知道连接已重置,但接下来会发生什么?

1 个答案:

答案 0 :(得分:3)

除非您正在使用非阻塞套接字,否则读取调用将被阻止。因此,您的进程在read()调用中被阻止。

由于某种原因,连接的另一端发送一个设置了RST标志的数据包,关闭连接。当OS检测到此事件时,recv系统调用将返回ECONNRESET,在linux / include / errno.h中定义,并对应于错误代码104.

Python使用errno模块(https://docs.python.org/2/library/errno.html#module-errno)转换错误代码并引发异常。正如预期的那样,错误代码104是errno.ECONNRESET:

>>> import errno
>>> print errno.ECONNRESET
104

然后您正在捕获该异常并调用

logging.exception(e)

打印堆栈跟踪。之后,要么继续循环,要么遵循else分支。鉴于你的输出,我不清楚会发生什么。

这很容易复制。非常简单的客户端代码:

import urllib2
import logging

r = urllib2.urlopen("http://localhost:8080")
try:
   print "Reading!" 
   r.read()
except Exception as e:
    logging.exception(e)

在服务器端,直接从命令行:

➜  ~ [1] at 22:50:53 [Wed 12] $ nc -l -p 8080

建立连接后,客户端会阻止读取呼叫。一旦检测到某些流量,tcpkill可用于终止与RST标志的连接:

~ [1] at 22:51:19 [Wed 12] $ sudo tcpkill -i lo port 8080

而且,正如预期的那样,客户端的结果是:

➜  ~ [1] at 23:12:37 [Wed 12] $ python m.py
Reading!
ERROR:root:[Errno 104] Connection reset by peer
Traceback (most recent call last):
  File "m.py", line 7, in <module>
    r.read()
  File "/usr/lib/python2.7/socket.py", line 351, in read
    data = self._sock.recv(rbufsize)
  File "/usr/lib/python2.7/httplib.py", line 561, in read
    s = self.fp.read(amt)
  File "/usr/lib/python2.7/httplib.py", line 1302, in read
    return s + self._file.read(amt - len(s))
  File "/usr/lib/python2.7/socket.py", line 380, in read
    data = self._sock.recv(left)
error: [Errno 104] Connection reset by peer

添加超时不会解决太多问题。如果在读取调用中阻止进程时重置连接(即使超时),结果将完全相同。我认为您首先应该尝试理解连接重置的原因。但是读取已经用RST标志关闭的套接字是一个你无法避免的事件,你应该处理它。