Redis pub / sub在订阅中添加额外频道

时间:2011-03-14 19:59:48

标签: python redis

是否可以向Redis连接添加其他订阅?我有一个监听线程,但似乎不受新的SUBSCRIBE命令的影响。

如果这是预期的行为,如果用户为他们的兴趣或加入聊天室添加股票代码提要,应该使用的模式是什么?

我想实现一个类似于:

的Python类
import threading
import redis

class RedisPubSub(object):
    def __init__(self):
        self._redis_pub = redis.Redis(host='localhost', port=6379, db=0)        
        self._redis_sub = redis.Redis(host='localhost', port=6379, db=0)        
        self._sub_thread = threading.Thread(target=self._listen)
        self._sub_thread.setDaemon(True)
        self._sub_thread.start()

    def publish(self, channel, message):
        self._redis_pub.publish(channel, message)

    def subscribe(self, channel):
        self._redis_sub.subscribe(channel)

    def _listen(self):
        for message in self._redis_sub.listen():
            print message

2 个答案:

答案 0 :(得分:5)

python-redis RedisConnectionPool类继承自threading.local,这会产生你所看到的“神奇”效果。

摘要:您的主线程和工作线程'self._redis_sub客户端最终使用与服务器的两个不同连接,但只有主线程的连接已发出SUBSCRIBE命令。

详细信息:由于主线程正在创建self._redis_sub,因此该客户端最终会被放置在main的线程本地存储中。接下来我假设主线程进行client.subscribe(channel)调用。现在主线程的客户端在连接1上订阅。接下来启动self._sub_thread工作线程,最终将自己的self._redis_sub属性设置为redis.Client的新实例,该实例构造一个新的连接池并建立一个与redis服务器的新连接。

此新连接尚未订阅您的频道,因此listen()会立即返回。因此,对于python-redis,您无法在线程之间传递与未完成订阅(或任何其他有状态命令)的已建立连接。

根据您计划实施应用的方式,您可能需要切换到使用其他客户端,或者提出一些其他方式将订阅状态传达给工作线程,例如:通过队列发送订阅命令。

另一个问题是python-redis使用阻塞套接字,这会阻止您的侦听线程在等待消息时执行其他工作,并且除非在收到消息后立即执行此操作,否则它无法表示希望取消订阅。 / p>

答案 1 :(得分:1)

异步方式:

Twisted framework和插件txredisapi

示例代码(订阅:

import txredisapi as redis

from twisted.application import internet
from twisted.application import service


class myProtocol(redis.SubscriberProtocol):
    def connectionMade(self):
        print "waiting for messages..."
        print "use the redis client to send messages:"
        print "$ redis-cli publish chat test"
        print "$ redis-cli publish foo.bar hello world"
        self.subscribe("chat")
        self.psubscribe("foo.*")


        reactor.callLater(10, self.unsubscribe, "chat")
        reactor.callLater(15, self.punsubscribe, "foo.*")

        # self.continueTrying = False
        # self.transport.loseConnection()

    def messageReceived(self, pattern, channel, message):
        print "pattern=%s, channel=%s message=%s" % (pattern, channel, message)

    def connectionLost(self, reason):
        print "lost connection:", reason


class myFactory(redis.SubscriberFactory):
    # SubscriberFactory is a wapper for the ReconnectingClientFactory
    maxDelay = 120
    continueTrying = True
    protocol = myProtocol


application = service.Application("subscriber")
srv = internet.TCPClient("127.0.0.1", 6379, myFactory())
srv.setServiceParent(application)

只有一个线程,没有头痛:)

当然取决于你编写什么样的应用程序。在网络案例中扭曲。