如何异步使用Tornado和Redis?

14 投票
4 回答
12306 浏览
提问于 2025-04-17 17:29

我正在尝试了解如何异步使用Redis和Tornado。我找到了tornado-redis这个库,但我需要的不仅仅是在代码中加一个yield

我有以下代码:

import redis
import tornado.web

class WaiterHandler(tornado.web.RequestHandler):

    @tornado.web.asynchronous
    def get(self):
        client = redis.StrictRedis(port=6279)
        pubsub = client.pubsub()
        pubsub.subscribe('test_channel')

        for item in pubsub.listen():
            if item['type'] == 'message':
                print item['channel']
                print item['data']

        self.write(item['data'])
        self.finish()


class GetHandler(tornado.web.RequestHandler):

    def get(self):
        self.write("Hello world")


application = tornado.web.Application([
    (r"/", GetHandler),
    (r"/wait", WaiterHandler),
])

if __name__ == '__main__':
    application.listen(8888)
    print 'running'
    tornado.ioloop.IOLoop.instance().start()

我需要访问/这个网址,并在/wait有请求等待时获取“Hello World”。我该怎么做呢?

4 个回答

4

对于Python版本3.3及以上,我建议你使用aioredis这个库。下面的代码我没有测试过,但应该是类似这样的:

import redis
import tornado.web
from tornado.web import RequestHandler

import aioredis
import asyncio
from aioredis.pubsub import Receiver


class WaiterHandler(tornado.web.RequestHandler):

    @tornado.web.asynchronous
    def get(self):
        client = await aioredis.create_redis((host, 6279), encoding="utf-8", loop=IOLoop.instance().asyncio_loop)

        ch = redis.channels['test_channel']
        result = None
        while await ch.wait_message():
            item = await ch.get()
            if item['type'] == 'message':
                print item['channel']
                print item['data']
                result = item['data']

        self.write(result)
        self.finish()


class GetHandler(tornado.web.RequestHandler):

    def get(self):
        self.write("Hello world")


application = tornado.web.Application([
    (r"/", GetHandler),
    (r"/wait", WaiterHandler),
])

if __name__ == '__main__':
    print 'running'
    tornado.ioloop.IOLoop.configure('tornado.platform.asyncio.AsyncIOLoop')
    server = tornado.httpserver.HTTPServer(application)
    server.bind(8888)
    # zero means creating as many processes as there are cores.
    server.start(0)
    tornado.ioloop.IOLoop.instance().start()
5

你需要使用与Tornado IOLoop兼容的Redis客户端。

现在有几个这样的客户端可供选择,比如toredisbrukva等。

这里有一个使用toredis的发布/订阅示例:https://github.com/mrjoes/toredis/blob/master/tests/test_handler.py

7

你不应该在Tornado的主线程中使用Redis的发布/订阅功能,因为这样会阻塞输入输出循环。你可以在主线程中处理来自网页客户端的长轮询,但应该为监听Redis创建一个单独的线程。然后,你可以使用 ioloop.add_callback() 和/或 threading.Queue 来与主线程进行沟通,当你收到消息时就可以通知主线程。

撰写回答