无法通过http.client重新正确连接

时间:2018-12-16 16:39:50

标签: python http https bots telegram-bot

我有一个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)

可以修复它,但显然不能。

我对如何自动重启我的机器人而不创建错误循环感到困惑。非常感谢您的帮助。

1 个答案:

答案 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"]