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)

Така че съм любопитен къде в моя код да се опитам да уловя това IO изключение... пиша ли 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, за да добавите блок за опит ... освен блок. Така или иначе ще ви трябва нов сървърен клас, за да приложите по-ранното предложение за обработка на повече от една заявка наведнъж.

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) е да се замени методът SocketServer.StreamRequestHandler finish(), нещо подобно:

...
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