抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

MQ相关问题

  • MQ使用场景
  • MQ高可用
  • MQ重复消费
  • MQ可靠性传输
  • MQ顺序消费
  • MQ积压消息

MQ使用场景

  • 解耦
  • 削峰
  • 异步

优缺点

  • 系统可用性降低. MQ出问题, 系统可能就崩溃了, 无法运转了.
  • 系统的复杂性提高. 考虑的问题变多, 导致系统的复杂性变高. 系统投递到MQ的消息重复, 会导致下游消费系统会重复消费. 消息的顺序问题. 积压数据过多.
  • 一致性问题. 某个下游系统出问题, 导致数据不一致.

MQ对比

  • RabbitMQ 延时很低, 指从消息写到MQ到被消费, 提供很好的用户管理界面. 社区很活跃, 问题是erlang语言的, 意味着基于是不可控的状态.对于MQ来说, 吞吐量到万级, 功能完备, 开源社区活跃.
  • RocketMQ. topic数量多. 分布式架构. 没有RabbitMQ活跃. 基于Java语言, 可掌控, 如果基础结构能力强的公司可以考虑.
  • Kafka, 纯分布式系统, 可以做到零丢失, 功能简单, 大数据, 日志采集领域用很多.
  • ActiveMQ, 不推荐, 大规模场景没有得到业界的验证.

MQ高可用

RabbitMQ高可用

单机模式。普通集群模式. 镜像集群模式.

普通集群模式。其中一个实例包含元数据和实际数据, 而其它机器包含的是元数据. 如果访问的机器没有实际的数据, 会从有数据的机器上拉取. 实际就是一个集群, 不算分布式, 可以从多个机器消费数据, 提高吞吐量.

如果固定连接 queue 所在的实例, 会有单实例的性能瓶颈.

缺点: 内部产生大量的数据传输; 基本没有可用性的保障.

镜像集群模式。每个节点都包含所有数据(互相自动同步). 所以任何节点宕机了, 其它节点上还包含所有完整数据. 也不是分布式的.

缺点: 节点容量上限会造成系统上的瓶颈.

Kafka高可用

分布式架构。由多个 broker 组成,每个 broker 是一个节点;每创建一个 topic,这个 topic 可以被划分成多个 partition,每个 partition 可以存在于不同的 broker 上,每个 partition 放一部分数据,把数据分散了,是一个天然的分布式消息队列。

如果某个 partition 丢失了, 会导致部分数据丢失, 这样不是高可用.

所以引入 replica 副本机制. 一个 partition 多个副本, 副本之间会选举出 leader, 其余是 follower.

生产者只能往 leader 写数据. 消费者也只能从 leader 消费数据. 所有读写都在 leader 上面. 因为如果可以读其它 replica 的话, 就会出现数据一致性的问题, 这样系统又变复杂了.

leader 负责同步消息到 follower 上去.

MQ可靠性传输

其实就是消息丢失问题, 可靠性投递.

MQ 是一般是公司的核心业务, 数据是不能丢失的.

从整个链路来看, 生产者, MQ, 消费者, 这三者都可能出现消息丢失的问题.

对于 RabbitMQ

生产者

  1. RabbitMQ 事务. 主要API: channel.txSelect/channel.txRollback/channel.txCommit. 这种事务机制是同步阻塞的, 等待是成功还是失败, 所以会导致发送消息的吞吐量下降.
  2. confirm模式. 设置 channelconfirm 模式. 发送消息之后就不用管了. 如果收到了, 会回调生产者本地的接口(ack), 通知说已经收到了. 如果接收时报错了, 也会回调接口, 可以再次重发.

channel 是与 broker 建立连接之后创建的 AMQP信道. 实际是使用多个 channel 对应一个 Connection, 可以提高连接的利用率. 毕竟 TCP 连接的创建和销毁是消耗资源的.

RabbitMQ

持久化到磁盘. 创建 queue 时要设置成持久化的, 持久化的是元数据, 而不是 queue 里的数据; 发送消息时, 将 deliveryMode 设置为2, 这时候才会将数据持久化到磁盘上.

即使开启持久化了, 也有可能持久化到磁盘的过程中丢失数据.

消费者

打开了 autoAck 的选项, 就是消费数据之后, 会自动通知 RabbitMQ 说消费成功了. 有一种可能就是, 没有处理完就 autoAck 了, 不巧的是, 消费者系统宕机了, 那条数据就出现问题了.

关闭 autoAck, 开发者手动 Ack. 如果没有发送成功, RabbitMQ 会将此消息发送给其它消费者进行消费.

对于 Kafka

生产者

按照下面 Kafka 设置, 这样不会丢数据. 因为要求 leader 接收到消息, 所有的 follower 都同步之后才认为本次写成功. 如果没有成功会不断重试.

Kafka

某个 leader partition 宕机, 其它 follower partition 还没来得及同步, 这时候会导致部分数据丢失.

一般设置4个参数.

  • 每个 partition 至少2个副本. topic 设置 replication.factor 参数.
  • min.insync.replicas 参数. 这个值大于1, 要求 leader 至少感知有一个 follower 还跟自己保持联系, 确保宕机之后还有一个 follower.
  • producer 端设置 ack=all, 要求每条数据, 必须写入所有 replica 之后, 才能认为是写成功了.
  • producer 端设置 retries=MAX, 要求一旦写入失败就无限重试, 卡在这里.

消费者

默认自动提交 offset, 以为已经消费成功了, 但是其实消费者还没开始处理, 这时候就自己就挂了. 所以关闭自动提交 offset, 手动提交.

MQ重复消费

如何保证消费时的幂等性, 也就是避免重复消费问题.

MQ 都可能出现这个问题.

Kafka 通过 offset 解决这个问题, 每次消息都有一个 offset, 由 Kafka 分配, 代表这个消息的序号.

而消费者是按照这个顺序进行消费的. 消费完会提交 offset, 告诉 Kafka 已经消费完了(基于 Zookeeper).

消费者不是消费完一条数据就提交 offset 的, 而是定时定期提交的. 所以, 如果有消费者准备提交而未提交时, 进程被重启了, 这时候已经消费的 offset 并没有提交, Kafka 是不知道的。

保证幂等性, 数据重复出现多次, 但是只有一个数据被写入.

  • 写库. 先判定是否存在, 可以放在内存中, 没有必要穿透到数据库, 如果存在就 update.
  • 如果写的是redis的set结构, 那没问题, 天然幂等性.
  • 基于数据库的唯一键来保证重复数据不会重复插入, 出现脏数据.

MQ顺序消费

  • RabbitMQ: 一个 producer , 多个 consumer. 并行执行, 顺序乱掉.
  • Kafka: 一个 topic, 一个 partition, 一个 consumer, 内部多线程.

RabbitMQ顺序错乱的场景

RabbitMQ 顺序错乱场景

RabbitMQ 保证顺序一致, 一个 queue 对应 一个 consumer.

Kafka顺序错乱场景

写入一个 partition 的数据是一定有顺序的. producer 在写的时候, 可以指定 key, 这样数据就会被分到一个 partition 中, 而且这个 partition 中的数据是有序的.

一个 partition 对应一个 consumer.

consumerpartition 中取出数据一定是有序的. 如果消费者是使用多线程的, 也会导致消费不是顺序的.

Kafka 保证消息的顺序性, 使用内存队列, 同一key的数据放在一个内存队列, 让同一线程进行处理.

如果是单线程, 吞吐量太低了, 消费者使用多线程提升明显.

MQ积压消息

TODO 消息积压问题

设计一个消息队列

  • 首先要支持可伸缩性(弹性扩容), 可以增加吞吐量和容器, 那就设计成分布式系统. 像 kafka 那样, broker -> topic -> partition, 每个 partition 存一部分数据. 如果资源不够了, 就给 topic 添加 partition, 然后做数据迁移.
  • 是否要持久化, 要不要落地磁盘, 如果需要, 因为是有状态的, 需要保存, 就可以使用顺序写, 这样就没有磁盘随机读写的选址开销.
  • 可用性. kafka 的 replica 机制 -> leader & follower -> broker 挂掉的话重新选举 leader, 恢复对外服务.
  • 数据丢失问题.保证数据0丢失, 参考 kafka 的方案.

评论