Python BaseHTTPServer,如何捕获“破损管道”错误?
我在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)
所以我想知道在我的代码中哪里可以捕捉到这个输入输出异常……我是否需要在每个self.send_header/self.end_headers/self.wfile.write的调用周围写try/except语句?我偶尔还会看到另一个错误,但不确定应该关注哪个异常来捕捉这个错误:
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')
3 个回答
在我的应用程序中,错误不是发生在 finish()
这个地方,而是在 handle()
里。这个修复可以捕捉到 broken pipe
的错误:
class MyHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
...
def handle(self):
try:
BaseHTTPServer.BaseHTTPRequestHandler.handle(self)
except socket.error:
pass
这看起来是SocketServer中的一个错误,详细情况可以查看这个链接 Python Bug: 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)
“断开的管道”异常意味着你的代码试图向一个已经关闭的连接写数据。如果这个连接是一个网页浏览器,用户可能已经停止了请求。你可以忽略这个错误信息,它并不表示有什么严重的问题。如果你想不让这个信息显示出来,可以在你的 http_output 函数里的所有代码周围加上一个 try ... except 结构,如果需要的话,可以记录这个异常。
另外,如果你希望你的 HTTP 服务器能够同时处理多个请求,你需要让你的服务器类使用 SocketServer.ForkingMixIn 或 SocketServer.ThreadingMixIn 这两个类中的一个。具体的细节可以查看 SocketServer 模块的文档。
补充一下:“连接被对方重置”异常意味着你的代码试图从一个已经失效的连接读取数据。如果你想不显示错误信息,你需要扩展 BaseHTTPServer 类,并重写 handle_one_request 方法,添加一个 try ... except 结构。无论如何,你都需要一个新的服务器类,以实现之前提到的同时处理多个请求的建议。