9.2发布消息/订阅频道
Redis的“发布/订阅”模式是一种消息通信模式,实现了一对多的消息实时发布功能。
9.2.1 实例36:实现一对多的消息发布
实例描述
分别使用Redis的字符串和“发布/订阅”模式实现一对多的消息通信,比较这两种方式的差异。
1.使用字符串实现一对多的消息发布功能
使用字符串来实现一对多的消息发布功能,逻辑非常简单:
(1)定好一个字符串Key,例如message。
(2)发送端使用字符串的set操作把新信息设置到这个Key中。
(3)多个接收端不停获取 message 的值。如果发现值变化了,则认为来了新的消息,接收并保存。
使用字符串实现一对多的消息发布功能,代码分为发送端和接收端。
(1)发送端代码:
代码9-8 使用字符串实现一对多的消息发布(发送端代码)
其中,主要代码说明如下。
● 第8行代码:使用一个无限循环来实现持续发布消息。
● 第9行代码:让用户在命令行输入需要发布的信息。
● 第10行代码:记录当前的时间,时间格式为“2018-08-19 11:29:12”。
● 第11行代码:把信息和时间组装为一个字典。
● 第12行代码:以JSON格式把信息设置到Redis的message字符串中。
(2)接收端的代码如下:
代码9-9 使用字符串实现一对多的消息发布(接收端代码)
其中,主要代码说明如下。
● 第7行代码:初始化时间记录变量。
● 第8行代码:使用一个无限循环来持续接收数据。
● 第9行代码:读取Redis中名字为message的字符串。
● 第10~12行代码:message这个Key可能不存在,此时返回None。遇到这种情况就等待1秒以后跳过本次循环。
● 第 13~15行代码:使用json模块解析JSON字符串,以获取信息和发送时间。
● 第16~19行代码:如果信息的发送时间与上一条信息的发送时间一样,则说明是同一条信息,不需要打印出来。
● 第21行代码:更新时间记录变量。
发送端的运行效果如图9-10所示。
图9-10 发送端运行效果
接收端的运行效果如图9-11所示。
图9-11 接收端运行效果
2.使用字符串的弊端
使用字符串进行消息的发布,虽然说代码简单易懂,但它也存在诸多问题。举例如下:
● 接收端不知道发送端什么时候发布消息,因此必须持续不断检查Redis,浪费系统资源。
● 由于轮询查询,所以消息有延迟。
● 如果发送端在1秒内连续更新10条,则后一条会覆盖前一条,而接收端每1秒才获取一次数据,必然导致最多漏掉9条数据。要减少遗漏数量就需要增加轮询频率,进一步增大系统开销。
3.使用Redis的“发布/订阅”模式实现消息通信
“发布/订阅”模式是 Redis 自带的一对多消息通信模式。使用“发布/订阅”模式不仅可以解决字符串通信遇到的各种问题,而且代码更简洁。
(1)发送端代码如下。
代码9-10 使用“发布/订阅”模式实现一对多消息通信(发送端代码)
其中,主要代码说明如下。
● 第8行代码:进入无限循环,不停发布信息。
● 第9行代码:获取用户在命令行输入的内容。
● 第10行代码:获取当前的时间,并转化为“2018-08-19 13:23:00”这种格式。
● 第11行代码:把信息和当前时间组装为一个字典。
● 第12行代码:把信息以JSON字符串的形式发布到名为pubinfo的频道中。
(2)接收端代码如下。
代码9-11 使用“发布/订阅”模式实现一对多消息通信(接收端代码)
其中,主要代码说明如下。
● 第5行代码:生成一个发布/订阅对象,并忽略订阅成功的消息。
● 第6行代码:订阅名为pubinfo的频道。
● 第7~9行代码:从频道中获取信息并打印。
9.2.2 实例37:在Python中发布消息/订阅频道
实例描述
在Python中操作Redis,使用“发布/订阅”模式实现以下功能:
(1)向一个频道中发布消息。
(2)订阅多个频道。
“发布/订阅”模式在Redis中只有6个命令,对应到Python中有6个方法。
1.发布消息
在Python中向一个频道发送消息,代码和向Redis字符串设置值一样简单,差别只是使用的方法名为“publish”,格式如下:
client.publish(’频道名’, ’消息’)
例如代码9-12。
代码9-12 使用Python向一个频道发布消息
2.订阅频道
订阅频道涉及的步骤稍微多一些。首先需要生成一个“发布/订阅”对象,然后使用这个对象来订阅频道。订阅频道以后,循环从频道里面获取数据。
(1)一个订阅实例只订阅一个频道。
格式为:
代码9-13 使用Python订阅频道
第3行的instener.listen()是一个阻塞式的方法。程序运行到这里,如果频道里面没有数据,则程序就会“卡住”,直到频道里面有了新的信息,才会继续运行后面的代码。
第3行的for循环获得的message是一个字典,它的内容有两种情况。
● 第1次进入for循环时,数据为:
{'type': 'subscribe', 'pattern': None, 'channel': b'pubinfo', 'data': 1}
这条信息表明订阅频道“pubinfo”成功。如果不想显示这一条内容,则在初始化“发布/订阅”对象时,可以指定一个参数:ignore_subscribe_messages=True。
● 从第2次循环开始就是正式的频道信息,格式为:
{'type': 'message', 'pattern': None, 'channel': b'pubinfo', 'data': b'{"message":"yy", "time": "2018-08-19 13:38:58"}'}
发送端发送的信息,保存在字典的data这个Key对应的值里面,还是bytes型的数据,需要解码为字符串以后做进一步处理。
(2)一个“发布/订阅”实例可以订阅多个频道。
一个“发布/订阅”实例可以订阅多个频道,格式为:
listener = client.pubsub()
listener.subscribe(’频道名1', ’频道名2', ’频道名n')
例如代码9-14。
代码9-14 在一个发布订阅实例中订阅多个频道
其中,主要代码说明如下。
● 第5行代码:生成“发布/订阅”对象,并且不显示订阅成功信息。
● 第6行代码:订阅computer、math、shopping三个频道。
● 第7行代码:使用for循环获取频道发布的信息。
● 第8行代码:获取这一条信息属于哪一个频道。
● 第9~10行代码:获取信息内容并打印。
运行效果如图9-12所示。
图9-12 订阅多个频道
3.“发布/订阅”模式的注意事项
“发布/订阅”模式的工作过程就像收音机的广播一样,只有调到了这个频道,才能收到信息,而之前的信息就都丢失了。例如,发送端先发送10条信息,再启动接收端,则接收端是没有办法收到先发送的10条信息的。
可以有非常多的接收端同时订阅一个频道。一旦这个频道有消息发布,所有接收端都会收到信息。
9.2.3 实例38:在redis-cli中发布消息/订阅频道
实例描述
在redis-cli中使用“发布/订阅”模式,实现以下功能:
(1)向一个频道中发布消息。
(2)订阅多个频道并接收频道中的消息。
1.发布信息
在redis-cli中向频道发布信息非常简单,使用命令“publish”即可,格式如下:
publish 频道名 信息
例如:
publish computer 人工智能新突破
运行效果如图9-13所示。
图9-13 在redis-cli发布信息
2.订阅频道
订阅频道使用的命令为“subscribe”,命令格式如下:
subscribe 频道名1 频道名2 频道名n
例如:
subscribe computer math
订阅以后,一旦被订阅的频道有新的消息发布,订阅端就会收到信息,如图9-14所示。中文无法正常显示,每一条新发布的信息都会对应redis-cli中的3条返回信息:第1条是信息类型,第2条是频道名,第3条是被发布的内容。
图9-14 在redis-cli订阅频道并接收信息