消息队列

1、消息队列的基本作用?

消息队列的主要作用是:解耦、异步、削峰。

  • 解耦

A 系统通过接口调用发送数据到 B、C、D 三个系统。那如果现在 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?现在 A 系统又要发送第二种数据了呢?这样的话 A 系统的维护成本就非常的高,而且 A 系统要时时刻刻考虑B、C、D、E 四个系统如果出现故障该怎么办?A 系统是重发还是先把消息保存起来呢?使用消息队列就可以解决这个问题。A 系统只负责生产数据,不需要考虑消息被哪个系统来消费。

  • 异步

A 系统需要发送个请求给 B 系统处理,由于 B 系统需要查询数据库花费时间较长,以至于 A 系统要等待 B 系统处理完毕后再发送下个请求,造成 A 系统资源浪费。使用消息队列后,A 系统生产完消息后直接丢进消息队列,不用等待 B 系统的结果,直接继续去干自己的事情了。

  • 削峰

A 系统调用 B 系统处理数据,每天 0 点到 12 点,A 系统风平浪静,每秒并发请求数量就 100 个。结果每次一到 12 点 ~ 13 点,每秒并发请求数量突然会暴增到 1 万条。但是 B 系统最大的处理能力就只能是每秒钟处理 1000 个请求,这样系统很容易就会崩掉。这种情况可以引入消息队列,把请求数据先存入消息队列中,消费系统再根据自己的消费能力拉取消费。

2、消息队列的优缺点有哪些?

  • 优点

消息队列的优点就是:解耦、异步、削峰。

  • 缺点
  1. 降低系统的可用性:系统引入的外部依赖越多,越容易挂掉;

  2. 系统复杂度提高:使用 MQ 后可能需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题;

  3. 一致性问题:A 系统处理完了直接返回成功了,但问题是:要是 B、C、D 三个系统那里,B 和 D 两个系统写库成功了,结果 C 系统写库失败了,就造成数据不一致了。

3、如何保证消息队列的高可用?

根据不同的 MQ 或者你用过的 MQ 进行回答:

  • RabbitMQ:镜像集群模式

RabbitMQ 是基于主从做高可用性的,Rabbitmq有三种模式:单机模式、普通集群模式、镜像集群模式。单机模式一般在生产环境中很少用,普通集群模式只是提高了系统的吞吐量,让集群中多个节点来服务某个 Queue 的读写操作。那么真正实现 RabbitMQ 高可用的是镜像集群模式。

镜像集群模式跟普通集群模式不一样的是,创建的 Queue,无论元数据还是Queue 里的消息都会存在于多个实例上,然后每次你写消息到 Queue 的时候,都会自动和多个实例的 Queue 进行消息同步。这样设计,好处在于:任何一个机器宕机不影响其他机器的使用。坏处在于:1. 性能开销太大:消息同步所有机器,导致网络带宽压力和消耗很重;2. 扩展性差:如果某个 Queue 负载很重,即便加机器,新增的机器也包含了这个 Queue 的所有数据,并没有办法线性扩展你的 Queue。

  • Kafka:partition 和 replica 机制

Kafka 基本架构是多个 broker 组成,每个 broker 是一个节点。创建一个 topic 可以划分为多个 partition,每个 partition 可以存在于不同的 broker 上,每个 partition 就放一部分数据,这就是天然的分布式消息队列。就是说一个 topic 的数据,是分散放在多个机器上的,每个机器就放一部分数据。

Kafka 0.8 以前,是没有 HA 机制的,任何一个 broker 宕机了,它的 partition 就没法写也没法读了,没有什么高可用性可言。

Kafka 0.8 以后,提供了 HA 机制,就是 replica 副本机制。每个 partition 的数据都会同步到其他机器上,形成自己的多个 replica 副本。然后所有 replica 会选举一个 leader 出来,生产和消费都跟这个 leader 打交道,然后其他 replica 就是 follower。写的时候,leader 会负责把数据同步到所有 follower 上去,读的时候就直接读 leader 上数据即可。Kafka 会均匀的将一个 partition 的所有 replica 分布在不同的机器上,这样才可以提高容错性。

4、如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性?

要保证消息不被重复消费,其实就是要保证消息消费时的幂等性。幂等性:无论你重复请求多少次,得到的结果都是一样的。例如:一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性。

那么如何保证幂等性呢?

  1. 写数据时,先根据主键查一下这条数据是否存在,如果已经存在则 update;

  2. 数据库的唯一键约束也可以保证不会重复插入多条,因为重复插入多条只会报错,不会导致数据库中出现脏数据;

  3. 如果是写 redis,就没有问题,因为 set 操作是天然幂等性的。

5、如何保证消息的可靠性传输?或者说,如何处理消息丢失的问题?

  • RabbitMQ

image-20210608163015583

  • Kafka

image-20210608163054065

6、如何保证消息的顺序性?

  • RabbitMQ

拆分多个 Queue,每个 Queue一个 Consumer,就是多一些 Queue 而已,确实是麻烦点;或者就一个 Queue 但是对应一个 Consumer,然后这个 Consumer 内部用内存队列做排队,然后分发给底层不同的 Worker 来处理。

  • Kafka
  1. 一个 Topic,一个 Partition,一个 Consumer,内部单线程消费,单线程吞吐量太低,一般不会用这个。

  2. 写 N 个内存 Queue,具有相同 key 的数据都到同一个内存 Queue;然后对于 N 个线程,每个线程分别消费一个内存 Queue 即可,这样就能保证顺序性。

7、大量消息在 MQ 里长时间积压,该如何解决?

具体可以看这里:https://www.jianshu.com/p/5f4b3a520719

一般这个时候,只能临时紧急扩容了,具体操作步骤和思路如下:

  1. 先修复 consumer 的问题,确保其恢复消费速度,然后将现有 consumer 都停掉;

  2. 新建一个 topic,partition 是原来的 10 倍,临时建立好原先 10 倍的 queue 数量;

  3. 然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的 10 倍数量的 queue;

  4. 接着临时征用 10 倍的机器来部署 consumer,每一批 consumer 消费一个临时 queue 的数据。这种做法相当于是临时将 queue 资源和 consumer 资源扩大 10 倍,以正常的 10 倍速度来消费数据;

  5. 等快速消费完积压数据之后,得恢复原先部署的架构,重新用原先的 consumer 机器来消费消息。

8、MQ 中的消息过期失效了怎么办?

具体可以看这里:https://www.jianshu.com/p/5f4b3a520719

假设你用的是 RabbitMQ,RabbtiMQ 是可以设置过期时间的,也就是 TTL。如果消息在 Queue 中积压超过一定的时间就会被 RabbitMQ 给清理掉,这个数据就没了。这时的问题就不是数据会大量积压在 MQ 里,而是大量的数据会直接搞丢。这个情况下,就不是说要增加 Consumer 消费积压的消息,因为实际上没啥积压,而是丢了大量的消息。

我们可以采取一个方案,就是批量重导。就是大量积压的时候,直接丢弃数据了,然后等过了高峰期以后开始写程序,将丢失的那批数据一点一点的查出来,然后重新灌入 MQ 里面去,把丢的数据给补回来。

9、RabbitMQ 有哪些重要的角色?

RabbitMQ 中重要的角色有:生产者、消费者和代理。

  1. 生产者:消息的创建者,负责创建和推送数据到消息服务器;

  2. 消费者:消息的接收方,用于处理数据和确认消息;

  3. 代理:就是 RabbitMQ 本身,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。

10、RabbitMQ 有哪些重要的组件?

  1. ConnectionFactory(连接管理器):应用程序与 rabbit 之间建立连接的管理器,程序代码中使用;

  2. Channel(信道):消息推送使用的通道;

  3. Exchange(交换器):用于接受、分配消息;

  4. Queue(队列):用于存储生产者的消息;

  5. RoutingKey(路由键):用于把生成者的数据分配到交换器上;

  6. BindingKey(绑定键):用于把交换器的消息绑定到队列上。

11、RabbitMQ 有几种广播类型?

RabbitMQ 有三种广播模式:fanout、direct、topic。

  1. fanout:所有 bind 到此 exchange 的 queue 都可以接收消息;很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout 交换机转发消息是最快的。

  2. direct:通过 routingKey 和 exchange 中的 bindingKey 决定的那个唯一的 queue 可以接收消息;

  3. topic:所有符合 routingKey 所 bind 的 queue 可以接收消息。

12、Kafka 可以脱离 zookeeper 单独使用吗?为什么?

Kafka 不能脱离 zookeeper 单独使用,因为 Kafka 使用 zookeeper 管理和协调 Kafka 的节点服务器。

13、Kafka 有几种数据保留的策略?

Kafka 有两种数据保存策略:按照过期时间保留和按照存储的消息大小保留。

14、Kafka 的分区策略有哪些?

所谓分区策略就是决定生产者将消息发送到哪个分区的算法。

  1. 轮询策略:默认的分区策略,非常优秀的负载均衡表现,它总是能保证消息最大限度地被平均分配到所有分区上;

  2. 随机策略:实现随机策略版的 partition 方法;

  3. 按消息键保序策略:也称 Key-Ordering 策略,可以保证同一个 Key 的所有消息都进入到相同的分区里,由于每个分区下的消息处理是有顺序的,所以称之为消息键保序策略;

  4. 自定义分区策略:在编写生产者程序时,你可以编写一个具体的类实现org.apache.kafka.clients.producer.Partitioner 接口。这个接口也很简单,只定义了两个方法:partition() 和 close(),通常只用实现 partition() 方法即可。同时还需要设置 partitioner.class 参数为你自己实现类的全限定类名

发表评论

后才能评论