Redis随记--pubsub学习
PUBSUB学习
基础知识
在Redis中可以使用LIST来实现简单的消息队列功能,但基于LIST实现的消息队列无法实现"消息多播"的功能。
Redis单独实现PubSub模块来实现"消息多播"功能,并支持模式订阅
常用命令
- PUBSUB:用于检查消息订阅的状态信息。
- PUBLIST:将消息传递给特定的Channel。
- SUBSCRIBE:订阅特定的Channel。
- PSUBSCRIBE:基于模式订阅匹配该模式的所有Channel。
- UNSUBSCRIBE:退订特定的Channel。
- PUNSUBSCRIBE:特对特定模式的所有Channel。
实例代码
发布消息代码:
import time
import datetime
import redis
REDIS_HOST = "xxx.xxx.xxx.xxx"
REDIS_PASSWORD = "xxxxxxxxxxxx"
REDIS_PORT = 6379
REDIS_CHARSET = "utf-8"
PUB_SUB_CHANNEL = "demo_channel"
def pub_demo():
redis_client = redis.StrictRedis(
host=REDIS_HOST,
port=REDIS_PORT,
password=REDIS_PASSWORD,
charset=REDIS_CHARSET
)
for task_index in range(1, 1000):
task_msg = "this is task {} at {}".format(
task_index,
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
)
redis_client.publish(PUB_SUB_CHANNEL, task_msg)
print(task_msg)
time.sleep(1)
if __name__ == '__main__':
pub_demo()
使用监听方式订阅消息代码:
import redis
REDIS_HOST = "xxx.xxx.xxx.xxx"
REDIS_PASSWORD = "xxxxxxxxxxxx"
REDIS_PORT = 6379
REDIS_CHARSET = "utf-8"
PUB_SUB_CHANNEL = "demo_channel"
def sub_demo():
redis_client = redis.StrictRedis(
host=REDIS_HOST,
port=REDIS_PORT,
password=REDIS_PASSWORD,
charset=REDIS_CHARSET
)
sub_handler = redis_client.pubsub()
sub_handler.subscribe(PUB_SUB_CHANNEL)
for sub_msg in sub_handler.listen():
if sub_msg["type"] == "subscribe":
print("start to subscribe message")
if sub_msg["type"] == "message":
print("task message:{}".format(
str(sub_msg["data"], encoding="utf-8")
))
if __name__ == '__main__':
sub_demo()
使用定期轮循方式订阅消息:
import redis
import time
REDIS_HOST = "xxx.xxx.xxx.xxx"
REDIS_PASSWORD = "xxxxxxxxxxxx"
REDIS_PORT = 6379
REDIS_CHARSET = "utf-8"
PUB_SUB_CHANNEL = "demo_channel"
def sub_demo():
redis_client = redis.StrictRedis(
host=REDIS_HOST,
port=REDIS_PORT,
password=REDIS_PASSWORD,
charset=REDIS_CHARSET
)
sub_handler = redis_client.pubsub()
sub_handler.subscribe(PUB_SUB_CHANNEL)
while True:
sub_msg = sub_handler.get_message()
if sub_msg is None:
time.sleep(1)
continue
if sub_msg["type"] == "subscribe":
print("start to subscribe message")
if sub_msg["type"] == "message":
print("task message:{}".format(
str(sub_msg["data"], encoding="utf-8")
))
if __name__ == '__main__':
sub_demo()
运行发布消息产生的结果:
(venv) H:\GGA_CODE\pubsub_demo>python pub_demo.py
this is task 1 at 2021-02-28 20:39:06
this is task 2 at 2021-02-28 20:39:07
this is task 3 at 2021-02-28 20:39:08
this is task 4 at 2021-02-28 20:39:09
this is task 5 at 2021-02-28 20:39:10
this is task 6 at 2021-02-28 20:39:11
this is task 7 at 2021-02-28 20:39:13
this is task 8 at 2021-02-28 20:39:14
this is task 9 at 2021-02-28 20:39:15
运行订阅消息产生的结果(早于发布运行):
(venv) H:\GGA_CODE\pubsub_demo>python sub_loop_demo.py
start to subscribe message
task message:this is task 1 at 2021-02-28 20:39:06
task message:this is task 2 at 2021-02-28 20:39:07
task message:this is task 3 at 2021-02-28 20:39:08
task message:this is task 4 at 2021-02-28 20:39:09
task message:this is task 5 at 2021-02-28 20:39:10
task message:this is task 6 at 2021-02-28 20:39:11
task message:this is task 7 at 2021-02-28 20:39:13
task message:this is task 8 at 2021-02-28 20:39:14
task message:this is task 9 at 2021-02-28 20:39:15
运行订阅消息产生的结果(晚于发布运行):
(venv) H:\GGA_CODE\pubsub_demo>python sub_listen_demo.py
start to subscribe message
task message:this is task 5 at 2021-02-28 20:39:10
task message:this is task 6 at 2021-02-28 20:39:11
task message:this is task 7 at 2021-02-28 20:39:13
task message:this is task 8 at 2021-02-28 20:39:14
task message:this is task 9 at 2021-02-28 20:39:15
总结:
- Python客户端支持lister和get_message两种方式订阅消息,区别在于是否阻塞方式调用parse_response方法。
- Redis支持多用户订阅相同Channel,订阅的程序能收到从建立订阅连接开始的消息,建立连接前发布的消息会丢失。
def listen(self):
"Listen for messages on channels this client has been subscribed to"
while self.subscribed:
response = self.handle_message(self.parse_response(block=True))
if response is not None:
yield response
def get_message(self, ignore_subscribe_messages=False, timeout=0):
"""
Get the next message if one is available, otherwise None.
If timeout is specified, the system will wait for `timeout` seconds
before returning. Timeout should be specified as a floating point
number.
"""
response = self.parse_response(block=False, timeout=timeout)
if response:
return self.handle_message(response, ignore_subscribe_messages)
return None
学习总结
Redis的PubSub模块实现简单的发布订阅功能,支持消息多播,但PubSub模块不支持消息持久化,消费者断连期间的消息会丢失,同时PubSub也不支持消费组和消息确认等功能,导致在生产环境很难有场景可以使用PubSub模块。