消息中间件
场景
- 解耦
- 异步
- 削峰
- 系统可用性降低
- 系统复杂性增加
分类
- Kafka
- ActiveMQ
- RabbitMQ
- RocketMQ
重复消费(幂等性)
- 拿消息做数据库的insert操作: 给消息做一个唯一主键,就算出现重复消费的情况,就会导致主键冲突
- 拿消息做redis的set的操作: 不用解决,set操作本来就算幂等操作
- 给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis,开始消费前,先去redis中查询有没有消费记录
可靠性传输
-
生产者弄丢数据:
1.1. 事务方式
a. 发送消息前,开启事务
b. 发送消息,如果发送过程中出现异常,事务回滚
c. 发送成功则提交事务
1.2. Confirm 机制
a. 每次写消息的时候会分配一个唯一的 ID
b. RabbitMQ 收到之后会回传一个 ACK
c. 如果 RabbitMQ 没有接收到ACK,那么就回调一个 Nack 的接口,这个时候生产者就可以重发消息 -
消息队列弄丢数据
2.1. 开启持久化磁盘的配置
a. 将队列的持久化标识durable设置为true,代表是一个持久的队列
b. 发送消息的时候将deliveryMode=2,将消息持久化到磁盘上去
c. 即使rabbitMQ挂了,重启后也能恢复数据 -
消费者弄丢数据
3.1 关闭 RabbitMQ 消费者的自动提交 ACK,在消费者处理完这条消息之后再手动提交 ACK
高可用性
- 单机模式
- 普通集群模式
2.1. Master-Slave 模式,创建的 Queue,只会放在一个 Master RabbtiMQ 实例上,其他实例都同步那个接收消息的 RabbitMQ 元数据
2.2. 如果放 Queue 的实例宕机了,会导致其他实例无法拉取数据
2.3. 此方案主要是提高吞吐量的,让集群中多个节点来服务某个 Queue 的读写操作 - 镜像集群模式
3.1. 建的 Queue 无论元数据还是 Queue 里的消息都会存在于多个实例上
3.2. 每次写消息到 Queue 的时候,都会自动把消息到多个实例的 Queue 里进行消息同步
3.3. 性能开销过高,消息需要同步所有机器,会导致网络带宽压力和消耗很重
3.4. 无法解决某个 Queue 数据量特别大的情况,导致 Queue 无法线性拓展
顺序性
- 拆分多个 queue,每个 queue 一个 consumer
- 一个queue对应一个consumer,consumer内部用内存队列做排队,然后分发给底层不同的 worker 来处理