目录
rocketMq架构
- Name Server:是一个几乎无状态节点,可集群部署,在消息队列RocketMQ版中提供命名服务,更新和发现Broker服务。
- Broker:消息中转角色,负责存储消息,转发消息。分为Master Broker和Slave Broker,一个Master Broker可以对应多个Slave Broker,但是一个Slave Broker只能对应一个Master Broker。Broker启动后需要完成一次将自己注册至Name Server的操作;随后每隔30s定期向Name Server上报Topic路由信息。
- 生产者:与Name Server集群中的其中一个节点(随机)建立长连接(Keep-alive),定期从Name Server读取Topic路由信息,并向提供Topic服务的Master Broker建立长连接,且定时向Master Broker发送心跳。
- 消费者:与Name Server集群中的其中一个节点(随机)建立长连接,定期从Name Server拉取Topic路由信息,并向提供Topic服务的Master Broker、Slave Broker建立长连接,且定时向Master Broker、Slave Broker发送心跳。Consumer既可以从Master Broker订阅消息,也可以从Slave Broker订阅消息,订阅规则由Broker配置决定。
nameServer,类似于一个 注册中心,broker进行注册,汇报自身信息,消费者、生产者与namerServer建立长连接,获取topic信息,然后去broker上进行数据操作。
mq作用
1. 解耦
2. 异步
3. 削峰
消息幂等性判断
1. 根据消息主键,查询数据库是否存在
2. redis set操作,自动覆盖,天然幂等
3. 生产者提供消息唯一id,保存redis, 存在则说明消费过这条消息
4. 数据库主键约束,(唯一主键,联合主键,主键)
mq消息积压
1. 修复consumer
2. 临时添加一个topic, partition是原来的10倍,写一个分发数据的consumer,消费后将消息直接分发到新的topic上面,使用10倍的机器部署之前出问题的consumer,消息消费完之后恢复原先的架构
mq消息过期
批量重导,在流量低的时候,通过临时程序,将数据从数据库查询出来,然后重新灌入到mq
rabbitMq
集群模式
优点
任何一个机器宕机了,没事儿,其它机器(节点)还包含了这个 queue 的完整数据,别的 consumer 都可以到其它节点上去消费数据。
缺点
第一,这个性能开销也太大了吧,消息需要同步到所有机器上,导致网络带宽压力和消耗很重!
第二,这么玩儿,不是分布式的,就没有扩展性可言了,如果某个 queue 负载很重,你加机器,新增的机器也包含了这个 queue 的所有数据,并没有办法线性扩展你的 queue
消息重复消费
丢失数据
1. 生产者丢数据
rabbitMq
启动ack确认机制
确保说写 RabbitMQ 的消息别丢,可以开启 confirm
模式,在生产者那里设置开启 confirm
模式之后,你每次写的消息都会分配一个唯一的 id,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个 ack
消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack
接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发
kafka
启用回调函数查看消息是否保存成功
失败重试机制
2. mq丢数据
rabbitMq
开启消息持久化
- 创建 queue 的时候将其设置为持久化
这样就可以保证 RabbitMQ 持久化 queue 的元数据,但是它是不会持久化 queue 里的数据的。 - 第二个是发送消息的时候将消息的
deliveryMode
设置为 2
就是将消息设置为持久化的,此时 RabbitMQ 就会将消息持久化到磁盘上去。
kafka
ack确认机制,kafka服务端配置,当所有的副本都同步完成之后,消息才保存成功,防止在leader宕机后,主从切换的时候,未同步的数据丢失,设置,ack=all或 -1 表示所有的副本同步完数据之后,消息才算保存成功
3. 消费者丢数据
rabbitMq
关闭自动提交ack
Kafka
关闭自动提交offset
顺序消费
rabbitMq
拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点;
或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。
kafka
写 N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性。