龙卷风+ aioredis:为什么我的redis通话会阻塞?

时间:2018-08-13 14:32:16

标签: python-3.x redis async-await tornado python-asyncio

我尝试在Tornado和Redis上构建具有两个API端点的简单系统:

  1. API从Redis读取值,或等待该值存在(使用BRPOPvalue = yield from redis.brpop("test")
  2. 编写此值的API(使用LPUSHredis.lpush("test", "the value"))。

因此,我希望能够以任何顺序调用这些API。确实,如果我调用2,然后调用1.,它会按预期工作,则调用1.会立即返回该值。

问题是,如果我先拨打1.然后2.,这两个请求都会阻塞,永不返回。

同时,在请求阻塞的同时,我仍然可以直接在Redis中LPUSH / BRPOP,即使是相同的密钥。同样,我可以在Tornado中调用其他处理程序。所以我想这个方块既不在Redis中也不在Tornado中,而是在我使用aioredis时?也许异步循环?但是我不明白我在哪里弄错了。有提示吗?

感谢您的帮助。

这是我的代码:

import tornado.ioloop
import tornado.web
from tornado import web, gen
from tornado.options import options, define
import aioredis
import asyncio


class WaitValueHandler(tornado.web.RequestHandler):
    @asyncio.coroutine
    def get(self):
        redis = self.application.redis
        value = yield from redis.brpop("test")
        self.write("I received a value: %s" % value)


class WriteValueHandler(tornado.web.RequestHandler):
    @asyncio.coroutine
    def get(self):
        redis = self.application.redis
        res = yield from redis.lpush("test", "here is the value")
        self.write("Ok ")


class Application(tornado.web.Application):
    def __init__(self):
        tornado.ioloop.IOLoop.configure('tornado.platform.asyncio.AsyncIOMainLoop')

        handlers = [
            (r"/get", WaitValueHandler),
            (r"/put", WriteValueHandler)
        ]

        super().__init__(handlers, debug=True)

    def init_with_loop(self, loop):
        self.redis = loop.run_until_complete(
            aioredis.create_redis(('localhost', 6379), loop=loop)
        )

if __name__ == "__main__":
    application = Application()
    application.listen(8888)

    loop = asyncio.get_event_loop()
    application.init_with_loop(loop)
    loop.run_forever()

1 个答案:

答案 0 :(得分:2)

好吧,我明白了为什么,as the doc states

  

在共享模式下的阻塞操作(例如blpop,brpop或长期运行的LUA脚本)将阻塞连接,从而可能导致整个程序故障。

     

通过将排他连接用于此类操作,可以轻松解决此阻塞问题:

redis = await aioredis.create_redis_pool(
    ('localhost', 6379),
    minsize=1,
    maxsize=1)

async def task():
   # Exclusive mode
   with await redis as r:
       await r.set('key', 'val')
asyncio.ensure_future(task())
asyncio.ensure_future(task())
# Both tasks will first acquire connection.