面经系列之 -- 消息中间件

324 阅读15分钟

消息确认机制

RabbitMq消费者的消息确定机制:

       NONE:无应答,rabbitmq默认consumer正确处理所有请求。

       AUTO:consumer自动应答,处理成功(注意:此处的成功确认是没有发生异常)发出ack,处理失败发出nack。rabbitmq发出消息后会等待consumer端应答,只有收到ack确定信息后才会将消息在rabbitmq清除掉。收到nack异常信息的处理方法由setDefaultRequeueReject()方法设置,这种模式下,发送错误的消息可以恢复。

       MANUAL:基本等同于AUTO模式,区别是需要人为调用方法确认

集群消费与广播消费

默认集群消费

       集群模式:一个ConsumerGroup中的Consumer实例根据队列分配策略算法为Consumer分配队列,平均分摊(默认)消费消息。

  • 平均分配策略
  • 环行分配策略
  • 手动分配策略
  • 机房分配策略
  • 一致性Hash

广播消费

       广播消费:一条消息被多个consumer消费,即使这些consumer属于同一个ConsumerGroup,消息也会被ConsumerGroup中的每个Consumer都消费一次,广播消费中ConsumerGroup概念可以认为在消息划分方面无意义。

       广播消费,订阅该 Topic 的消息者们都会消费每个消息。集群消费,订阅该 Topic 的消息者们只会有一个去消费某个消息

       广播消费与集群消费在消息落盘区别:具体表现在消息消费进度的保存上。
广播消费,由于每个消费者都独立的去消费每个消息,因此每个消费者各自保存自己的消息消费进度。

       集群消费下,订阅了某个 Topic,而旗下又有多个 MessageQueue,每个消费者都可能会去消费不同的 MessageQueue,因此总体的消费进度保存在 Broker 上集中的管理

RocketMq零拷贝

消息者消费消息的时候用到了零拷贝原理;

  1. 使用 mmap + write 方式 (RocketMQ选择的方式:因为有小块数据传输的需求,效果会比 sendfile 更好) 优点:即使频繁调用,使用小块文件传输,效率也很高; 缺点:不能很好的利用 DMA 方式,会比 sendfile 多消耗CPU,内存安全性控制复杂,需要避免 JVM Crash 问题。
  2. 使用 sendfile 方式 优点:可以利用 DMA 方式,消耗 CPU 较少,大块文件传输效率高,无内存安全新问题; 缺点:小块文件效率低亍 mmap 方式,只能是 BIO 方式传输,不能使用 NIO。

消息百分百投递

  • transaction和confirm模式来确保生产者不丢消息。

  • 消息落库,对消息状态进行打标

  • 消息的延迟投递,做二次确认,回调检查

  • 事务方式

  • 事务方式发布消息,性能太差,往往不采用事务的方式发布消息,建议采用异步发送确认的方式

    1. channel.txSelect() 声明启动事务模式
    2. channel.txCommit() 提交事务
    3. channel.txRollback()回滚事务

    • 发布确认

消息堆积

  • concurrentConsumers
  • prefetchCount
  • concurrentConsumers设置的是对每个listener在初始化的时候设置的并发消费者的个数。
  • prefetchCount是每次一次性从broker里面取的待消费的消息的个数。prefetchCount是BlockingQueueConsumer内部维护的一个阻塞队列LinkedBlockingQueue的大小,其作用就是如果某个消费者队列阻塞,就无法接收新的消息,该消息会发送到其它未阻塞的消费者 如果我们想增大消费者的速度,可以通过配置者两个参数来进行
spring.rabbitmq.listener.simple.concurrency: 最小的消费者数量
spring.rabbitmq.listener.simple.max-concurrency: 最大的消费者数量
spring.rabbitmq.listener.simple.prefetch: 指定一个请求能处理多少个消息,如果有事务的话,必须大于等于transaction数量

顺序消息

       顺序消息(FIFO 消息)是 MQ 提供的一种严格按照顺序进行发布和消费的消息类型。顺序消息由两个部分组成:顺序发布和顺序消费。主要就是把topic的分区数设置为1,来达到单线程发送消息。 顺序消息包含两种类型:

  • 分区顺序:一个Partition内所有的消息按照先进先出的顺序进行发布和消费
  • 全局顺序:一个Topic内所有的消息按照先进先出的顺序进行发布和消费
  • 顺序消息的发送必须是单线程,多线程将不在保证有序。

在MQ的模型中,顺序需要由3个阶段去保障:

  1. 消息被发送时保持顺序
  2. 消息被存储时保持和发送的顺序一致
  3. 消息被消费时保持和存储的顺序一致

延时消息

       RabbitMQ本身是不支持延迟消息功能的,一般的做法,是通过最大生存时间(Time-To-Live)和死信交换机(Dead Letter Exchanges)两个特性模拟出延迟消息的功能。消息超过最大生存时间没有被消费就变成一条死信,便会被重新投递到死信交换机,然后死信交换机根据绑定规则转发到对应的死信队列上,监听该队列就可以让消息被重新消费。

       在RocketMQ中,支持延迟消息,但是不支持任意时间精度的延迟消息,只支持特定级别的延迟消息。如果要支持任意时间精度,不能避免在Broker层面做消息排序,再涉及到持久化的考量,那么消息排序就不可避免产生巨大的性能开销。

       消息延迟级别分别为1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h,共18个级别。在发送消息时,设置消息延迟级别即可,设置消息延迟级别时有以下3种情况:

  1. 设置消息延迟级别等于0时,则该消息为非延迟消息。
  2. 设置消息延迟级别大于等于1并且小于等于18时,消息延迟特定时间,如:设置消息延迟级别等于1,则延迟1s;设置消息延迟级别等于2,则延迟5s,以此类推。
  3. 设置消息延迟级别大于18时,则该消息延迟级别为18,如:设置消息延迟级别等于20,则延迟2h。
  4. 实现原理是交由broker定时投递任务发送的,基于定时任务发送的,任务调度。

事务消息

       用户发送一个Half消息到broker,broker设置queueOffset=0;即对消费者不可见。        用户本地消息处理成功,发送一个commit消息到broker,broker蛇者queueOffset为正常值,达到重新投递的目的,此时消费者可以正常消费;如果本地事务处理失败,则发送一个rollback给到broker,broker将删除消息。如果生产者忘记投递commit或者rollback消息,broker会定期会查生产者,确定本地事务的执行状态,在决定Half消息的删除还是提交或者回滚。 在这里插入图片描述

RocketMQ

       RocketMQ 整体设计和其他的 MQ 类似,除了 Producer、Consumer,还有 NameServer 和 Broker。 在这里插入图片描述        NameServer 存储了 Topic 和 Broker 的信息,主要功能是管理 Broker,以及进行消费的路由信息管理。

       在服务器启动时,节点会注册到 NameServer 上,通过心跳保持连接,并记录各个节点的存活状态;除此之外,NameServer 还记录了生产者和消费者的请求信息,结合消息队列的节点信息,实现消息投递的负载均衡等功能。

       RocketMQ 的 Broker 和 Kafka 类似,Broker 是消息存储的承载,作为客户端请求的入口,可以管理生产者和消费者的消费情况。

       Broker 集群还承担了消息队列高可用的责任,它可以扩展副本机制,通过主从节点间的数据同步保证高可用,这一点和 Kafka 的分区副本机制非常类似。

       Broker 可以进行横向扩展——如果消息队列集群不能满足目前的业务场景,那么可以增加新的机器,扩展 Broker 集群。新的 Broker 节点启动以后,会注册到 NameServer 上,集群中的生产者和消费者通过 NameServer 感知到新的节点,接下来就可以进行消息的发布和消费。

       和其他的消息队列不同,RocketMQ 还支持 Tag,Tag 是对 Topic 的进一步扩展,可以理解为一个子主题。有了 Tag,在进行消息队列的主题划分时,可以把一个业务模块的消息进一步拆分,使其更加灵活。

RabbitMQ 基本概念

在这里插入图片描述

  1. Message
  2. Publisher
  3. Exchange
  4. Binding
  5. Queue
  6. Connection
  7. Channel
  8. Consumer
  9. Virtual Host
  10. Broker

交换机种类

• fanout:广发,都可以收到
• dircat:点对点,一对一
• topic:匹配符
• header

消息持久化

RabbitMQ:

  • 交换器的持久化

    交换器的持久化是在声明交换器的时候,将durable设置为true。如果交换器不设置持久化,那么在RabbitMQ交换器服务重启之后,相关的交换器信息会丢失,不过消息不会丢失,但是不能将消息发送到这个交换器。

  • 队列对持久化

    队列的持久化在声明队列的时候,将durable设置为true。如果队列不设置持久化,那么RabbitMQ交换器服务重启之后,相关的队列信息会丢失,同时队列中的消息也会丢失。

  • 消息的持久化

    消息的持久化是在BasicProperties中设置deliveryMode设置为2。队列的持久化能保证本身的元数据不会因为异常而丢失,但是不能保证内部所存在的消息不会丢失。要确保消息不丢失,需要将消息持久化。

    • exchange 持久化,在声明时指定 durable 为 true
    • queue 持久化,在声明时指定 durable 为 true
    • message 持久化,在投递时指定 delivery_mode=2(1是非持久化)

RocketMQ的集群

针对Broker做的集群部署,分为5种:

  1. 单Master,单机节点,master挂了,集群不可用
  2. 单master,单salve,master挂了之后,不可写,但可以读
  3. 多master,无salve,多master,一个挂了,其他顶上,不影响使用,性能最好
  4. 多master,多salve,异步复制
  5. 多master,多salve,同步复制

消息同步:

  • 同步复制
  • 生产者等待同步复制成功后,才会返回生产者发送消息成功
  • 异步复制
  • 生产者不用等待,直接返回成功消息,采用最终一致性思想解决

NameSrv

       主要用来保存元数据,提高可用性,高可用是基于NameSrv集群实现。主要功能是临时保存、管理Topic路由信息。各个nameSrv之间是无状态的,不能进行通讯,互相不知道彼此的存在。

       Broker在启动时,把自己的元数据信息上报给nameSrv,也叫做topic路由。

Broker

       broker在rocketMQ中主要起一个存储的作用,主要负责处理各种TCP请求以及消息存储。
broker分为Master和Salve,master主要提供服务,salve主要在master宕机之后提供消费服务。

       broker类似于zookeeper,信息保存在不同的文件路劲下

  1. commitLog
  2. consumequeue

       包含该broker上所有的topic对应的消费队列文件信息。每个消费队列相当于一个commitlog的一个索引,提供给消费者做拉取消息,更新位点使用

  1. Index File

    该目录下的全部的文件都是按照消息key创建的hash索引,文件名是按照创建时的时间戳命名的

  2. config

    保存当前broker中全部的topic,订阅关系和消费进度,这些数据broker会定时的从内存持久化到磁盘,以防宕机后恢复

  3. abort

    记录是否关闭异常,正常关闭是文件会被删除,异常关闭则不会删除,当broker启动时主要检测该文件是否存在,在决定是否需要重新构建index索引

  4. checkpoint

    主要记录最近一些时间做的操作,比如最后一次刷盘的时间

文件过期删除需要满足三个条件
  • 当前时间等于已经配置的删除时间
  • 磁盘使用空间超过85%
  • 手动执行删除

如何防止数据丢失?

主要基于三个维度来讲:

  • 生产者 producer

    生产者发送一条消息到消息中间件,基于confim机制,同步的方式,收到确认ok才认为发送成功,失败则重试2次,还不行则任务发送失败;

  • broker

           消息支持持久化到Commitlog里面,即使宕机后重启,未消费的消息也是可以加载出来的。

            Broker集群支持 1主N从的策略,支持同步复制和异步复制的方式,同步复制可以保证即使Master 磁盘崩溃,消息仍然不会丢失。

    Broker自身支持同步刷盘、异步刷盘的策略,可以保证接收到的消息一定存储在本地的内存中。

           主要基于三个文件,commitlog,consumerQueue,indexFile来进行持久化,最终可以在config中可以查看到对应的消费进度,订阅关系得等,如果宕机或者关机,可以基于abort文件和checkpoint文件来进行判断如何回滚。

  • 消费者

           基于ack机制,主要有手动ack和自动ack来确认,是否消费成功,失败的可以把消息发送给broker。

           consumequeue:consumequeue目录下是以Topic作为文件名称,每个Topic下又以queue id作为文件夹分组,用于记录每个消息在commitlog目录下文件中消息的位置,consumequeue目录下文件中记录的内容格式为 queueoffset/size/tag

           index:消息索引文件信息,根据topic和tag名称生成哈希键,值为消息在commitlog的偏移量。

           config:主要记录一些消费者的配置信息

同步刷盘、异步刷盘

  1. 异步刷盘方式:在返回写成功状态时,消息可能只是被写入了内存的PAGECACHE,写操作的返回快,吞吐量大;当内存里的消息量积累到一定程度时,统一触发写磁盘操作,快速写入 优点:性能高 缺点:Master宕机,磁盘损坏的情况下,会丢失少量的消息, 导致MQ的消息状态和生产者/消费者的消息状态不一致
  2. 同步刷盘方式:在返回应用写成功状态前,消息已经被写入磁盘。具体流程是,消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,给应用返回消息写成功的状态。

       优点:可以保持MQ的消息状态和生产者/消费者的消息状态一致

       缺点:性能比异步的低

同步复制、异步复制  

       如果一个broker组有Master和Slave,消息需要从Master复制到Slave上,有同步和异步两种复制方式。

  1. 同步复制方式:等Master和Slave均写成功后才反馈给客户端写成功状态

       优点:如果Master出故障,Slave上有全部的备份数据,容易恢复,消费者仍可以从Slave消费, 消息不丢失

       缺点:增大数据写入延迟,降低系统吞吐量,性能比异步复制模式略低,大约低10%左右,发送单个Master的响应时间会略高

  1. 异步复制方式:只要Master写成功即可反馈给客户端写成功状态

       优点:系统拥有较低的延迟和较高的吞吐量. Master宕机之后,消费者仍可以从Slave消费,此过程对应用透明,不需要人工干预,性能同多个Master模式几乎一样

       缺点:如果Master出了故障,有些数据因为没有被写入Slave,而丢失少量消息。