发布订阅
角色: 发布者 订阅者 频道
发布订阅的通信模型:
发布订阅模型
发布者和订阅者都在客户端,而频道在redis服务端。
发布者发布消息到redis服务器,频道会将消息广播到关注了这个频道的所有订阅者。
命令
publish channel message # 发布消息,返回订阅者个数
subscribe channel # 订阅,可以接多个channel
unsubscribe channel # 取消订阅,可以接多个channel
对比消息队列和发布订阅:
二者的共同点是:消息发布者和订阅者都在客户端,频道或者队列在服务端。
不同点:发布订阅会将消息发给所有订阅者,但是消息队列则只有一个订阅者能接收到(或者说是抢到)队列弹出来的元素。
消息队列模型
所以消息队列除了能用来缓解请求压力之外,还能够实现一个"抢"的功能,例如实现抢红包功能。不过消息队列用来处理并发请求用的是阻塞的brpop命令,而实现抢红包应该是列表中已经有红包,直接使用rpop就好。
注意,订阅者只可以接收到订阅之后的消息,订阅之前频道发的消息是接收不到的。
Python演示:
订阅者 subscriber.py
# coding=utf-8
import redis
import time
r = redis.Redis("127.0.0.1",6379)
p = r.pubsub() # 创建一个PubSub 发布订阅对象
p.subscribe("test_channel") # 订阅一个频道
while True:
# 接收消息
data = p.get_message()
if data:
print(data)
time.sleep(0.1)
# 或者使用p.listen(),listen()会返回一个生成器,可以遍历这个生成器来获取消息,当频道没有消息的时候,listen会阻塞循环;当有消息时,会接收消息
#for message in p.listen():
# print(message)
发布者 publisher.py
# coding=utf-8
import redis
r = redis.Redis("127.0.0.1",6379)
res = r.publish("test_channel","This is test data");
print(res)
订阅者先订阅
python subscriber.py
发布者再发布
python publisher.py
注意:当客户端订阅了一个频道,这个客户端脚本就会一直处于一个阻塞的运行状态。如果想要结束脚本就要调用 unsubscribe 取消订阅
# coding=utf-8
import redis
import time
r = redis.Redis("127.0.0.1",7000)
p = r.pubsub() # 创建一个PubSub 发布订阅对象
p.subscribe("test_channel") # 订阅一个频道
count = 0
for message in p.listen():
count+=1
print(message)
if count>5:
p.unsubscribe("test_channel")
如果是用php来订阅消息,取消订阅后脚本还无法结束运行,此时要die强行终止才行:
<?php
set_time_limit(0);
$r = new Redis();
$r->connect("127.0.0.1",7000);
$r->subscribe(['test'],"cb");
function cb($redis,$channel,$msg){
var_dump($msg);
$redis->unsubscribe(["test"]);
die;
}
?>