Python BaseHTTPServer, как мне поймать/уловить ошибки сломанного канала?

Я создаю механизм преобразования коротких URL-адресов в Python, и я вижу ТОННУ ошибок «сломанной трубы», и мне любопытно, как лучше всего их ловить при использовании классов BaseHTTPServer. Это не весь код, но дает вам представление о том, что я делаю до сих пор:

    from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
    import memcache

    class clientThread(BaseHTTPRequestHandler):

            def do_GET(self):
                    content = None
                    http_code,response_txt,long_url = \
                            self.ag_trans_url(self.path,content,'GET')
                    self.http_output( http_code, response_txt, long_url )
                    return

            def http_output(self,http_code,response_txt,long_url):
                    self.send_response(http_code)
                    self.send_header('Content-type','text/plain')
                    if long_url:
                            self.send_header('Location', long_url)
                    self.end_headers()
                    if response_txt:
                            self.wfile.write(response_txt)
                    return

        def ag_trans_url(self, orig_short_url, post_action, getpost):
                short_url = 'http://foo.co' + orig_short_url

                # fetch it from memcache
                long_url = mc.get(short_url)

                # other magic happens to look it up from db if there was nothing
                # in memcache, etc
                return (302, None, log_url)

def populate_memcache()
        # connect to db, do lots of mc.set() calls

def main():
        populate_memcache()
        try:
                port = 8001
                if len(sys.argv) > 1:
                        port = int(sys.argv[1])
                server = HTTPServer(('',port), clientThread)
                #server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                print '[',str(datetime.datetime.now()),'] short url processing has begun'

                server.serve_forever()
        except KeyboardInterrupt,SystemExit:
                print '^C received, shutting down server'
                server.socket.close()

Сам код работает отлично, но почти сразу начал выдавать ошибки при работе:

Traceback (most recent call last):
  File "/usr/lib/python2.5/SocketServer.py", line 222, in handle_request
    self.process_request(request, client_address)
  File "/usr/lib/python2.5/SocketServer.py", line 241, in process_request
    self.finish_request(request, client_address)
  File "/usr/lib/python2.5/SocketServer.py", line 254, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python2.5/SocketServer.py", line 522, in __init__
    self.handle()
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 316, in handle
    self.handle_one_request()
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 310, in handle_one_request
    method()
  File "/opt/short_url_redirector/shorturl.py", line 38, in do_GET
    self.http_output( http_code, response_txt, long_url )
  File "/opt/short_url_redirector/shorturl.py", line 52, in http_output
    self.send_response(http_code)
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 370, in send_response
    self.send_header('Server', self.version_string())
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 376, in send_header
    self.wfile.write("%s: %s\r\n" % (keyword, value))
  File "/usr/lib/python2.5/socket.py", line 274, in write
    self.flush()
  File "/usr/lib/python2.5/socket.py", line 261, in flush
    self._sock.sendall(buffer)
error: (32, 'Broken pipe')

Большая часть этих ошибок, по-видимому, связана с проблемой вызова метода send_header(), где все, что я пишу, это:

self.send_header('Location', long_url)

Поэтому мне любопытно, где в моем коде попытаться поймать это исключение ввода-вывода... писать ли вызовы try/except вокруг каждого из вызовов self.send_header/self.end_headers/self.wfile.write? Другая ошибка, которую я вижу время от времени, это вот эта, но я не уверен, какое исключение смотреть, чтобы поймать это:

Traceback (most recent call last):
  File "/usr/lib/python2.5/SocketServer.py", line 222, in handle_request
    self.process_request(request, client_address)
  File "/usr/lib/python2.5/SocketServer.py", line 241, in process_request
    self.finish_request(request, client_address)
  File "/usr/lib/python2.5/SocketServer.py", line 254, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "/usr/lib/python2.5/SocketServer.py", line 522, in __init__
    self.handle()
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 316, in handle
    self.handle_one_request()
  File "/usr/lib/python2.5/BaseHTTPServer.py", line 299, in handle_one_request
    self.raw_requestline = self.rfile.readline()
  File "/usr/lib/python2.5/socket.py", line 381, in readline
    data = self._sock.recv(self._rbufsize)
error: (104, 'Connection reset by peer')

person iandouglas    schedule 19.05.2011    source источник


Ответы (3)


Исключение «сломанный канал» означает, что ваш код пытался записать в сокет/канал, который был закрыт на другом конце. Если на другом конце находится веб-браузер, пользователь мог остановить запрос. Вы можете игнорировать трассировку; это не указывает на серьезную проблему. Если вы хотите подавить сообщение, вы можете попробовать блокировать весь код в вашей функции http_output, за исключением того, что вы хотите, и зарегистрировать исключение, если хотите.

Кроме того, если вы хотите, чтобы ваш HTTP-сервер обрабатывал более одного запроса одновременно, вам нужно, чтобы класс вашего сервера использовал один из классов SocketServer.ForkingMixIn и SocketServer.ThreadingMixIn. Подробности смотрите в документации модуля SocketServer.

Дополнение: Исключение «сброс соединения одноранговым узлом» означает, что ваш код пытался прочитать из мертвого сокета. Если вы хотите подавить трассировку, вам нужно будет расширить класс BaseHTTPServer и переопределить метод handle_one_request, чтобы добавить блок try...except. В любом случае вам понадобится новый класс сервера, чтобы реализовать предыдущее предложение об обработке более одного запроса за раз.

person Kushal Kumaran    schedule 21.05.2011
comment
Спасибо! Я пока не слишком беспокоюсь о многопроцессорности. Мне любопытно, какое исключение на самом деле ловить, это просто исключение IOError или что-то еще? Я также подозреваю, что ошибки Broken Pipe происходят из-за того, что эта система находится за брандмауэром HAProxy. Это мой следующий поиск. - person iandouglas; 23.05.2011
comment
Функции в модуле сокета вызывают socket.error. Начиная с Python 2.6, socket.error является подклассом IOError (документация модуля сокета). Поскольку вы нацелены на 2.5 (судя по именам файлов в вашей трассировке), вам нужно будет поймать socket.error. - person Kushal Kumaran; 26.05.2011

Похоже, это ошибка в SocketServer, см. эту ссылку Ошибка Python: 14574

Исправление (у меня работает в Python 2.7) состоит в том, чтобы переопределить метод finish() SocketServer.StreamRequestHandler, что-то вроде этого:

...
def finish(self,*args,**kw):
  try:
    if not self.wfile.closed:
      self.wfile.flush()
      self.wfile.close()
  except socket.error:
    pass
  self.rfile.close()

  #Don't call the base class finish() method as it does the above
  #return SocketServer.StreamRequestHandler.finish(self)
person Jason M    schedule 16.01.2013
comment
Спасибо за продолжение Джейсон! - person iandouglas; 16.01.2013
comment
Мое решение похоже на ваше, но в методе handle() - person Brent Washburne; 15.03.2017

В моем приложении ошибка возникла не в finish(), а в handle(). Это исправление улавливает ошибки broken pipe:

class MyHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):

    ...

    def handle(self):
        try:
            BaseHTTPServer.BaseHTTPRequestHandler.handle(self)
        except socket.error:
            pass
person Brent Washburne    schedule 15.03.2017