我写了一个Django网站,处理并发数据库请求和子进程调用非常好,如果我只是运行" python manage.py runserver"
这是我的模特
class MyModel:
...
def foo(self):
args = [......]
pipe = subprocess.Popen(args, stdout=subproccess.PIPE, stderr=subprocess.PIPE)
在我看来:
def call_foo(request):
my_model = MyModel()
my_model.foo()
然而,在我使用Tornado服务器包装它之后,它不再能够处理并发请求。当我点击我的网站向这个call_foo()函数发送async get请求时,似乎我的应用程序无法处理其他请求。例如,如果我打开主页url,它会一直等待并且不会显示,直到foo()中的上述子进程调用结束。
如果我不使用龙卷风,一切正常。
以下是启动龙卷风服务器的代码。有什么我做错了吗?
MAX_WAIT_SECONDS_BEFORE_SHUTDOWN = 5
def sig_handler(sig, frame):
logging.warning('Caught signal: %s', sig)
tornado.ioloop.IOLoop.instance().add_callback(force_shutdown)
def force_shutdown():
logging.info("Stopping tornado server")
server.stop()
logging.info('Will shutdown in %s seconds ...', MAX_WAIT_SECONDS_BEFORE_SHUTDOWN)
io_loop = tornado.ioloop.IOLoop.instance()
deadline = time.time() + MAX_WAIT_SECONDS_BEFORE_SHUTDOWN
def stop_loop():
now = time.time()
if now < deadline and (io_loop._callbacks or io_loop._timeouts):
io_loop.add_timeout(now + 1, stop_loop)
else:
io_loop.stop()
logging.info('Force Shutdown')
stop_loop()
def main():
parse_command_line()
logging.info("starting tornado web server")
os.environ['DJANGO_SETTINGS_MODULE'] = 'mydjango.settings'
django.setup()
wsgi_app = tornado.wsgi.WSGIContainer(django.core.handlers.wsgi.WSGIHandler())
tornado_app = tornado.web.Application([
(r'/(favicon\.ico)', tornado.web.StaticFileHandler, {'path': "static"}),
(r'/static/(.*)', tornado.web.StaticFileHandler, {'path': "static"}),
('.*', tornado.web.FallbackHandler, dict(fallback=wsgi_app)),
])
global server
server = tornado.httpserver.HTTPServer(tornado_app)
server.listen(options.port)
signal.signal(signal.SIGTERM, sig_handler)
signal.signal(signal.SIGINT, sig_handler)
tornado.ioloop.IOLoop.instance().start()
logging.info("Exit...")
if __name__ == '__main__':
main()
答案 0 :(得分:1)
您的设置没有任何问题。这是设计的。
因此, WSGI 协议(以及 Django )使用同步模型。这意味着当您的应用程序开始处理请求时,它会获得控制权并仅在请求完成时将其返回。这就是它可以立即处理单个请求的原因。要允许同时发出请求,通常会在多线程或多进程模式下启动 wsgi 应用程序。
另一方的 Tornado 服务器使用异步模型。这里的想法是拥有自己的调度程序而不是与线程和进程一起使用的OS调度程序。因此,您的代码运行一些逻辑,然后启动一些长任务(数据库调用,URL获取),设置任务完成时运行的内容并将控制权交还给调度程序。
将控制权交还给调度程序是至关重要的部分,它允许异步服务器快速工作,因为它可以在以前等待数据时开始处理新请求。
This answer解释了sync / async详细信息。它专注于客户,但我认为你可以看到这个想法。
您的代码有什么问题:Popen
无法控制IOLoop
。在你的子进程完成之前,Python什么都不做,因此无法处理其他请求,甚至不能处理Django的请求。 runserver
“在这里工作”,因为它是多线程的。因此,在完全锁定线程时,其他线程仍然可以处理请求。
出于这个原因,通常不推荐在 async 服务器下运行 WSGI 应用程序,如龙卷风。该文档声称它将可伸缩性,但您可以在自己的代码上看到问题。因此,如果您需要两个服务器(例如Tornado for socket和Django for main site),我建议在 nginx 后面运行,并使用 uwsgi 或 gunicorn 运行 Django 。或者查看django-channels
应用,而不是龙卷风。
此外,虽然它适用于测试环境,但我认为这并不是你想要实现的目标。很难建议解决方案,因为我不知道你用Popen
调用了什么,但它接缝是长时间运行的。也许你应该看看Celery项目。这是一个运行长期后台工作的软件包。
然而,回到运行子流程。在 Tornado 中,您可以使用tornado.process.Subprocess
。它是Popen
的包装器,允许它与IOLoop
一起使用。不幸的是,我不知道你是否可以在 tornado 下的 wsgi 部分使用它。我记得有一些项目,比如django futures,但似乎已经放弃了。
作为另一个快速而又脏的修复 - 您可以使用多个进程运行 Tornado 。检查this example
如何分叉服务器。但是无论如何我都不会在生产中重新使用它(fork是正常的,运行wsgi后备不是)。
总而言之,我会重写您的代码以执行以下操作之一:
tornado.processes
模块运行子进程。总的来说,我会寻求另一个部署基础架构,并且不会在龙卷风下运行Durango。