requests发请求时timeout配置及异常捕获

背景

今天有用户在访问web系统时,出现了Nginx返回的超时报错。经排查是由于某台服务器异常,导致web系统requests请求时,一直等待响应,等待时间超过了Nginx配置的超时时间,所以Nginx就直接返回了。

思路

  • 配置timeout

一般在使用requests库时,是不设置超时时间的,那么请求就会一直等待,直到某一方关闭。所以在发请求的时候手工指定下timeout参数。

requeststimeout配置如下:

1
2
3
4
5
6
7
8
def request(method, url, **kwargs):
"""Constructs and sends a :class:`Request <Request>`.
...
:param timeout: (optional) How many seconds to wait for the server to send data
before giving up, as a float, or a :ref:`(connect timeout, read
timeout) <timeouts>` tuple.
:type timeout: float or tuple
...

官网介绍如下:

如果你制订了一个单一的值作为timeout,如下所示:

1
r = requests.get('https://github.com', timeout=5)

这一timeout值将会用作 connectread二者的 timeout。如果要分别制定,就传入一个元组:

1
r = requests.get('https://github.com', timeout=(3.05, 27))

如果远端服务器很慢,你可以让 Request 永远等待,传入一个None作为timeout值,然后就冲咖啡去吧。

1
r = requests.get('https://github.com', timeout=None)

http://docs.python-requests.org/zh_CN/latest/user/advanced.html#timeout

  • 捕获异常

通过try... except...可以捕获异常。获取的异常信息如下:

requests_except

在老版本的python中,有e.message可以获取str格式的报错信息,但要注意的是,虽然在定义中e.messagestr类型的,但是实际上未必。类型错误的话,在后续的处理中就可能会造成其他异常了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# e.message 定义
class BaseException(object):
...
args = property(lambda self: tuple())
""":type: tuple"""

message = property(lambda self: '', lambda self, v: None, lambda self: None)
""":type: string"""
...

# 输入
try:
requests.get('http://122.248.19.3', timeout=0.001)
except requests.exceptions.ReadTimeout as e:
print(e.message)
print(type(e.message))

# 输出
HTTPConnectionPool(host='122.248.19.3', port=80): Read timed out. (read timeout=0.001)
<class 'requests.packages.urllib3.exceptions.ReadTimeoutError'>

而在新版本的python中,e.message已经被淘汰了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class BaseException(object):

"""Superclass representing the base of the exception hierarchy.

The __getitem__ method is provided for backwards-compatibility
and will be deprecated at some point. The 'message' attribute
is also deprecated.

"""

def __init__(self, *args):
self.args = args

def __str__(self):
return str(self.args[0]
if len(self.args) <= 1
else self.args)

def __repr__(self):
func_args = repr(self.args) if self.args else "()"
return self.__class__.__name__ + func_args

def __getitem__(self, index):
"""Index into arguments passed in during instantiation.

Provided for backwards-compatibility and will be
deprecated.

"""
return self.args[index]

def _get_message(self):
"""Method for 'message' property."""
warnings.warn("the 'message' attribute has been deprecated "
"since Python 2.6")
return self.args[0] if len(args) == 1 else ''

message = property(_get_message,
doc="access the 'message' attribute; "
"deprecated and provided only for "
"backwards-compatibility")

https://www.python.org/dev/peps/pep-0352/#transition-plan

所以可以用e.args来获取异常说明,e.args返回的是一个tuple,直接通过str(e.args)转换成str类型做后续处理。

结论

代码如下,对不通原因造成的异常,可以加不同的except

1
2
3
4
5
6
7
8
9
10
import requests

try:
r = requests.get('https://github.com', timeout=5)
except requests.exceptions.ConnectTimeout as e:
msg = str(e.args)
# do sth with msg
except requests.exceptions.ReadTimeout as e:
msg = str(e.args)
# do sth with msg