MQ技术和其技术类型知识点流程化大总结

MQ技术和其技术类型知识点流程化总结

  • 1.为什么使用消息队列?
    • 核心的有 3 个:解耦异步削峰

    • 解耦:

      • 不使用MQ: A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据别的很多系统都需要 A 系统将这个数据发送过来。A 系统要时时刻刻考虑 BCDE 四个系统如果挂了该咋办?要不要重发,要不要把消息存起来?

      在这里插入图片描述

      • 使用MQ进行解耦A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况 。

      在这里插入图片描述

    • 异步:

      • 不使用MQ: A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求,等待个 1s,这几乎是不可接受的

      在这里插入图片描述

      • 使用MQ: 那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms,对于用户而言,其实感觉上就是点个按钮,8ms 以后就直接返回了 。

      在这里插入图片描述

    • 消峰:

      • 不适用MQ分时段会有不同数据量级的请求进入mysql,请求量大的时候,直接能把mysql打爆,少的时候又很正常

      • 使用MQ:系统A从MQ中每秒拉取数据库最高处理条数的请求(假设两千),那么数据库就算在高峰期也不会因为请求数据量太大而宕机。整个高峰时段的请求积压在MQ中也是没事的因为高峰期很短,等高峰期已过,但是数据拉取量不变,那么很快就会将沉积的请求数据全部消解。

      在这里插入图片描述

  • MQ有什么缺点:

    • 系统可用性降低:若MQ损坏,就会导致整个系统崩溃。

    在这里插入图片描述

    • 系统复杂度提高:维护MQ的复杂性。

    • 一致性问题A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了

  • Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点

在这里插入图片描述

  • MQ: 一般的业务系统要引入 MQ,最早大家都用ActiveMQ,但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证(可能丢消息),社区也不是很活跃。

  • RabbitMQ: RabbitMQ,但是确实人家是开源的,比较稳定的支持,活跃度也高 ,主要是有后台管理界面,特别方便管理

  • RocketMQ大型公司,基础架构研发实力较强,用 RocketMQ 是很好的选择,阿里开源的。

  • Kafka: 如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的

  • 2.如何保证消息队列的高可用性:
    • RabbitMQ 的高可用性:RabbitMQ 是比较有代表性的,因为是基于主从(非分布式)做高可用性的 。

      • 普通集群模式(无高可用性)

        • 原理:意思就是在多台机器上启动多个 RabbitMQ 实例,每个机器启动一个。你创建的 queue,只会放在一个 RabbitMQ 实例上,但是每个实例都同步 queue 的元数据(元数据可以认为是 queue 的一些配置信息,通过元数据,可以找到 queue 所在实例)。你消费的时候,实际上如果连接到了另外一个实例,那么那个实例会从 queue 所在实例上拉取数据过来 。
        • 缺点:1.你要么消费者每次随机连接一个实例然后拉取数据,要么固定连接那个 queue 所在实例消费数据,前者有数据拉取的开销,后者导致单实例性能瓶颈
        • 2.而且如果那个放 queue 的实例宕机了,会导致接下来其他实例就无法从那个实例拉取,如果你开启了消息持久化,让 RabbitMQ 落地存储消息的话,消息不一定会丢,得等这个实例恢复了,然后才可以继续从这个 queue 拉取数据
        • 3.这方案主要是提高吞吐量的,就是说让集群中多个节点来服务某个 queue 的读写操作

    在这里插入图片描述

    • 镜像集群模式(高可用性)

      • 原理: queue的元数据和消息都会存在于多个实例上,就是说,每个 RabbitMQ 节点都有这个 queue 的一个完整镜像,然后每次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上。

      • 开启方式: 在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候是可以要求数据同步到所有节点的,也可以要求同步到指定数量的节点,再次创建 queue 的时候,应用这个策略,就会自动将数据同步到其他的节点上去了

      • 优点:1. 任何一个机器宕机了,没事儿,其它机器(节点)还包含了这个 queue 的完整数据,别的 consumer 都可以到其它节点上去消费数据

      • 缺点:1. 这个性能开销也太大,消息需要同步到所有机器上,导致网络带宽压力和消耗很重

      • 2.不是分布式的,就没有扩展性可言了,如果某个 queue 负载很重,你加机器,新增的机器也包含了这个 queue 的所有数据,并没有办法线性扩展你的 queue。你想,如果这个 queue 的数据量很大,这个机器上的容量无法容纳了

      在这里插入图片描述

    • Kafka的高可用性

      • 原理: Kafka 一个最基本的架构认识:由多个 broker 组成,每个 broker 是一个节点;你创建一个 topic,这个 topic 可以划分为多个 partition每个 partition 可以存在于不同的 broker 上,每个 partition 就放一部分数据

        • 这就是天然的分布式消息队列,就是说一个 topic 的数据,是分散放在多个机器上的,每个机器就放一部分数据

        在这里插入图片描述

        • TopicTopic,由用户定义并配置在Kafka服务器,用于建立生产者和消息者之间的订阅关系:生产者发送消息到指定的Topic下,消费者从这个Topic下消费消息 。

        • Partition:Partition,一个Topic下面会分为很多分区,例如:“kafka-test”这个Topic下可以分为6个分区,分别由两台服务器提供,那么通常可以配置为让每台服务器提供3个分区,假如服务器ID分别为0、1,则所有的分区为0-0、0-1、0-2和1-0、1-1、1-2。Topic物理上的分组,一个 topic可以分为多个 partition,每个 partition 是一个有序的队列partition中的每条消息都会被分配一个有序的 id(offset)
          原文链接:https://blog.csdn.net/yuan_xw/article/details/51210954

        • BrokerKafka的服务器,用户存储消息,Kafa集群中的一台或多台服务器统称为 broker

        • GroupGroup,用于归组同类消费者,在Kafka中,多个消费者可以共同消息一个Topic下的消息,每个消费者消费其中的部分消息,这些消费者就组成了一个分组,拥有同一个分组名称,通常也被称为消费者集群

      • 高可用性机制

        • 1.每个 partition 的数据都会同步到其它机器上,形成自己的多个 replica 副本所有 replica 会选举一个 leader 出来,那么生产和消费都跟这个 leader 打交道,然后其他 replica 就是 follower

        • 2.因为如果某个 broker 宕机了,没事儿,那个 broker上面的 partition 在其他机器上都有副本的。如果这个宕机的 broker 上面有某个 partition 的 leader,那么此时会从 follower 中重新选举一个新的 leader 出来,大家继续读写那个新的 leader 即可。这就有所谓的高可用性了。

        • 3.写数据的时候**,生产者就写 leader**,然后 leader 将数据落地写本地磁盘,接着其他 follower 自己主动从 leader 来 pull 数据。一旦所有 follower 同步好数据了,就会发送 ack 给 leader,leader 收到所有 follower 的 ack 之后,就会返回写成功的消息给生产者

        • 4.消费的时候,只会从 leader 去读,但是**只有当一个消息已经被所有 follower 都同步成功返回 ack 的时候,这个消息才会被消费者读到**

        在这里插入图片描述

  • 3.如何保证消息不被重复消费/幂等性?
    • 重复消费的机制

      • 1.Kafka 每个消息写进去,都有一个 offset,代表消息的序号,然后 consumer 消费了数据之后,每隔一段时间定时定期),会把自己消费过的消息的 offset 提交一下(提交到zookeeper),表示“我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的 offset 来继续消费吧”。

      • 2.如果碰到点着急的,直接 kill 进程了,再重启。这会导致 consumer 有些消息处理了,但是没来得及提交 offset,尴尬了。重启之后,少数消息会再次消费一次.

        在这里插入图片描述

    • 怎么保证幂等性

      • 什么是幂等性: 通俗点说,就一个数据,或者一个请求,给你重复来多次,你得确保对应的数据是不会改变的,不能出错。 (得判断是否消费过这个消息了
        • 1、 拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update .
        • 2、 你是写 Redis,那没问题了,反正每次都是 set,天然幂等性
        • 3、基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据 。
        • 4、 生产者发送每条数据的时候,里面加一个全局唯一的 id,类似订单 id 之类的东西,然后你这里消费到了之后,先根据这个 id 去比如 Redis 里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个 id 写 Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可
  • 4、 RabbitMQ如何处理消息丢失的问题?
    • 1、生产者弄丢了数据

      • 弄丢原因: 生产者将数据发送到 RabbitMQ 的时候,可能数据就在半路给搞丢了,因为网络问题啥的,都有可能

      • 解决方法一:此时可以选择用 RabbitMQ 提供的事务功能,就是生产者发送数据之前开启 RabbitMQ 事务channel.txSelect,然后发送消息,如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务channel.txRollback,然后重试发送消息;如果收到了消息,那么可以提交事务channel.txCommit

      // 开启事务
      channel.txSelect
      try {
          // 这里发送消息
      } catch (Exception e) {
          channel.txRollback
          // 这里再次重发这条消息
      }
      // 提交事务
      channel.txCommit
      
      • 解决方法二: 如果你要确保说写 RabbitMQ 的消息别丢,可以开启 confirm 模式,在生产者那里设置开启 confirm 模式之后,你每次写的消息都会分配一个唯一的 id,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个 ack 消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发

    在这里插入图片描述

    • 两个方式的区别: 事务机制和 confirm 机制最大的不同在于,事务机制是同步的,你提交一个事务之后会阻塞在那儿,但是 confirm 机制是异步的,你发送个消息之后就可以发送下一个消息,然后那个消息 RabbitMQ 接收了之后会异步回调你的一个接口通知你这个消息接收到了

    • 2、RabbitMQ弄丢了数据

      • 解决方案: 这个你必须开启 RabbitMQ 的持久化,就是消息写入之后会持久化到磁盘,哪怕是 RabbitMQ 自己挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢。除非极其罕见的是,RabbitMQ 还没持久化,自己就挂了,可能导致少量数据丢失,但是这个概率较小。
      • 设置持久化的步骤
        • 1、创建 queue 的时候将其设置为持久化,这样就可以保证 RabbitMQ 持久化 queue 的元数据,但是它是不会持久化 queue 里的数据
        • 2、 第二个是发送消息的时候将消息的 deliveryMode 设置为 2
          就是将消息设置为持久化的,此时 RabbitMQ 就会将消息持久化到磁盘上去
        • 3、必须要同时设置这两个持久化才行,RabbitMQ 哪怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据 。
        • 4、持久化可以跟生产者那边的 confirm 机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者 ack 了,所以哪怕是在持久化到磁盘之前,RabbitMQ 挂了,数据丢了,生产者收不到 ack,你也是可以自己重发的 。
    • 3、消费端弄丢了数据

      • 解决方案: 就是你必须关闭 RabbitMQ 的自动 ack,可以通过一个 api 来调用就行,然后每次你自己代码里确保处理完的时候,再在程序里 ack 一把 。

      在这里插入图片描述

  • 5、Kafka怎么处理消息丢失:
    • 1、消费者弄丢了数据

      • 原因: 唯一可能导致消费者弄丢数据的情况,就是说,你消费到了这个消息,然后消费者那边自动提交了 offset,让 Kafka 以为你已经消费好了这个消息,但其实你才刚准备处理这个消息,你还没处理,你自己就挂了,此时这条消息就丢失。
      • 解决方案关闭自动提交 offset,在处理完之后自己手动提交 offset,就可以保证数据不会丢。但是此时确实还是可能会有重复消费,比如你刚处理完,还没提交 offset,结果自己挂了,此时肯定会重复消费一次,需要自己保证幂等性
    • kafka弄丢了数据

      • 原因:Kafka 某个 broker 宕机,然后重新选举 partition 的 leader。大家想想,要是此时其他的 follower 刚好还有些数据没有同步,结果此时 leader 挂了,然后选举某个 follower 成 leader 之后,不就少了一些数据?这就丢了一些数据啊。
      • 解决方案
        • 1、 给 topic 设置 replication.factor 参数:这个值必须大于 1,要求每个 partition 必须有至少 2 个副本
        • 2、 在 Kafka 服务端设置 min.insync.replicas 参数:这个值必须大于 1,这个是要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系,这样才能确保 leader 挂了还有一个follower 。
        • 3、在 producer 端设置 acks=all:这个是要求每条数据,必须是写入所有 replica 之后,才能认为是写成功了
        • 4、在 producer 端设置 retries=MAX(很大很大很大的一个值,无限次重试的意思):这个是要求一旦写入失败,就无限重试
          在这里插入图片描述
    • 生产者不会丢掉数据

      • 如果按照上述的思路设置了 acks=all,一定不会丢,要求是,你的 leader 接收到消息,所有的 follower 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。
  • 6、 RabbitMQ如何保证消息的顺序性?
    • 顺序错乱的原因一个 queue,多个 consumer。比如,生产者向 RabbitMQ 里发送了三条数据,顺序依次是 data1/data2/data3,压入的是 RabbitMQ 的一个内存队列。有三个消费者分别从 MQ 中消费这三条数据中的一条,结果消费者2先执行完操作,把 data2 存入数据库,然后是 data1/data3。这不明显乱了
      在这里插入图片描述
    • 解决方法拆分多个 queue每个 queue 一个 consumer(特异指定),就是多一些 queue 而已,确实是麻烦点;或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理
      在这里插入图片描述
  • 7、 Kafka如何保证消息的顺序性?
    • 原因: 比如说我们建了一个 topic,有三个 partition。生产者在写的时候,其实可以指定一个 key,比如说我们指定了某个订单 id 作为 key,那么这个订单相关的数据,一定会被分发到同一个 partition 中去,而且这个 partition 中的数据一定是有顺序的。
      消费者从 partition 中取出来数据的时候,也一定是有顺序的。到这里,顺序还是 ok 的,没有错乱。

      • 接着,我们在消费者里可能会搞多个线程来并发处理消息。多个线程并发跑的话,顺序可能就乱掉了

      在这里插入图片描述

    • 解决方案:

      • 一个 topic,一个 partition,一个 consumer,内部单线程消费,单线程吞吐量太低,一般不会用这个。
      • N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性
        在这里插入图片描述
  • 8、如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?
    • Kafka大量消息在MQ中积压几个小时
    • 解决方案
      • 1、先修复 consumer 的问题,确保其恢复消费速度,然后将现有 consumer 都停掉
      • 2、新建一个 topic,partition 是原来的 10 倍,临时建立好原先 10 倍的 queue 数量
      • 3、 然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的 10 倍数量的 queue
      • 4、 接着临时征用 10 倍的机器来部署 consumer,每一批 consumer 消费一个临时 queue 的数据。这种做法相当于是临时将 queue 资源和 consumer 资源扩大 10 倍,以正常的 10 倍速度来消费数据
      • 5、 等快速消费完积压数据之后,得恢复原先部署的架构重新用原先的 consumer 机器来消费消息
        在这里插入图片描述
    • (RabbitMQ)MQ中消息过期了
      • 批量重导 : 写个临时程序,一点一点的查出来,然后重新灌入 mq 里面去,把白天丢的数据给他补回来
    • MQ中的数据快满了: 你临时写程序,接入数据来消费,消费一个丢弃一个,都不要了,快速消费掉所有的消息。然后走第二个方案
  • 9、 如果让你写一个消息队列,该如何进行架构设计?
    • 1、首先这个 mq 得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下 kafka 的设计理念
      • broker -> topic -> partition,每个 partition 放一个机器,就存一部分数据。如果现在资源不够了,给 topic 增加 partition,然后做数据迁移,增加机器,存放更多数据,提供更高的吞吐量了。
    • 2、落磁盘才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是 kafka 的思路
    • 3、 多副本 -> leader & follower -> broker 挂了重新选举 leader 即可对外服务
    • 4、 kafka 数据零丢失方案 :6==》kafka弄丢了数据

迁移,增加机器,存放更多数据,提供更高的吞吐量了。

  • 2、落磁盘才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是 kafka 的思路
  • 3、 多副本 -> leader & follower -> broker 挂了重新选举 leader 即可对外服务
  • 4、 kafka 数据零丢失方案 :6==》kafka弄丢了数据
    **参考文章:**https://github.com/Stone-Tree/advanced-java
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值