RabbitMQ

保证消息不丢失

生产者确认机制: 消息发送到 MQ 后会返回一个结果给发送者, 表示消息是否处理成功.

消息失败后处理方法:

  • 回调方法即时重发
  • 记录日志
  • 保存到数据库然后定时重发, 成功发送后即时删除表中的数据

消息持久化, 默认内存存储消息, 开启持久化确保缓存在 mq 中的消息不丢失:

  • 交换机持久化
  • 队列持久化
  • 消息持久化, SpringAMQP 中的消息默认时持久的, 可以通过MessageProperties中的DeliveryMode指定

消费者确认: 消费者处理消息后向 mq 发送 ack, mq 接受到 ack 后才会删除该消息, SpringAMQP 有三种确认模式:

  • manual: 手动 ack, 业务代码结束后调用 api 发送 ack
  • auto: 自动 ack, 由 spring 检测 listen 代码是否出现异常, 没有异常返回 ack, 否则返回 nack
  • none: 关闭 ack, mq 假定消费者获取消息后会成功处理, 消息投递后立即被删除

利用重试机制, 消费者出现异常时重试. 达到重试次数后, 如果依然失败则将消息投递到异常交换机, 人工介入处理.

消息重复消费

  • 每条消息设置一个唯一标识 id
  • 加锁: 分布式锁, 数据库锁

延迟队列

延迟队列 = TTL + 死信交换机

消息成为死信的条件:

  • 消费者使用basic.reject或者basic.nack声明消费失败, 并且消息的requeue设为false
  • 消息时一个过期消息, 超时无人消费
  • 要投递的队列消息堆积满了, 最早的消息可能成为死信

队列配置了dead-letter-exchange属性, 制定了一个交换机, 则队列中的死信就会投递到这个交换机种, 这个交换机被称为死信交换机 (DLX)

ttl: 一个队列中的消息 ttl 结束仍未消费, 则会变为死信, ttl 超时的两种情况:

  • 消息所在队列设置了存活时间
  • 消息本身设置了存活时间

或者安装延迟队列插件使用延迟队列

消息堆积解决

三种方案:

  • 增加更多消费者, 提高消费速度
  • 在消费者内开启线程池加快消息处理速度
  • 扩大队列容积, 提高堆积上限, 使用惰性队列

惰性队列:

  • 接收到消息后直接存入到磁盘中而非内存
  • 消费者要消费消息时才会从磁盘中读取并加载到内存
  • 支持数百万条的消息存储

高可用机制

普通集群

标准集群, 会在集群的各个节点之间共享部分数据, 包括: 交换机, 队列元信息. 不包含队列中的消息.

  • 当访问集群中某节点时, 如果队列不在该节点, 会从数据所在节点传递到当前节点并返回.
  • 队列所在节点宕机, 队列中的消息就会丢失.

一个节点宕机就不可用, 没有高可用

镜像集群

主从模式:

  • 交换机, 队列, 队列中的消息会在各个 mq 镜像节点之间同步备份
  • 创捷队列的节点被称为该队列的主节点, 备份到的其他节点叫做该队列的镜像节点
  • 一个队列的主节点可能是另一个队列的镜像节点
  • 所有操作都是在主节点完成, 然后同步给镜像节点
  • 主节点宕机后, 镜像节点会代替成为新的主节点

同步期间宕机会丢失数据

仲裁队列

用来代替镜像队列:

  • 与镜像队列一样, 主从模式, 支持主从数据同步
  • 主从同步基于 Raft 协议, 强一致性

Kafka

保证消息不丢失

生产者发送消息到 broker 丢失:

  • 设置异步发送, 接受异常记录日志并处理
  • 消息重试

消息在 broker 中存储丢失:

  • 发送确认机制 acks

消费者从 broker 接收消息丢失:

kafka 分区机制: 将每个主题划分为多个分区 (partition)

topic 分区中的消息只能由消费者组中的唯一一个消费者处理, 不同分区分配给同一消费者组的不同消费者

关闭自动提交, 使用手动提交避免消息没有消费/重复消费:

同步+异步提交:

保证消费顺序性

一个 topic 有多个分区, 多个分区无法保证顺序性, 但是分区内偏移量顺序递增, 所以可以:

  • 发送消息时指定分区号
  • 发送消息时按照相同的业务设置相同的 key, kafka 会对 key 哈希来指定分区号

高可用机制

集群: 多个 broker 组成一个 kafka 集群

复制机制:

  • 一个 topic 有多个分区, 每个分区有多个副本, 一个 leader, 其余都是 follower, 副本存储在不同的 broker 中
  • 所有分区副本内容相同, leader 故障时会将一个 follower 提升为 leader
  • follower 有 ISR(in-sync replica, 同步复制) 和普通副本 (异步复制), 优先从 ISR 中选取副本作为 leader

数据清理机制

存储结构:

  • topic 数据存储在分区上, 分区文件过大就会分段存储
  • 每个分段都在磁盘上以索引(xxx.index)和日志文件(xxx.log)形式存储

分段好处:

  • 减少单个文件内容大小,查找数据方便
  • 便于 Kafka 进行日志清理

日志清理策略:

  • 根据消息的保留时间,消息保存时间超过指定时间(默认 168 小时)就会触发清理
  • 根据 topic 存储的数据大小,当日志文件大于阈值则会开始删除最久的消息(默认关闭)

高性能设计

  • 消息分区:存储不受单台服务器限制,处理更多数据
  • 顺序读写:磁盘顺序读写,提高读写效率
  • 页缓存:把磁盘中的数据缓存到内存中,把对磁盘的访问变为对内存的访问
  • 零拷贝:减少上下文切换以及数据拷贝
  • 消息压缩:减少磁盘 IO 和网络 IO
  • 分批发送:将消息打包批量发送,减少网络开销

减少了 页缓存 -> Kafka -> socket 缓存 -> 网卡 的过程