消息确认机制
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零拷贝
消息者消费消息的时候用到了零拷贝原理;
- 使用 mmap + write 方式 (RocketMQ选择的方式:因为有小块数据传输的需求,效果会比 sendfile 更好)
优点:即使频繁调用,使用小块文件传输,效率也很高;
缺点:不能很好的利用 DMA 方式,会比 sendfile 多消耗CPU,内存安全性控制复杂,需要避免 JVM Crash 问题。 - 使用 sendfile 方式
优点:可以利用 DMA 方式,消耗 CPU 较少,大块文件传输效率高,无内存安全新问题;
缺点:小块文件效率低亍 mmap 方式,只能是 BIO 方式传输,不能使用 NIO。
消息百分百投递
-
transaction和confirm模式来确保生产者不丢消息。
-
消息落库,对消息状态进行打标
-
消息的延迟投递,做二次确认,回调检查
-
事务方式
-
事务方式发布消息,性能太差,往往不采用事务的方式发布消息,建议采用异步发送确认的方式
- channel.txSelect() 声明启动事务模式
- channel.txCommit() 提交事务
- 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个阶段去保障:
- 消息被发送时保持顺序
- 消息被存储时保持和发送的顺序一致
- 消息被消费时保持和存储的顺序一致
延时消息
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种情况:
- 设置消息延迟级别等于0时,则该消息为非延迟消息。
- 设置消息延迟级别大于等于1并且小于等于18时,消息延迟特定时间,如:设置消息延迟级别等于1,则延迟1s;设置消息延迟级别等于2,则延迟5s,以此类推。
- 设置消息延迟级别大于18时,则该消息延迟级别为18,如:设置消息延迟级别等于20,则延迟2h。
- 实现原理是交由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 基本概念
- Message
- Publisher
- Exchange
- Binding
- Queue
- Connection
- Channel
- Consumer
- Virtual Host
- 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种:
- 单Master,单机节点,master挂了,集群不可用
- 单master,单salve,master挂了之后,不可写,但可以读
- 多master,无salve,多master,一个挂了,其他顶上,不影响使用,性能最好
- 多master,多salve,异步复制
- 多master,多salve,同步复制
消息同步:
- 同步复制
- 生产者等待同步复制成功后,才会返回生产者发送消息成功
- 异步复制
- 生产者不用等待,直接返回成功消息,采用最终一致性思想解决
NameSrv
主要用来保存元数据,提高可用性,高可用是基于NameSrv集群实现。
主要功能是临时保存、管理Topic路由信息。各个nameSrv之间是无状态的,不能进行通讯,互相不知道彼此的存在。
Broker在启动时,把自己的元数据信息上报给nameSrv,也叫做topic路由。
Broker
broker在rocketMQ中主要起一个存储的作用,主要负责处理各种TCP请求以及消息存储。
broker分为Master和Salve,master主要提供服务,salve主要在master宕机之后提供消费服务。
broker类似于zookeeper,信息保存在不同的文件路劲下
- commitLog
- consumequeue
• 包含该broker上所有的topic对应的消费队列文件信息。每个消费队列相当于一个commitlog的一个索引,提供给消费者做拉取消息,更新位点使用 - Index File
• 该目录下的全部的文件都是按照消息key创建的hash索引,文件名是按照创建时的时间戳命名的 - config
• 保存当前broker中全部的topic,订阅关系和消费进度,这些数据broker会定时的从内存持久化到磁盘,以防宕机后恢复 - abort
• 记录是否关闭异常,正常关闭是文件会被删除,异常关闭则不会删除,当broker启动时主要检测该文件是否存在,在决定是否需要重新构建index索引 - 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:主要记录一些消费者的配置信息
同步刷盘、异步刷盘
- 异步刷盘方式:在返回写成功状态时,消息可能只是被写入了内存的PAGECACHE,写操作的返回快,吞吐量大;当内存里的消息量积累到一定程度时,统一触发写磁盘操作,快速写入
优点:性能高
缺点:Master宕机,磁盘损坏的情况下,会丢失少量的消息, 导致MQ的消息状态和生产者/消费者的消息状态不一致 - 同步刷盘方式:在返回应用写成功状态前,消息已经被写入磁盘。具体流程是,消息写入内存的PAGECACHE后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,给应用返回消息写成功的状态。
优点:可以保持MQ的消息状态和生产者/消费者的消息状态一致
缺点:性能比异步的低
同步复制、异步复制
如果一个broker组有Master和Slave,消息需要从Master复制到Slave上,有同步和异步两种复制方式。
- 同步复制方式:等Master和Slave均写成功后才反馈给客户端写成功状态
优点:如果Master出故障,Slave上有全部的备份数据,容易恢复,消费者仍可以从Slave消费, 消息不丢失
缺点:增大数据写入延迟,降低系统吞吐量,性能比异步复制模式略低,大约低10%左右,发送单个Master的响应时间会略高 - 异步复制方式:只要Master写成功即可反馈给客户端写成功状态
优点:系统拥有较低的延迟和较高的吞吐量. Master宕机之后,消费者仍可以从Slave消费,此过程对应用透明,不需要人工干预,性能同多个Master模式几乎一样
缺点:如果Master出了故障,有些数据因为没有被写入Slave,而丢失少量消息。