我有一个实时webapp,客户端从多个Redis PubSub频道接收更新,使用gevent-socketio和redis-py构建。
我正在查看views.py, l.26示例中的django-tictactoe。 当客户端发送频道的订阅消息时,它会生成一个新的greenlet。然后,greenlet订阅Redis PubSub频道并阻止,直到收到消息。
class GameNamespace(BaseNamespace):
def listener(self, chan):
red = redis.StrictRedis(REDIS_HOST)
red = red.pubsub()
red.subscribe(chan)
print 'subscribed on chan ', chan
while True:
for i in red.listen():
self.send({'message': i}, json=True)
def recv_message(self, message):
action, pk = message.split(':')
if action == 'subscribe':
Greenlet.spawn(self.listener, pk)
根据我的理解,如果不生成新的Greenlets或取消订阅订阅,就无法添加订阅。
您如何以有效的方式处理频繁的订阅和取消订阅?
更新:我正在构建的是一个理论上无限的2D地图上的HTML5实时MMO游戏。由于地图的大小,无法将整个地图的状态发送到浏览器。因此,地图以瓷砖分区,当玩家拖动地图时动态加载(想想谷歌地图)。
每当拖动地图时,客户端/浏览器都会订阅刚刚变为可见的磁贴的更新,并在短暂延迟后取消订阅不可见的磁贴更新。订阅的更改可能每个玩家每秒发生一次,因此频繁是相对的。
客户数量(希望)很大。从理论上讲,所有玩家都可以查看地图的相同部分,使SUBSCRIBE和UNSUBSCRIBE非常昂贵,因为它们是“O(N) where N is the number of clients already subscribed to a channel
”。在实践中,它将在全世界均匀分布,所以这应该不是问题。
但是,我的主要问题是Python Redis PubSub实现会阻止侦听。一旦我在上面的例子中调用red.listen(),我就不能再更改订阅了到达。上面的示例代码启动了一个新的Greenlet,每个订阅都有一个与Redis的新连接,这可能是一个坏主意。
答案 0 :(得分:0)
您没有说明订阅之间的分区基本原理/要求是什么,即哪些信息流过必须与其他订阅分开的订阅,因此很难明确回答。我会做一些假设...
如果您有少量或固定数量的客户端管理多个订阅,那么通过单个长期订阅多路复用许多不同事件可能会更好,Redis客户端会对最终事件使用者进行多路分解。从Redis的角度来看,这种方法可能更有效,而不是为每个消费者重复设置和拆除订阅。
这样的方案需要发布者的一些额外情报,以便他们可以为每条消息选择正确的队列,但有时可以很容易地实现,例如,通过散列客户端ID模N,其中N是发布/子队列的数量。
我希望这与您提出的问题有关...