我有一个Telegram机器人,该机器人不断向Telegram API发出更新请求。
有时我会收到这样的错误:
18-12-16 12:12:37: error: Traceback (most recent call last):
File "/home/pi/MuseBot/main.py", line 157, in <module>
main()
File "/home/pi/MuseBot/main.py", line 34, in main
updates = HANDLER.makeRequest("getUpdates", {"timeout": REQUEST_DELAY, "offset": lastOffset})
File "/home/pi/MuseBot/functions.py", line 42, in makeRequest
response = self.con.getresponse()
File "/usr/lib/python3.5/http/client.py", line 1198, in getresponse
response.begin()
File "/usr/lib/python3.5/http/client.py", line 297, in begin
version, status, reason = self._read_status()
File "/usr/lib/python3.5/http/client.py", line 258, in _read_status
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
File "/usr/lib/python3.5/socket.py", line 576, in readinto
return self._sock.recv_into(b)
File "/usr/lib/python3.5/ssl.py", line 937, in recv_into
return self.read(nbytes, buffer)
File "/usr/lib/python3.5/ssl.py", line 799, in read
return self._sslobj.read(len, buffer)
File "/usr/lib/python3.5/ssl.py", line 583, in read
v = self._sslobj.read(len, buffer)
OSError: [Errno 113] No route to host
几次获取此信息后,我为机器人实现了自动重启-我发现此错误不是我可以阻止的,它在服务器端。
但是我遇到了麻烦。
我有一个API处理程序类,使我可以更轻松地管理API请求。该脚本仅在运行脚本时实例化一次,而不是在每次重新启动后实例化。
它会像这样创建连接:
class ApiHandler:
def __init__(self):
self.con = http.client.HTTPSConnection(URL, 443)
当出现问题时,我在其上调用此函数:
def reconnect(self):
self.con.close()
self.con = http.client.HTTPSConnection(URL, 443)
然后我使用以下处理程序向此处理程序发出请求:
def makeRequest(self, cmd, data={}):
jsonData = json.dumps(data)
try:
self.con.request("POST", REQUEST_URL+cmd, jsonData, HEADERS)
except:
debug("An error occurred while carrying out the API request", 1)
response = self.con.getresponse()
decodedResponse = json.loads(response.read().decode())
if not decodedResponse["ok"]:
debug("reponse: {}".format(decodedResponse), 3)
raise ApiError(decodedResponse["error_code"])
return False
return decodedResponse["result"]
(ApiError只是从Exception构造的一个类,在处理崩溃时,我将其与其他错误区分开来。)
每当出现上述错误时,我都会自动reconnect
。但是随后的每个makeRequest
调用都会产生此错误:
18-12-16 12:13:02: notice: An error occurred while carrying out the API request
18-12-16 12:13:02: error: Traceback (most recent call last):
File "/home/pi/MuseBot/main.py", line 157, in <module>
main()
File "/home/pi/MuseBot/main.py", line 23, in main
metaData = HANDLER.makeRequest("getMe")
File "/home/pi/MuseBot/functions.py", line 42, in makeRequest
response = self.con.getresponse()
File "/usr/lib/python3.5/http/client.py", line 1194, in getresponse
response = self.response_class(self.sock, method=self._method)
File "/usr/lib/python3.5/http/client.py", line 235, in __init__
self.fp = sock.makefile("rb")
AttributeError: 'NoneType' object has no attribute 'makefile'
然后我自动重新连接并重试,但该错误再次发生,并且漫游器进入一个可怕的错误循环,未经检查就创建了一个庞大的日志文件并使我的树莓派崩溃。唯一的解决方案通常是重新启动树莓派。
我本以为在reconnect
中使用以下方法创建新连接:
self.con = http.client.HTTPSConnection(URL, 443)
可以修复它,但显然不能。
我对如何自动重启我的机器人而不创建错误循环感到困惑。非常感谢您的帮助。
答案 0 :(得分:2)
亲爱的@JThistle并没有亲自介绍这个问题,但您在makeRequest中存在一些错误,请阅读我的代码中的注释,然后编写一个示例,该示例可以完成您的期望。
您的代码:
def makeRequest(self, cmd, data={}):
jsonData = json.dumps(data)
try:
self.con.request("POST", REQUEST_URL+ cmd, jsonData, HEADERS)
except:
# this is you main bug you do not rerise the exception so you
# never actually reconnect
debug("An error occurred while carrying out the API request", 1)
# so your program follows to this step and the socket is closed when you try
# to read from it, hence the second exception
response = self.con.getresponse()
decodedResponse = json.loads(response.read().decode())
if not decodedResponse["ok"]:
debug("reponse: {}".format(decodedResponse), 3)
raise ApiError(decodedResponse["error_code"])
# this is wrong you can not reach this return, raise exception ends execution
# and goes up the stack until it will be caught or will crash the program
return False
return decodedResponse["result"]
这应该会引发异常,以便您实际上可以重新连接:
# python uses snake case not camel case, it might seem silly but pytonistas are
# easily annoyed bunch
def make_request(self, cmd, data=None):
# it is risky to use a reference type as default argument
# https://docs.python-guide.org/writing/gotchas/
data = data or {}
json_data = json.dumps(data)
try:
self.con.request("POST", REQUEST_URL + cmd, json_data, HEADERS)
except:
debug("An error occurred while carrying out the API request", 1)
raise # important
response = self.con.getresponse()
decoded_response = json.loads(response.read().decode())
# if the 'ok' is not present in the response string you will get 'KeyError'
# so use .get("ok") to get None instead
if not decoded_response.get("ok"):
debug("reponse: {}".format(decoded_response), 3)
error = decoded_response.get("error_code")
# what if the "error_code" not in the response, response could be empty ?
raise ApiError(error)
# again response could be malformed and you will get "KeyError" if
# "result" is not present
return decoded_response["result"]