Publish / Subscribe
发布/订阅
redis-py includes a PubSub object that subscribes to channels and listens for new messages. Creating a PubSub object is easy.
/redis-py 带有可以订阅频道又可以聆听新信息的 PubSub 目标。创建一个PubSub的方法如下:
>>> r = redis.StrictRedis(...)
>>> p = r.pubsub()
Once a PubSub instance is created, channels and patterns can be subscribed to.
创建PubSub 实例后就可以聆听频道和模式了。
>>> p.subscribe('my-first-channel', 'my-second-channel', ...)
>>> p.psubscribe('my-*', ...)
The PubSub instance is now subscribed to those channels/patterns. The subscription confirmations can be seen by reading messages from the PubSub instance.
现在这个PubSub实力已经订阅了这些频道/模式。通过读取来自PubSub实例的信息可以确定订阅已经成功。
>>> p.get_message()
{'pattern': None, 'type': 'subscribe', 'channel': 'my-second-channel', 'data': 1L}
>>> p.get_message()
{'pattern': None, 'type': 'subscribe', 'channel': 'my-first-channel', 'data': 2L}
>>> p.get_message()
{'pattern': None, 'type': 'psubscribe', 'channel': 'my-*', 'data': 3L}
Every message read from a PubSub instance will be a dictionary with the following keys.
所有通过PubSub实例读取的信息都是一个带有以下钥匙的字典:
type: One of the following: 'subscribe', 'unsubscribe', 'psubscribe', 'punsubscribe', 'message', 'pmessage'
类型:下面其中之一:。。。
channel: The channel [un]subscribed to or the channel a message was published to
频道:信息发布给订阅或者被订阅的频道
pattern: The pattern that matched a published message's channel. Will be None in all cases except for 'pmessage' types.
模式:一种对应(发布信息的频道)的模式。除了‘pmessage’类型都是’None’。
data: The message data. With [un]subscribe messages, this value will be the number of channels and patterns the connection is currently subscribed to. With [p]message messages, this value will be the actual published message.
数据:信息里的数据。带有订阅或者未订阅的信息,这个钥匙值代表本次连接被订阅的频道或模式的数量。带有[p]message信息时,这个钥匙值代表实际发布的信息。
Let's send a message now.
现在来发送一个信息。
# the publish method returns the number matching channel and pattern
# subscriptions.
发布的方法返回对应频道和模式的订阅的数量
'my-first-channel' matches both the 'my-first-channel'
# subscription and the 'my-*' pattern subscription, so this message will
# be delivered to 2 channels/patterns
'my-first-channel'对应'my-first-channel’的订阅和'my-*’模式的订阅,所以这个信息会被传送到两个频道/模式>>> r.publish('my-first-channel', 'some data')
2
>>> p.get_message()
{'channel': 'my-first-channel', 'data': 'some data', 'pattern': None, 'type': 'message'}
>>> p.get_message()
{'channel': 'my-first-channel', 'data': 'some data', 'pattern': 'my-*', 'type': 'pmessage'}
Unsubscribing works just like subscribing. If no arguments are passed to [p]unsubscribe, all channels or patterns will be unsubscribed from.
取消订阅和订阅是一样的道理。如果没有参数传到[p]unsubscribe,所有的频道和模式都会被取消订阅。
>>> p.unsubscribe()
>>> p.punsubscribe('my-*')
>>> p.get_message()
{'channel': 'my-second-channel', 'data': 2L, 'pattern': None, 'type': 'unsubscribe'}
>>> p.get_message()
{'channel': 'my-first-channel', 'data': 1L, 'pattern': None, 'type': 'unsubscribe'}
>>> p.get_message()
{'channel': 'my-*', 'data': 0L, 'pattern': None, 'type': 'punsubscribe'}
redis-py also allows you to register callback functions to handle published messages. Message handlers take a single argument, the message, which is a dictionary just like the examples above. To subscribe to a channel or pattern with a message handler, pass the channel or pattern name as a keyword argument with its value being the callback function.
redis-py还可以允许你组册回调函数去处理发布的信息。信息处理者会接受一个参数,信息,也就是和上面说的字典例子一样。当想要订阅一个带有信息处理者的频道或者模式时,传送以频道或者模式的名字为关键字的参数,参数的值为回调函数名。
When a message is read on a channel or pattern with a message handler, the message dictionary is created and passed to the message handler. In this case, a None value is returned from get_message() since the message was already handled.
当信息被频道或模式的信息处理者读取时,信息的字典会被创造然后传送给信息处理者。在这个情况下,没有数值会被get_message()传回,因为信息已经被信息处理者处理了。
>>> def my_handler(message):
... print 'MY HANDLER: ', message['data']
>>> p.subscribe(**{'my-channel': my_handler})
# read the subscribe confirmation message
读取订阅确认信息
>>> p.get_message()
{'pattern': None, 'type': 'subscribe', 'channel': 'my-channel', 'data': 1L}
>>> r.publish('my-channel', 'awesome data')
1
# for the message handler to work, we need tell the instance to read data.
# this can be done in several ways (read more below). we'll just use
# the familiar get_message() function for now
为了让信息处理者工作,我们必须让这个实例去读取数据。
有几种方法可以做到这点(更多的内容下面会介绍)。
这里我们用熟悉的get_message()函数
>>> message = p.get_message()
MY HANDLER: awesome data
# note here that the my_handler callback printed the string above.
# `message` is None because the message was handled by our handler.
注意这里回调函数my_handler打印出的字符串
`message`为空,因为信息被我们的信息处理者处理了>>> print message
None
If your application is not interested in the (sometimes noisy) subscribe/unsubscribe confirmation messages, you can ignore them by passing ignore_subscribe_messages=True to r.pubsub(). This will cause all subscribe/unsubscribe messages to be read, but they won't bubble up to your application.
如果你的应用不需要订阅/取消订阅的确认信息(有时候会很吵),你可以忽略他们:传送ignore_subscribe_messages=True到r.pubsub(),这会使所有的订阅/取消订阅信息被读取,但是他们不会充满你的应用。
>>> p = r.pubsub(ignore_subscribe_messages=True)
>>> p.subscribe('my-channel')
>>> p.get_message() # hides the subscribe message and returns None
>>> r.publish('my-channel')
1
>>> p.get_message()
{'channel': 'my-channel', data': 'my data', 'pattern': None, 'type': 'message'}
There are three different strategies for reading messages.
下面介绍几种读取信息的方法。
The examples above have been using pubsub.get_message(). Behind the scenes, get_message() uses the system's 'select' module to quickly poll the connection's socket. If there's data available to be read, get_message() will read it, format the message and return it or pass it to a message handler. If there's no data to be read, get_message() will immediately return None. This makes it trivial to integrate into an existing event loop inside your application.
刚才的例子使用了pubsub.get_message(),实际上,get_message()使用的是系用的'select’模块来快速检测(poll)连接的socket。如果有数据可以读取,get_message()会读取它们,格式好这些信息然后传给信息处理者。如果没有数据可以读取,则马上返回None。这种方法是比较直接的融入到你的应用的事件循环中。
>>> while True:
>>> message = p.get_message()
>>> if message:
>>> # do something with the message
>>> time.sleep(0.001) # be nice to the system :)
Older versions of redis-py only read messages with pubsub.listen(). listen() is a generator that blocks until a message is available. If your application doesn't need to do anything else but receive and act on messages received from redis, listen() is an easy way to get up an running.
更老版本的redis-py只会用pubsub.listen()读取信息。listen()是一个发射器,只有信息存在时才会运作。如果的应用除了接收然后处理来自redis的信息之外,不需要其他的东西的话,listen()是一个比较简便运作方法。
>>> for message in p.listen():
... # do something with the message
The third option runs an event loop in a separate thread. pubsub.run_in_thread() creates a new thread and starts the event loop. The thread object is returned to the caller of run_in_thread(). The caller can use the thread.stop() method to shut down the event loop and thread. Behind the scenes, this is simply a wrapper around get_message() that runs in a separate thread, essentially creating a tiny non-blocking event loop for you. run_in_thread() takes an optional sleep_time argument. If specified, the event loop will call time.sleep() with the value in each iteration of the loop.
第三种方法是在另外一个线程运行一个事件循环。pubsub.run_in_thread()会创建一个新的线程然后开始事件循环。这个线程目标会返回给run_in_thread()的召唤者。这个召唤者可以用thread.stop()方法来停止事件循环和线程。实际上,这是一个运行在另一个线程的简单的get_message()包装,本质上是创建了一个小的不阻挡的事件循环。run_in_thread()还可以接受一个选择性的sleep_time参数。如果注明了,这个事件循环会召唤time.sleep()然后给出每一个循环的碎叫时间。
Note: Since we're running in a separate thread, there's no way to handle messages that aren't automatically handled with registered message handlers. Therefore, redis-py prevents you from calling run_in_thread() if you're subscribed to patterns or channels that don't have message handlers attached.
注意,因为我们是在另一个线程运行,所以没有办法来处理那些(没有自动被(组册的信息处理者)处理过的)信息。所以redis-py不允许你召唤run_in_thread()如果你订阅了一个没有信息处理者的模式或频道。
>>> p.subscribe(**{'my-channel': my_handler})
>>> thread = p.run_in_thread(sleep_time=0.001)
# the event loop is now running in the background processing messages
# when it's time to shut it down...
现在这个事件循环正在后台处理信息
当需要关闭它时。。。
>>> thread.stop()
A PubSub object adheres to the same encoding semantics as the client instance it was created from. Any channel or pattern that's unicode will be encoded using the charset specified on the client before being sent to Redis. If the client's decode_responses flag is set the False (the default), the 'channel', 'pattern' and 'data' values in message dictionaries will be byte strings (str on Python 2, bytes on Python 3). If the client's decode_responses is True, then the 'channel', 'pattern' and 'data' values will be
automatically decoded to unicode strings using the client's charset.
一个PubSub目标遵循和客户实例同样的编码格式。在被发送给Redis之前,任何unicode的频道或者模式会被编码成由可以的charset指明的编码方式。如果客户的decode_responses flag被设置成默认的False,'channel', 'pattern' and ‘data’里的信息字典的值会是byte 字符串。(str on Python 2, bytes on Python 3).
如果客户的client's decode_responses是True,则自动解码为客户的charset。
PubSub objects remember what channels and patterns they are subscribed to. In the event of a disconnection such as a network error or timeout, the PubSub object will re-subscribe to all prior channels and patterns when reconnecting. Messages that were published while the client was disconnected cannot be delivered. When you're finished with a PubSub object, call its .close() method to shutdown the connection.
PubSub目标会记住他订阅的频道或者模式。在因为网络故障或者暂停而发生连接断开时,他会在重新连接之前再一次订阅之前的模式和频道。在连接断开时发布的信息不会被送达。当你使用完PubSub目标时,召唤它的.close()方法来关闭连接。
>>> p = r.pubsub()
>>> ...
>>> p.close()