我正在使用gevent wsgi运行一个cherrypy应用程序,并在请求程序中执行一些阻止gevent调用。如果我发出单个请求,阻塞调用会按预期成功并行地使用某些阻塞资源(管道到其他进程)。当我触发多个请求时出现问题,然后cherrypy返回抛出此异常的内部服务器错误:
[23/Mar/2012:17:50:35] Traceback (most recent call last):
File "/usr/local/lib/python2.7/site-packages/CherryPy-3.2.2-py2.7.egg/cherrypy/_cpwsgi.py", line 170, in trap
return func(*args, **kwargs)
File "/usr/local/lib/python2.7/site-packages/CherryPy-3.2.2-py2.7.egg/cherrypy/_cpwsgi.py", line 97, in __call__
return self.nextapp(environ, start_response)
File "/usr/local/lib/python2.7/site-packages/CherryPy-3.2.2-py2.7.egg/cherrypy/_cpwsgi.py", line 385, in tail
return self.response_class(environ, start_response, self.cpapp)
File "/usr/local/lib/python2.7/site-packages/CherryPy-3.2.2-py2.7.egg/cherrypy/_cpwsgi.py", line 232, in __init__
outstatus = r.output_status
AttributeError: 'Response' object has no attribute 'output_status'
我将问题缩减到核心,以下简单的应用程序每次都会重现问题:
import cherrypy
import gevent
from gevent import wsgi
class BugServer(object):
@cherrypy.expose
def index(self):
gevent.sleep(2)
return 'dummy foo'
if __name__ == "__main__":
app = cherrypy.tree.mount(BugServer(), '/')
wsgi.WSGIServer(('', 27726), app).serve_forever()
为了测试它,我使用了以下脚本同时触发三个请求:
import httplib
import threading
def make_req(host):
conn = httplib.HTTPConnection(host)
conn.request("GET", "/")
return conn.getresponse()
threads = []
for i in range(3):
t = threading.Thread(target=make_req, args=('192.168.128.7:27726',), kwargs={})
t.start()
threads.append(t)
for t in threads:
t.join()
我不确定是否需要深入了解cherrypy或gevent(wsgi)库来查找错误。将spawn = None设置为wsgi服务器将违反使用greenlet阻止请求中的资源调用的目的,并且无论如何都不起作用。
有什么建议吗?感谢。
答案 0 :(得分:4)
Cherrypy使用threading.local作为其request \ response对象(如果Python版本支持它)。可以使用gevent.monkey.patch_all修补此对象以使用gevent本地存储。只需使用:
from gevent.monkey import patch_all
patch_all()
在任何导入之前(你甚至可以使用普通的“时间”函数而不是gevent.time)。这修好了。
答案 1 :(得分:2)
CherryPy广泛使用threadlocals,因此不适用于事件循环或使用相同线程处理多个请求的其他方法。如果您的测试同步进行调用,它可能会工作一段时间,但只要您尝试多个同时发出的请求(比处理程序可以返回响应的速度快到达),您将遇到此类错误。
应该可以将cherrypy.serving
替换为专为greenlet或其他并行设计的某种上下文对象,但到目前为止,没有人花时间进行实验。