使用场景:
- 解耦:多个子系统不直接通讯,而是通过MQ
- 异步:
- 削峰:将同一时间过来的大量请求放入MQ里面,然后慢慢拉取消化
优点:解耦、异步、削峰
缺点:
- 如何保证高可用?(MQ系统崩溃了怎么办)
- 系统复杂度提高(如何保证消息不被重复消费、消息不丢失、消息传递的顺序性)
- 数据一致性问题
常用的MQ:
- Kafka
- ActiveMQ
- RabbitMQ
- RocketMQ
如何保证消息队列的高可用
- 以Kafka为例
- Kafka由多个 broker 组成,每个 broker 就是一个节点
- 当创建一个topic(主题),这个 topic 可以划分为多个 partition(分割),每个 partition 可以存在于不同的 broker 上,每个 partition 就放一部分数据。
- 每个 partition 的数据都会都会通过副本机制同步到其它机器上,形成自己的多个 replica 副本。
- 所有 replica 会选举一个 leader 出来,那么生产和消费都跟这个 leader 打交道,然后其他 replica 就是 follower
- 写的时候,leader 会负责把数据同步到所有 follower 上去,读的时候就直接读 leader 上的数据即可。只能读写 leader
这就是天然的分布式消息队列,就是说一个 topic 的数据,是分散放在多个机器上的,每个机器就放一部分数据。
如何保证消息不被重复消费(幂等性)
- 一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性。
- 比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update 一下好吧。
- 比如你是写 Redis,那没问题了,反正每次都是 set,天然幂等性。
- 比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的 id,类似订单 id 之类的东西,然后你这里消费到了之后,先根据这个 id 去比如 Redis 里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个 id 写 Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。
- 比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。
如何保证消息的可靠性传输(消息丢失的问题)
- kafka弄丢数据解决方案:
- 给 topic 设置
replication.factor
参数:这个值必须大于 1,要求每个 partition 必须有至少 2 个副本。 - 在 Kafka 服务端设置
min.insync.replicas
参数:这个值必须大于 1,这个是要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系,没掉队,这样才能确保 leader 挂了还有一个 follower - 在 producer 端设置
acks=all
:这个是要求每条数据,必须是写入所有 replica 之后,才能认为是写成功了。 - 在 producer 端设置
retries=MAX
(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试,卡在这里
这样配置之后,至少在 Kafka broker 端就可以保证在 leader 所在 broker 发生故障,进行 leader 切换时,数据不会丢失。
- 生产者丢失数据:
- 当上面设置了
acks=all,就不存在生产者丢失数据的情况
- 消费端丢失数据:
- Kafka 会自动提交 offset,那么只要关闭自动提交 offset,在处理完之后自己手动提交 offset,就可以保证数据不会丢
- 但是此时确实还是可能会有重复消费,比如你刚处理完,还没提交 offset,结果自己挂了,此时肯定会重复消费一次,自己保证幂等性就好了
- 以 Kafka 为例
- 写 N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;
- 然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性。
-
首先这个 mq 得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下 kafka 的设计理念,broker -> topic -> partition,每个 partition 放一个机器,就存一部分数据。如果现在资源不够了,简单啊,给 topic 增加 partition,然后做数据迁移,增加机器,不就可以存放更多数据,提供更高的吞吐量了?
-
其次你得考虑一下这个 mq 的数据要不要落地磁盘吧?那肯定要了,落磁盘才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是 kafka 的思路。
-
其次你考虑一下你的 mq 的可用性啊?这个事儿,具体参考之前可用性那个环节讲解的 kafka 的高可用保障机制。多副本 -> leader & follower -> broker 挂了重新选举 leader 即可对外服务。
-
能不能支持数据 0 丢失啊?可以的,参考我们之前说的那个 kafka 数据零丢失方案。
参考: