Redis系列(十三)、pub/sub发布与订阅(对比List和Kafka)

在前面的文章中或多或少有提到Redis的publish命令,它就是Redis中的发布订阅中的一个命令,本篇就介绍Redis中的发布订阅模式,以及对比使用List实现的消息队列和传统消息队列Kafka的区别。


目录

介绍

使用

对比

对比List

对比Kafka


Redis6系列文章

Redis系列(一)、CentOS7下安装Redis6.0.3稳定版

Redis系列(二)、数据类型之字符串String 

Redis系列(三)、数据类型之哈希Hash

Redis系列(四)、数据类型之列表List

Redis系列(五)、数据类型之无序集合Set

Redis系列(六)、数据类型之有序集合ZSet(sorted_set)

Redis系列(七)、常用key命令

Redis系列(八)、常用服务器命令 

Redis系列(九)、Redis的“事务”及Lua脚本操作

Redis系列(十)、详解Redis持久化方式AOF、RDB以及混合持久化

Redis系列(十一)、Redis6新特性之ACL安全策略(用户权限管理)

Redis系列(十二)、Redis6集群搭建及原理(主从、哨兵、集群)


介绍

Redis中的订阅、发布实现了发布/订阅消息范式,发布者不是计划发送消息给特定的订阅者,而是发布消息到不同的频道,发布者不需要知道是哪些订阅者订阅了消息。订阅者对一个或多个频道感兴趣,只需接收感兴趣的消息,不需要知道是什么样的发布者发布的消息。这种发布者和订阅者的解耦合可以带来更大的扩展性和更加动态的网络拓扑。

在Redis的发布订阅模式中,有三个部分:

Publisher(发布者):发送消息到频道中,每次只能往一个频道发送一条消息;

Subscriber(订阅者):订阅频道,订阅者可以同时订阅多个频道;

Channel(频道):将发布者发布的消息转发给当前订阅此频道的订阅者;

使用

发布订阅的命令如下:

#发布消息到指定的频道
PUBLISH channel message

#订阅给定的一个或多个频道的信息
SUBSCRIBE channel [channel ...]

#订阅一个或多个符合给定模式的频道
PSUBSCRIBE pattern [pattern ...] 

#指退订给定的频道
UNSUBSCRIBE [channel [channel ...]] 

#退订所有给定模式的频道
PUNSUBSCRIBE [pattern [pattern ...]] 

#查看订阅与发布系统状态
PUBSUB subcommand [argument [argument ...]] 

我们使用上篇文章中搭建的集群来测试Redis的订阅发布模式,A节点作为发布者,A,B,C节点作为订阅者消费A节点发布的消息:

订阅者6381:与发布者在同一节点,订阅www,csdn,wyk三个频道;

订阅者6382:订阅符合csdn*和wyk*模式的所有频道;

订阅者6383:订阅csdn频道;

发布者6381:分别往csdn1,csdn2,csdn,wyk四个频道发送消息,验证三个订阅者接收消息的情况以及发布者发布消息后的返回值;

断开后的订阅者重新订阅后会丢失断开期间发布者发布的消息:

在集群模式中,发布者发布消息后的返回值取决于订阅者与发布者在不在同一个节点上:

发布者发布消息后返回值为与发布者相同节点当前订阅了该频道的客户端数量。

对比

在上面的示例中,大家也可以看到,Redis中的发布订阅非常像消息队列,但还是有不同,我们就来对比一下Redis的List实现消息队列以及传统消息队列Kafka看看有哪些不同:

对比List

与Redis中的List对比,基于List实现的消息队列需要结合lpush + brpop来实现。

胜(多消费组):当多个客户端同时消费同一个List消息队列时,消费者A使用brpop消费的数据就从list中弹出了,消费者B就再也读不到该数据,而在发布订阅中,多个订阅者可以订阅相同的频道,频道内的数据会分发到各个订阅者,不会出现某一个订阅者消费了之后,另一个订阅者读不到该数据的情况。

负(断点消费):但对于List的消息队列来说,当消费者断开后重连,仍然可以从List中断点消费还没消费的数据,而发布订阅中,如果订阅者断开重连,会丢失断开期间发布者发布的数据,无法恢复。

对比Kafka

Redis的发布订阅以及List并不是要和专业的消息队列对标,而是可以实现类似的功能,真正在消息队列领域做的好的有很多,RabbitMQ,ActiveMQ,RocketMQ,Kafka,Pulsar等等,发布订阅相比于它们有什么异同呢?

不同点

持久化:Kafka会将数据持久化到磁盘内,而Redis的发布订阅做不到;

断点消费:上面也提到,当订阅者断开重连会丢失断开期间发布者发布的消息,而kafka中会记录每个消费者消费的topic的offset,因此kafka可以从断开的offset继续消费;

偏移量:基于上一条,同样的kafka的消费者可以指定从某个offset开始重新消费,而Redis发布订阅根本不会记录订阅者消费的偏移量; 

消费方式: 在Redis发布订阅中,数据消费情况是由发布者控制的,当发布者发布到频道中后,只有当前连接了频道的订阅者才能消费到数据,断开重连的会失去那部分数据。而kafka中消费进度是由消费者控制的,消费者从topic中拉取数据并记录消费的offset。

相同点

消息模型:在JMS消息模型中有点对点和订阅发布两种,Kafka和Redis发布订阅都是采用发布订阅的模型。

消费者组:Kafka里在不同的消费者组中的消费者消费相同的topic时会各自维护一个offset,因此不会出现A消费之后的数据,B就消费不到的情况。Redis中订阅者订阅相同的频道也不会出现类似的情况。

 

希望本文对你有帮助,请点个赞鼓励一下作者吧~ 谢谢!

<think>好的,我现在需要帮助用户了解如何使用Redis发布订阅Pub/Sub)功能在分布式系统中实现通知机制的最佳实践。首先,我应该回顾一下Redis Pub/Sub的基本概念命令,比如PUBLISHSUBSCRIBE、PSUBSCRIBE等,确保自己对这些基础内容有正确的理解。用户提到的引用[1][2]提到了Redis Pub/Sub其他消息队列系统的对比,特别是扩展性消息可靠性,这些可能在最佳实践中需要考虑。 接下来,我需要确定用户的具体需求。他们希望在分布式环境中实现通知机制,所以可能需要考虑多个服务实例之间的通信、消息的可靠传递、扩展性以及如何处理可能出现的故障。根据引用[1],Redis的扩展性有限,所以可能需要建议如何克服这一点,比如分片或者结合其他技术。同时,消息可靠性也是一个关键点,因为Redis Pub/Sub不保证消息持久化,如果订阅者断开连接可能会丢失消息,所以需要提出解决方案,比如确认机制或补充持久化存储。 然后,我需要整理最佳实践的步骤。首先,频道设计,合理的命名空间结构可以避免混乱。比如使用特定的前缀层级结构,如“notifications:order:created”。其次,订阅者的实现,可能需要使用多路复用或者多线程来处理消息,避免阻塞。例如在Python中使用redis-py库,创建单独的线程来监听消息。同时,错误处理重连机制也很重要,订阅者需要能够自动重新连接,防止网络问题导致的中断。 关于消息格式,建议使用结构化的数据,比如JSON,方便解析扩展。同时,消息大小需要控制,避免影响性能。另外,消息确认机制可能需要引入额外的组件,比如结合Redis Streams来跟踪处理状态,或者使用数据库记录已处理的消息ID,确保消息不会丢失。 扩展性方面,由于Redis是单线程的,单个实例可能成为瓶颈,所以可以考虑分片策略,将不同的频道分配到不同的Redis实例上。或者使用集群模式,但需要注意Pub/Sub在集群中的行为,可能需要每个节点单独订阅。 监控维护也是不可忽视的部分,需要记录消息的发布订阅情况,使用Redis的监控命令或者集成外部监控工具。此外,设置合理的过期时间,避免频道堆积未处理的消息,尤其是在高负载情况下。 最后,需要考虑Redis Pub/Sub的适用场景。如果系统对消息可靠性要求极高,可能需要结合其他消息队列Kafka或RabbitMQ,或者在发布消息的同时写入数据库备份,确保即使Redis出现问题,消息也能恢复。 总结这些点,我需要以清晰的结构将这些最佳实践分点列出,涵盖频道设计、订阅者实现、消息格式、确认机制、扩展性、监控、过期策略适用场景。同时,生成的相关问题需要引导用户进一步探索,比如可靠性保障、扩展性方案、Streams对比等。</think>### Redis Pub/Sub 实现分布式通知机制的最佳实践 #### 1. **频道设计命名规范** - **层级化命名**:使用`:`分隔符定义频道层级,例如 `notifications:order:created`,便于分类管理[^2] - **命名空间隔离**:为不同业务模块添加前缀,如 `payment:success` `inventory:low` - **通配符订阅**:通过 `PSUBSCRIBE notifications:*` 实现批量订阅相关频道 #### 2. **订阅者实现方案** ```python import redis import threading def message_handler(msg): print(f"收到通知: {msg['data']}") def subscribe_worker(): r = redis.Redis(host='redis-host', port=6379) pubsub = r.pubsub() pubsub.psubscribe('notifications:*') for msg in pubsub.listen(): if msg['type'] == 'pmessage': message_handler(msg) # 启动独立订阅线程 threading.Thread(target=subscribe_worker, daemon=True).start() ``` #### 3. **消息可靠性增强** - **消息格式标准化**:建议使用 JSON 结构化数据: $$\{ "event": "order_created", "timestamp": 1629999999, "data": \{...\} \}$$ - **补充持久化**:重要消息可同步写入 Redis Streams 或数据库[^1] - **心跳检测**:定期发送 `PING/PONG` 维持连接 #### 4. **扩展性优化** - **分片策略**:按业务维度分配不同 Redis 实例 ```bash # 订单类通知 redis-cli -h shard1 publish orders:update "{...}" # 库存类通知 redis-cli -h shard2 publish inventory:alert "{...}" ``` - **集群模式**:注意需在每个节点单独订阅 #### 5. **异常处理机制** - **自动重连**:在订阅循环中增加异常捕获 - **退避策略**:重连间隔采用指数退避算法 - **死信处理**:通过 `__keyspace@0__:频道名` 监听异常事件 #### 6. **监控指标** - 通过 `INFO PUBSUB` 获取活跃频道数 - 监控 `pubsub_patterns` 字段观察模式订阅数量 - 使用 `CLIENT LIST` 跟踪订阅者连接状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王义凯_Rick

遇见即是缘,路过就给个评论吧~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值