如何实现Redis的发布订阅超时功能?

14 投票
2 回答
6092 浏览
提问于 2025-04-17 04:53

我想用Redis的发布订阅功能来实现一种叫做comet的技术,但发布订阅没有超时设置,所以如果我使用ps.listen(),它会一直阻塞,即使客户端关闭了浏览器。

Greenlet在启动进程时有一个超时功能,但我不知道怎么把它们结合起来。

Flask的伪代码:

@app.route('/')
def comet():
    rc = redis.Redis()
    ps = rc.pubsub()
    ps.subscribe('foo')
    for item in ps.listen():
        if item['type'] == 'message':
            return item['data']
    # ps.listen() will block, so how to make it timeout after 30 s?

2 个回答

1
p = redis.pubsub(
     ignore_subscribe_messages=True
)

p.subscribe(
    DB_PREFIX + CHANEL_KEY
)

message = None
timeout = 20
stop_time = time.time() + timeout

# little hack for setting get_message polling timeout
# because redis-py have bug and first call this method processed
# without timeout
while time.time() < stop_time:
    message = p.get_message(timeout=stop_time - time.time())
    if message:
        break

if message:
    data = json.loads(message["data"])
else:
    raise HTTPRequestTimeout

当然可以!请把你想要翻译的内容发给我,我会帮你把它变得简单易懂。

1

因为你没有使用多线程(我假设这是故意的,有时候这样做是明智的),所以你必须使用一种中断方式。在Unix系统中,信号就是一种中断,它可以让你在一个可能会阻塞的调用中返回到一个回调函数。

这个关于打开文件的例子,永远不会返回,正好符合你想做的事情。这个例子来自于 http://docs.python.org/library/signal.html#module-signal

不过要提醒一下,因为Python使用了全局解释器锁来处理操作系统的信号,所以可能会出现一些稳定性问题。不过这些问题通常是比较少见的。

import signal, os

def handler(signum, frame):
    print 'Signal handler called with signal', signum
    raise IOError("Couldn't open device!")

# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)

# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)

signal.alarm(0)          # Disable the alarm

撰写回答