消息队列
消息队列是分布式系统的一个重要组件,从五个问题来初步认识一下消息队列,基本原理是什么样的,如何正确的使用消息队列。
- Q1: 为什么需要消息队列?
- Q2: 如何保证消息不丢失?
- Q3: 如何处理重复消息?
- Q4: 如何保证消息有序性?
- Q5: 如何处理消息堆积?
为什么需要
异步处理
- 随着业务的增长,业务逻辑会不断加重,为了保持较快速的响应,可以在核心逻辑处理完后就返回,其他逻辑放到消息队列之后异步处理
应用解耦
- 业务模块增加,可以通过订阅核心服务的消息主题,不影响核心服务
流量控制
- 后端服务无法支撑大量的并发请求,请求先放到队列,后端服务尽最大的能力消费队列
日志处理
基本概念
模型
点对点(队列)模型
同一个消息只能由一个消费者消费一次
- Rabbit MQ
发布/订阅模型
订阅了某个主题的所有消费者都可以消费该主题的消息
- Rocket MQ、Kafka
各个组件术语(Kafka)
生产者(Producer)
消息队列服务器(Broker)
主服务器(Leader)
从服务器(Follower)
主题(Topic)
- 分区(Partition)
消费者组(Consumer Group)
- 消费者(Consumer)
工作流程
生产消息(发送数据)
- 从Kafka Cluster获取分区的Leader,Producer将消息发送给Leader
- Leader将消息写入本地文件(此时可以直接到步骤3)
- 2.1. Followers从Leader pull消息并写入本地后向Leader发送ACK确认
- Leader向Producer响应ACK
存储数据
单独开辟一块磁盘,顺序写入
每个分区相当于一个文件目录
Partition/Segment
- .index
- .log
- .timeindex
消费消息(接收数据)
- Consumer也是从Leader中拉取数据
- 一个消费者组内的某个消费者可以消费一个Topic的不同分区,单同一个组内的不同消费者不能同时消费某个Topic的同一个分区,一个组的消费者数量最好和分区数相同
分区的好处
- 方便扩展
- 提高并发
实际问题
如何保证消息队列不丢失?
- 生产消息阶段
- 正确处理Broker的响应,做重试机制
- 数据存储阶段
- 数据落盘再响应成功,有多个副本时可以等多副本都落盘再响应成功
- 消费消息阶段
- 业务逻辑处理完再确认消息
确保了可靠性的同时会影响性能,根据业务选择合适的方式
如何处理重复消息?
为了保证消息不丢失,消息重复是不可避免的
业务逻辑幂等性
- 增加version版本号做控制
- 数据库唯一索引
如何保证消息有序?
全局有序
- 一个生产者、一个分区、一个消费者
部分有序
- 消息按特定规则分配到不同的分区,分区本身是有序的,每个分区由一个消费者消费
如何处理消息堆积?
- 定位问题,如果是bug引起,修改bug(实际生产场景如果由于发版导致,先回滚再定位原因)
- 如果不是bug,看能不能优化消费逻辑
- 如果不能优化,就要横向扩容,同时增加分区数和消费者数量