关于python:在Tornado中处理Redis连接的正确方法是什么? (异步-发布/订阅)

What is the proper way to handle Redis connection in Tornado ? (Async - Pub/Sub)

我将Redis和Tornado应用程序与asyc客户端Brukva一起使用,当我查看Brukva站点上的示例应用程序时,它们正在websocket的" init "方法上建立新连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MessagesCatcher(tornado.websocket.WebSocketHandler):
    def __init__(self, *args, **kwargs):
        super(MessagesCatcher, self).__init__(*args, **kwargs)
        self.client = brukva.Client()
        self.client.connect()
        self.client.subscribe('test_channel')

    def open(self):
        self.client.listen(self.on_message)

    def on_message(self, result):
        self.write_message(str(result.body))

    def close(self):
        self.client.unsubscribe('test_channel')
        self.client.disconnect()

在websocket的情况下

很好,但是如何在常见的Tornado RequestHandler post方法中处理它说了长轮询操作(发布-订阅模型)。我在更新处理程序的每个post方法中都建立新的客户端连接,这是正确的方法吗?当我在redis控制台上检查时,我看到客户端在每个新的发布操作中都在增加。

enter image description here

这是我的代码的示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
c = brukva.Client(host = '127.0.0.1')
c.connect()

class MessageNewHandler(BaseHandler):
    @tornado.web.authenticated
    def post(self):

        self.listing_id = self.get_argument("listing_id")
        message = {
           "id": str(uuid.uuid4()),
           "from": str(self.get_secure_cookie("username")),
           "body": str(self.get_argument("body")),
        }
        message["html"] = self.render_string("message.html", message=message)

        if self.get_argument("next", None):
            self.redirect(self.get_argument("next"))
        else:
            c.publish(self.listing_id, message)
            logging.info("Writing message :" + json.dumps(message))
            self.write(json.dumps(message))

    class MessageUpdatesHandler(BaseHandler):
        @tornado.web.authenticated
        @tornado.web.asynchronous
        def post(self):
            self.listing_id = self.get_argument("listing_id", None)
            self.client = brukva.Client()
            self.client.connect()
            self.client.subscribe(self.listing_id)
            self.client.listen(self.on_new_messages)

        def on_new_messages(self, messages):
            # Closed client connection
            if self.request.connection.stream.closed():
                return
            logging.info("Getting update :" + json.dumps(messages.body))
            self.finish(json.dumps(messages.body))
            self.client.unsubscribe(self.listing_id)


        def on_connection_close(self):
            # unsubscribe user from channel
            self.client.unsubscribe(self.listing_id)
            self.client.disconnect()

如果您提供类似案例的示例代码,我们将不胜感激。


有点晚了,但是,我一直在使用龙卷风-redis。它与龙卷风的ioloop和tornado.gen模块

一起使用

安装tornadoredis

可以从pip

安装

1
pip install tornadoredis

或使用setuptools

1
easy_install tornadoredis

但是你真的不应该那样做。您也可以克隆存储库并提取它。然后运行

1
2
python setup.py build
python setup.py install

连接到redis

以下代码位于您的main.py或等效版本中

1
2
redis_conn = tornadoredis.Client('hostname', 'port')
redis_conn.connect()

redis.connect仅被调用一次。这是一个阻塞调用,因此应在启动主ioloop之前调用它。所有处理程序之间共享同一连接对象。

您可以将其添加到您的应用程序设置中,例如

1
2
3
4
5
settings = {
    redis = redis_conn
}
app = tornado.web.Application([('/.*', Handler),],
                              **settings)

使用tornadoredis

该连接可以在处理程序中用作self.settings['redis'],也可以将其添加为BaseHandler类的属性。您的请求处理程序会对该类进行子类化,并访问该属性。

1
2
3
4
5
class BaseHandler(tornado.web.RequestHandler):

    @property
    def redis():
        return self.settings['redis']

要与Redis通信,请使用tornado.web.asynchronoustornado.gen.engine装饰器。

1
2
3
4
5
6
7
class SomeHandler(BaseHandler):

    @tornado.web.asynchronous
    @tornado.gen.engine
    def get(self):
        foo = yield gen.Task(self.redis.get, 'foo')
        self.render('sometemplate.html', {'foo': foo}

其他信息

更多示例以及其他功能(例如连接池和管道)可以在github存储库中找到。


您应该在应用程序中合并连接。由于似乎brukva不自动支持此功能(redis-py支持此功能,但由于受到自然原因的限制,因此与龙卷风不能很好地配合使用),因此您需要编写自己的连接池。

该模式非常简单。这些方面的东西(这不是真正的操作代码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class BrukvaPool():

    __conns = {}


    def get(host, port,db):
        ''' Get a client for host, port, db '''

        key ="%s:%s:%s" % (host, port, db)

        conns = self.__conns.get(key, [])
        if conns:
            ret = conns.pop()
            return ret
        else:
           ## Init brukva client here and connect it

    def release(client):
        ''' release a client at the end of a request '''
        key ="%s:%s:%s" % (client.connection.host, client.connection.port, client.connection.db)
        self.__conns.setdefault(key, []).append(client)

这可能有点棘手,但这是主要思想。