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控制台上检查时,我看到客户端在每个新的发布操作中都在增加。
这是我的代码的示例。
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和
一起使用
安装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
该连接可以在处理程序中用作
1 2 3 4 5 | class BaseHandler(tornado.web.RequestHandler): @property def redis(): return self.settings['redis'] |
要与Redis通信,请使用
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) |
这可能有点棘手,但这是主要思想。