在Tornado的chat demo中,它有一个这样的方法:
@tornado.web.asynchronous
def post(self):
cursor = self.get_argument("cursor", None)
global_message_buffer.wait_for_messages(self.on_new_messages,
cursor=cursor)
我对这个漫长的民意调查很新,而且我并不完全明白线程的工作原理,尽管它表明:
通过使用非阻塞网络I / O,Tornado可以扩展到数万个开放连接......
我的理论是通过制作一个简单的应用程序:
import tornado.ioloop
import tornado.web
import time
class MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self):
print("Start request")
time.sleep(4)
print("Okay done now")
self.write("Howdy howdy howdy")
self.finish()
application = tornado.web.Application([
(r'/', MainHandler),
])
如果我连续发出两个请求(即我打开了两个浏览器窗口并快速刷新),我会看到:
Start request
Start request
Okay done now
Okay done now
相反,我看到了
Start request
Okay done now
Start request
Okay done now
这让我相信它实际上是在这种情况下阻止。为什么我的代码是阻塞的,我如何获得一些代码来做我期望的呢?我在Windows 7上使用核心i7获得了相同的输出,在Linux Mint 13盒子中获得相同的输出,我认为是两个核心。
我找到了一种方法 - 如果有人能提供一种跨平台工作的方法(我不太担心性能,只是它是非阻塞的),我会接受这个答案。
答案 0 :(得分:6)
将测试应用转换为不会阻止IOLoop的形式的正确方法是这样的:
from tornado.ioloop import IOLoop
import tornado.web
from tornado import gen
import time
@gen.coroutine
def async_sleep(timeout):
""" Sleep without blocking the IOLoop. """
yield gen.Task(IOLoop.instance().add_timeout, time.time() + timeout)
class MainHandler(tornado.web.RequestHandler):
@gen.coroutine
def get(self):
print("Start request")
yield async_sleep(4)
print("Okay done now")
self.write("Howdy howdy howdy")
self.finish()
if __name__ == "__main__":
application = tornado.web.Application([
(r'/', MainHandler),
])
application.listen(8888)
IOLoop.instance().start()
区别在于将time.sleep
的调用替换为不会阻止IOLoop的调用。 Tornado旨在处理大量并发I / O而无需多个线程/子进程,但如果使用同步API,它仍会阻塞。为了使您的长轮询解决方案能够按照您的方式处理并发,您必须确保没有长时间运行的调用阻止。
答案 1 :(得分:5)
原始问题中代码的问题在于,当您调用time.sleep(4)
时,您实际上阻止了事件循环的执行4秒。并且接受的答案也没有解决问题(恕我直言)。
Tornado中的异步服务依赖于信任。 Tornado会在发生任何事情时调用您的函数,但它相信您会尽快将控制权返回给它。如果您使用time.sleep()
阻止,则此信任被破坏 - Tornado无法处理新连接。
使用多个线程只能隐藏错误;使用数千个线程运行Tornado(因此您可以同时提供1000个连接)将是非常低效的。适当的方法是运行一个只阻止Tornado内部的线程(在select
或Tornado监听事件的方式) - 不在您的代码上(确切地说:从不在您的代码上)码)。
正确的解决方案是在get(self)
之前从time.sleep()
返回(不调用self.finish()
),如下所示:
class MainHandler(tornado.web.RequestHandler):
@tornado.web.asynchronous
def get(self):
print("Starting")
您当然必须记住此请求仍处于打开状态,稍后会在其上调用write()
和finish()
。
我建议你看看chat demo。一旦删除了身份验证,就会得到一个非常好的异步长轮询服务器示例。
答案 2 :(得分:1)
自Tornado 5.0起,asyncio自动启用,因此只需将time.sleep(4)
更改为await asyncio.sleep(4)
,@tornado.web.asynchronous def get(self):
更改为async def get(self):
即可解决问题。
示例:
import tornado.ioloop
import tornado.web
import asyncio
class MainHandler(tornado.web.RequestHandler):
async def get(self):
print("Start request")
await asyncio.sleep(4)
print("Okay done now")
self.write("Howdy howdy howdy")
self.finish()
app = tornado.web.Application([
(r'/', MainHandler),
])
app.listen(8888)
tornado.ioloop.IOLoop.current().start()
输出:
Start request
Start request
Okay done now
Okay done now
来源: