2011-05-19 15 views
12

Construyo un motor de traducción de url corto en Python, y estoy viendo una TONELADA de errores de "pipas rotas", y tengo curiosidad sobre cómo atraparlo mejor cuando uso las clases de BaseHTTPServer. Este no es el código completo, pero le da una idea de lo que estoy haciendo hasta ahora:Python BaseHTTPServer, ¿cómo atrapo/atrapo errores de "tubería rota"?

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() 

El código en sí funciona muy bien, pero comenzó a lanzar errores casi de inmediato cuando en la producción:

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') 

la mayor parte de estos errores parece que se derivan de tener un problema de llamar al método send_header(), donde todo lo que estoy escribiendo es la siguiente:

self.send_header('Location', long_url) 

Así que estoy curioso cuando en mi código para tratar de trampa para este IO excepto ion ... ¿escribo try/except calls en cada una de las llamadas self.send_header/self.end_headers/self.wfile.write? El otro error que veo de vez en cuando es éste, pero no está seguro de qué excepción de ver incluso a atrapar a este:

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') 

Respuesta

6

la "tubería rota" excepción significa que su código intentó escribir a una toma de corriente/pipa que el otro extremo ha cerrado. Si el otro extremo es un navegador web, el usuario podría haber detenido la solicitud. Puede ignorar el rastreo; no indica un problema grave. Si desea suprimir el mensaje, puede intentarlo ... excepto bloquear todo el código en su función http_output, y registrar la excepción si lo desea.

Además, si desea que su servidor HTTP procese más de una solicitud a la vez, necesita que su clase de servidor utilice una de las clases SocketServer.ForkingMixIn y SocketServer.ThreadingMixIn. Consulte la documentación del módulo SocketServer para más detalles.

Add: La excepción "connection reset by peer" significa que su código intentó leer desde un socket muerto. Si desea suprimir el rastreo, necesitará extender la clase BaseHTTPServer y anular el método handle_one_request para agregar un try ... excepto el bloque. De todos modos, necesitará una nueva clase de servidor para implementar la sugerencia anterior sobre el procesamiento de más de una solicitud a la vez.

+0

Thanks! No estoy demasiado preocupado por el procesamiento múltiple todavía. Tengo curiosidad por saber qué excepción atrapar realmente, ¿es solo la excepción IOError o es algo más? También sospecho que los errores de Broken Pipe provienen del hecho de que este sistema se encuentra detrás de un firewall HAProxy. Esa es mi próxima búsqueda. – iandouglas

+1

Las funciones en el módulo de socket aumentan socket.error. A partir de python 2.6, socket.error es una subclase de IOError (documentación del [módulo de socket] (http://docs.python.org/library/socket.html)). Dado que tiene como objetivo 2.5 (a juzgar por los nombres de archivo en su rastreo), necesitará capturar socket.error. –

9

Esto parece ser un error en SocketServer, véase este enlace Python Bug: 14574

Una solución (que funciona para mí en Python 2.7) es reemplazar el método SocketServer.StreamRequestHandler acabado(), algo como esto:

... 
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) 
+0

Gracias por el seguimiento Jason! – iandouglas

+0

Mi solución es similar a la tuya, pero en el método 'handle()' –

3

En mi aplicación, el error no ocurrió en finish(), ocurrió en handle(). Esta revisión captura los errores broken pipe:

class MyHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): 

    ... 

    def handle(self): 
     try: 
      BaseHTTPServer.BaseHTTPRequestHandler.handle(self) 
     except socket.error: 
      pass 
Cuestiones relacionadas