消息中间件

RabbitMQ消息的可靠性传递怎么保证以及消费端如何保证消息不丢失?

在使用 RabbitMQ 的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ 为我们提供了两种方式用来控制消息的投递可靠性模式。

   confirm 确认模式

  return 退回模式

rabbitmq 整个消息投递的路径为: producer--->rabbitmq broker--->exchange--->queue--->consumer

消息从 producer 到 exchange 则会返回一个 confirmCallback 。

消息从 exchange-->queue 投递失败则会返回一个 returnCallback 。

我们将利用这两个 callback 控制消息的可靠性投递。

 

ack指Acknowledge,确认。 表示消费端收到消息后的确认方式。

有三种确认方式:

  自动确认:acknowledge="none"

  手动确认:acknowledge="manual"

  根据异常情况确认:acknowledge="auto",(这种方式使用麻烦,不作讲解)

其中自动确认是指,当消息一旦被Consumer接收到,则自动确认收到,并将相应 message 从 RabbitMQ 的消息缓存中移除。但是在实际业务处理中,很可能消息接收到,业务处理出现异常,那么该消息就会丢失。如果设置了手动确认方式,则需要在业务处理成功后,调用channel.basicAck(),手动签收,如果出现异常,则调用channel.basicNack()方法,让其自动重新发送消息。

 

消息可靠性总结:

持久化

  exchange要持久化

  queue要持久化

  message要持久化

生产方确认Confirm

消费方确认Ack

Broker高可用

 

RabbitMQ消费端限流是怎么做的?

在<rabbit:listener-container> 中配置 prefetch属性设置消费端一次拉取多少消息,

消费端的确认模式一定为手动确认。acknowledge="manual"。

消息成为死信的三种情况?

1. 队列消息长度到达限制;

2. 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;

3. 原队列存在消息过期设置,消息到达超时时间未被消费;

消息幂等性保障

幂等性指一次和多次请求某一个资源,对于资源本身应该具有同样的结果。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。

在MQ中指,消费多条相同的消息,得到与消费该消息一次相同的结果。可以通过乐观锁机制保证。

 

RocketMQ的消息消费者有哪些特性?

负责消费消息,一般是后台系统负责异步消费。一个消息消费者会从Broker服务器拉取消息、并将其提供给应用程序。从用户应用的角度而言提供了两种消费形式:拉取式消费、推动式消费。

  • 拉取式消费的应用通常主动调用Consumer的拉消息方法从Broker服务器拉消息、主动权由应用控制。一旦获取了批量消息,应用就会启动消费过程。

  • 推动式消费模式下Broker收到数据后会主动推送给消费端,该消费模式一般实时性较高。

消费者同样会把同一类Consumer组成一个集合,叫做消费者组,这类Consumer通常消费同一类消息且消费逻辑一致。消费者组使得在消息消费方面,实现负载均衡和容错的目标变得非常容易。要注意的是,消费者组的消费者实例必须订阅完全相同的Topic。RocketMQ 支持两种消息模式:集群消费(Clustering)和广播消费(Broadcasting)。

  • 集群消费模式下,相同Consumer Group的每个Consumer实例平均分摊消息。
  • 广播消费模式下,相同Consumer Group的每个Consumer实例都接收全量的消息。

消息幂等的必要性

在互联网应用中,尤其在网络不稳定的情况下,消息队列 RocketMQ 的消息有可能会出现重复,这个重复简单可以概括为以下情况:

  • 发送时消息重复

    当一条消息已被成功发送到服务端并完成持久化,此时出现了网络闪断或者客户端宕机,导致服务端对客户端应答失败。 如果此时生产者意识到消息发送失败并尝试再次发送消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。

  • 投递时消息重复

    消息消费的场景下,消息已投递到消费者并完成业务处理,当客户端给服务端反馈应答的时候网络闪断。 为了保证消息至少被消费一次,消息队列 RocketMQ 的服务端将在网络恢复后再次尝试投递之前已被处理过的消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。

  • 负载均衡时消息重复(包括但不限于网络抖动、Broker 重启以及订阅方应用重启)

    当消息队列 RocketMQ 的 Broker 或客户端重启、扩容或缩容时,会触发 Rebalance,此时消费者可能会收到重复消息。

MQ消息重复的处理方式

从上面的分析中,我们知道,在RocketMQ中,是无法保证每个消息只被投递一次的,所以要在业务上自行来保证消息消费的幂等性。

而要处理这个问题,RocketMQ的每条消息都有一个唯一的MessageId,这个参数在多次投递的过程中是不会改变的,所以业务上可以用这个MessageId来作为判断幂等的关键依据。

但是,这个MessageId是无法保证全局唯一的,也会有冲突的情况。所以在一些对幂等性要求严格的场景,最好是使用业务上唯一的一个标识比较靠谱。例如订单ID。而这个业务标识可以使用Message的Key来进行传递。

1、为什么要发送个half消息?有什么用?

这个half消息是在订单系统进行下单操作前发送,并且对下游服务的消费者是不可见的。那这个消息的作用更多的体现在确认RocketMQ的服务是否正常。相当于嗅探下RocketMQ服务是否正常,并且通知RocketMQ,我马上就要发一个很重要的消息了,你做好准备。

2.half消息如果写入失败了怎么办?

如果没有half消息这个流程,那我们通常是会在订单系统中先完成下单,再发送消息给MQ。这时候写入消息到MQ如果失败就会非常尴尬了。而half消息如果写入失败,我们就可以认为MQ的服务是有问题的,这时,就不能通知下游服务了。我们可以在下单时给订单一个状态标记,然后等待MQ服务正常后再进行补偿操作,等MQ服务正常后重新下单通知下游服务。

订单系统写数据库失败了怎么办?

这个问题我们同样比较下没有使用事务消息机制时会怎么办?如果没有使用事务消息,我们只能判断下单失败,抛出了异常,那就不往MQ发消息了,这样至少保证不会对下游服务进行错误的通知。但是这样的话,如果过一段时间数据库恢复过来了,这个消息就无法再次发送了。当然,也可以设计另外的补偿机制,例如将订单数据缓存起来,再启动一个线程定时尝试往数据库写。而如果使用事务消息机制,就可以有一种更优雅的方案。

如果下单时,写数据库失败(可能是数据库崩了,需要等一段时间才能恢复)。那我们可以另外找个地方把订单消息先缓存起来(Redis、文本或者其他方式),然后给RocketMQ返回一个UNKNOWN状态。这样RocketMQ就会过一段时间来回查事务状态。我们就可以在回查事务状态时再尝试把订单数据写入数据库,如果数据库这时候已经恢复了,那就能完整正常的下单,再继续后面的业务。这样这个订单的消息就不会因为数据库临时崩了而丢失。

RocketMQ消息零丢失方案总结

完整分析过后,整个RocketMQ消息零丢失的方案其实挺简单

  • 生产者使用事务消息机制。
  • Broker配置同步刷盘+Dledger主从架构
  • 消费者不要使用异步消费。
  • 整个MQ挂了之后准备降级方案

那这套方案是不是就很完美呢?其实很明显,这整套的消息零丢失方案,在各个环节都大量的降低了系统的处理性能以及吞吐量。在很多场景下,这套方案带来的性能损失的代价可能远远大于部分消息丢失的代价。所以,我们在设计RocketMQ使用方案时,要根据实际的业务情况来考虑。例如,如果针对所有服务器都在同一个机房的场景,完全可以把Broker配置成异步刷盘来提升吞吐量。而在有些对消息可靠性要求没有那么高的场景,在生产者端就可以采用其他一些更简单的方案来提升吞吐,而采用定时对账、补偿的机制来提高消息的可靠性。而如果消费者不需要进行消息存盘,那使用异步消费的机制带来的性能提升也是非常显著的。

那再回到我们的消息不丢失的问题,在这种情况下,RocketMQ相当于整个服务都不可用了,那他本身肯定无法给我们保证消息不丢失了。我们只能自己设计一个降级方案来处理这个问题了。例如在订单系统中,如果多次尝试发送RocketMQ不成功,那就只能另外找给地方(Redis、文件或者内存等)把订单消息缓存下来,然后起一个线程定时的扫描这些失败的订单消息,尝试往RocketMQ发送。这样等RocketMQ的服务恢复过来后,就能第一时间把这些消息重新发送出去。整个这套降级的机制,在大型互联网项目中,都是必须要有的。

 

如何保证消息有序?

MQ的顺序问题分为全局有序和局部有序。

  • 全局有序:整个MQ系统的所有消息严格按照队列先入先出顺序进行消费。
  • 局部有序:只保证一部分关键消息的消费顺序。

​ 首先 我们需要分析下这个问题,在通常的业务场景中,全局有序和局部有序哪个更重要?其实在大部分的MQ业务场景,我们只需要能够保证局部有序就可以了。例如我们用QQ聊天,只需要保证一个聊天窗口里的消息有序就可以了。而对于电商订单场景,也只要保证一个订单的所有消息是有序的就可以了。至于全局消息的顺序,并不会太关心。而通常意义下,全局有序都可以压缩成局部有序的问题。例如以前我们常用的聊天室,就是个典型的需要保证消息全局有序的场景。但是这种场景,通常可以压缩成只有一个聊天窗口的QQ来理解。即整个系统只有一个聊天通道,这样就可以用QQ那种保证一个聊天窗口消息有序的方式来保证整个系统的全局消息有序。

​ 然后 落地到RocketMQ。通常情况下,发送者发送消息时,会通过MessageQueue轮询的方式保证消息尽量均匀的分布到所有的MessageQueue上,而消费者也就同样需要从多个MessageQueue上消费消息。而MessageQueue是RocketMQ存储消息的最小单元,他们之间的消息都是互相隔离的,在这种情况下,是无法保证消息全局有序的。

​ 而对于局部有序的要求,只需要将有序的一组消息都存入同一个MessageQueue里,这样MessageQueue的FIFO设计天生就可以保证这一组消息的有序。RocketMQ中,可以在发送者发送消息时指定一个MessageSelector对象,让这个对象来决定消息发入哪一个MessageQueue。这样就可以保证一组有序的消息能够发到同一个MessageQueue里。生产者把一组有序的消息放到同一个队列当中,而消费者一次消费整个队列当中的消息。

​ 另外,通常所谓的保证Topic全局消息有序的方式,就是将Topic配置成只有一个MessageQueue队列(默认是4个)。这样天生就能保证消息全局有序了。这个说法其实就是我们将聊天室场景压缩成只有一个聊天窗口的QQ一样的理解方式。而这种方式对整个Topic的消息吞吐影响是非常大的,如果这样用,基本上就没有用MQ的必要了。

1、如何确定RocketMQ有大量的消息积压?

​ 在正常情况下,使用MQ都会要尽量保证他的消息生产速度和消费速度整体上是平衡的,但是如果部分消费者系统出现故障,就会造成大量的消息积累。这类问题通常在实际工作中会出现得比较隐蔽。例如某一天一个数据库突然挂了,大家大概率就会集中处理数据库的问题。等好不容易把数据库恢复过来了,这时基于这个数据库服务的消费者程序就会积累大量的消息。或者网络波动等情况,也会导致消息大量的积累。这在一些大型的互联网项目中,消息积压的速度是相当恐怖的。所以消息积压是个需要时时关注的问题。

​ 对于消息积压,如果是RocketMQ或者kafka还好,他们的消息积压不会对性能造成很大的影响。而如果是RabbitMQ的话,那就惨了,大量的消息积压可以瞬间造成性能直线下滑。

​ 对于RocketMQ来说,有个最简单的方式来确定消息是否有积压。那就是使用web控制台,就能直接看到消息的积压情况。

​ 在Web控制台的主题页面,可以通过 Consumer管理 按钮实时看到消息的积压情况。

另外,也可以通过mqadmin指令在后台检查各个Topic的消息延迟情况。

还有RocketMQ也会在他的 ${storePathRootDir}/config 目录下落地一系列的json文件,也可以用来跟踪消息积压情况。

2、如何处理大量积压的消息?

如果Topic下的MessageQueue配置得是足够多的,那每个Consumer实际上会分配多个MessageQueue来进行消费。这个时候,就可以简单的通过增加Consumer的服务节点数量来加快消息的消费,等积压消息消费完了,再恢复成正常情况。最极限的情况是把Consumer的节点个数设置成跟MessageQueue的个数相同。但是如果此时再继续增加Consumer的服务节点就没有用了。

而如果Topic下的MessageQueue配置得不够多的话,那就不能用上面这种增加Consumer节点个数的方法了。这时怎么办呢? 这时如果要快速处理积压的消息,可以创建一个新的Topic,配置足够多的MessageQueue。然后把所有消费者节点的目标Topic转向新的Topic,并紧急上线一组新的消费者,只负责消费旧Topic中的消息,并转储到新的Topic中,这个速度是可以很快的。然后在新的Topic上,就可以通过增加消费者个数来提高消费速度了。之后再根据情况恢复成正常情况。

为什么要对Topic下数据进行分区存储?

1、commit log文件会受到所在机器的文件系统大小的限制,分区之后可以将不同的分区放在不同的机器上,相当于对数据做了分布式存储,理论上一个topic可以处理任意数量的数据。
2、为了提高并行度。

消费顺序

一个partition同一个时刻在一个consumer group中只能有一个consumer instance在消费,从而保证消费顺序。consumer group中的consumer instance的数量不能比一个Topic中的partition的数量多,否则,多出来的consumer消费不到消息。
Kafka只在partition的范围内保证消息消费的局部顺序性,不能在同一个topic中的多个partition中保证总的消费顺序性。
如果有在总体上保证消费顺序的需求,那么我们可以通过将topic的partition数量设置为1,将consumer group中的consumer instance数量也设置为1,但是这样会影响性能,所以kafka的顺序消费很少用。

Kafka为什么比RocketMQ的吞吐量要高?

Kafka的生产者采用的是异步发送消息机制,当发送一条消息时,消息并没有发送到Broker而是缓存起来,然后直接向业务返回成功,当缓存的消息达到一定数量时再批量发送给Broker。这种做法减少了网络io,从而提高了消息发送的吞吐量,但是如果消息生产者宕机,会导致消息丢失,业务出错,所以理论上kafka利用此机制提高了性能却降低了可靠性。

Kafka的Pull和Push分别有什么优缺点?

1.pul表示消费者主动拉取,可以批量拉取,也可以单条拉取,所以pul可以由消费者白己控制,根据自己的消息处理能力来进行控制,但是消费者不能及时知道是否有消息,可能会拉到的消息为空。
2.push表示Broker主动给消费者推送消息,所以肯定是有消息时才会推送,但是消费者不能按自己的能力来消费消息,推过来多少消息,消费者就得消费多少消息,所以可能会造成网络堵塞,消费者压力大等问题。

pull模式:
。根据consumer的消费能力进行数据拉取,可以控制速率;
。可以批量拉取、也可以单条拉取;
。可以设置不同的提交方式,实现不同的传输语义。
缺点: 如果kafka没有数据,会导致consumer空循环。
消耗资源解决: 通过参数设置,consumer拉取数据为空或者没有达到一定数量时进行阻塞。

push模式: 不会导致consumer循环等待
缺点: 速率固定、忽略了consumer的消费能力,可能导致拒绝服务或者网络拥塞等情况

Kafka、ActiveMQ、RabbitMQ、RocketMQ 对比?

ActiveMQ:JMS规范,支持事务、支持XA协议,没有生产大规模支撑场景、官方维护越来越少RabbitMQ: erlang语言开发、性能好、高并发,支持多种语言,社区、文档方面有优势,erlang语言不利于java程序员二次开发,依赖开源社区的维护和升级,需要学习AMQP协议、学习成本相对较高;吞吐量单机都在万级;
kafka: 高性能,高可用,生产环境有大规模使用场景,单机容量有限(超过64个分区响应明显变长)、社区更新,吞吐量单机百万;
rocketmq: java实现,方便二次开发、设计参考了kafka,高可用、高可靠,社区活跃度一般、支持语言较少吞吐量单机十万;

Kafka消息高可靠解决方案

消息发送:
。ack: 0、不重试,1、lead写入成功就返回了,all/-1、等待ISR同步完再返回。
。unclean.leader.election.enable:false,禁止选举ISR以外的follower为leader
。tries > 1,重试次数;
。min.insync.replicas > 1: 同步副本数,没满足该值前、不提供读写服务、写操作会异常

消费:
手工提交offset
broker: 减小刷盘间隔
事务消息。

零拷贝是什么?

Linux操作系统分为【用户态】和【内核态】,文件操作、网络操作需要涉及这两种形态的切换,免不了进行数据复制。
一台服务器 把本机磁盘文件的内容发送到客户端,一般分为两个步骤:
1)read;读取本地文件内容;
2)write;将读取的内容通过网络发送出去。
这两个看似简单的操作,实际进行了4 次数据复制,分别是:
1. 从磁盘复制数据到内核态内存;
2. 从内核态内存复 制到用户态内存;
3. 然后从用户态 内存复制到网络驱动的内核态内存;
4. 最后是从网络驱动的内核态内存复 制到网卡中进行传输。
 
而通过使用mmap的方式,可以省去向用户态的内存复制,提高速度。这种机制在Java中是通过NIO包中的MappedByteBuffffer实现的。RocketMQ充分利用了上述特性,也就是所谓的“零拷贝”技术,提高
消息存盘和网络发送的速度。sendFile主要通过DMA去实现的,对应java API是FileChannel。
Java当中对零拷贝进行了封装,Mmap方式通过MappedByteBuffer对象进行操作,而transfile通过FileChannel来进行操作。
Mmap 适合比较小的文件,通常文件大小不要超过1.5G ~2G 之间。
Transfile没有文件大小限制。
RocketMQ当中使用Mmap方式来对他的文件进行读写。commitlog。 1G
在kafka当中,他的index日志文件也是通过mmap的方式来读写的。在其他日志文件当中,并没有使用零拷贝的方式。
kafka使用transfile方式将硬盘数据加载到网卡。

kafka高性能高吞吐的原因?

1、磁盘顺序读写: 保证了消息的堆积
  顺序读写,磁盘会预读,预读即在读取的起始地址连续读取多个页面,主要时间花费在了传输时间,而这个时间两种读写可以认为是一样的。
  随机读写,因为数据没有在一起,将预读浪费掉了。需要多次寻道和旋转延迟。而这个时间可能是传输时间的许多倍。
2、零拷贝: 避免 CPU 将数据从一块存储拷贝到另外一块存储的技术。传统的数据复制:
  1、读取磁盘文件数据到内核缓冲区
  2、将内核缓冲区的数据copy到用户缓冲区
  3、将用户缓冲区的数据copy到socket的发送缓冲区
  4、将socket发送缓冲区中的数据发送到网卡、进行传输
零拷贝:
磁盘文件->内核空间读取缓冲区->网卡接口->消费者进程

3、分区分段+索引
Kafka的message消息实际上是分布式存储在一个一个小的segment中的,每次文件操作也是直接操作的segment。为了进一步的查询优化,Kafka又默认为分段后的数据文件建立了索引文件,就是文件系统上的.index文件。这种分区分段+索引的设计,不仅提升了数据读取的效率,同时也提高了数据操作的并行度。
4、批量压缩:多条消息一起压缩,降低带宽
5、批量读写
直接操作page cache,而不是JVM、避免GC耗时及对象创建耗时,且读写速度更高,进程重启、缓存也不会丢失。

Kafka中zk的作用?

/brokers/ids: 临时节点,保存所有broker节点信息,存储broker的物理地址、版本信息、启动时间等,节点名称为brokerlD,broker定时发送心跳到zk,如果断开则该brokerID会被删除
/brokers/topics: 临时节点,节点保存broker节点下所有的topic信息,每一个topic节点下包含一个固定的partitions节点,partitions的子节点就是topic的分区,每个分区下保存一个state节点、保存着当前leader分区和ISR的brokerlD,state节点由leader创建,若leader宕机该节点会被删除,直到有新的leader选举产生、重新生成state节点
/consumers/[group_id]/owners/[topic]/[broker_id-partitidn_id]: 维护消费者和分区的注册关系
/consumers/[group_id]/offsets/[topic]/[broker_id-partition _id]: 分区消息的消费进度Offset
cient通过topic找到topic树下的state节点、获取leader的brokerlD,到broker树中找到broker的物理地址,但是client不会直连zk,而是通过配置的broker获取到zk中的信息。

使用MQ如何保证分布式事务的最终一致性?

分布式事务: 业务相关的多个操作,保证他们同时成功或者同时失败。
最终一致性: 与之对应的就是强一致性
MQ中要保护事务的最终一致性,就需要做到两点
1、生产者要保证100%的消息投递。 事务消息机制
2、消费者这一端需要保证幂等消费。 唯一ID+ 业务自己实现幂等
分布式MQ的三种语义:
at least once
at most once
exactly once:RocketMQ 并不能保证exactly once。商业版本当中提供了exactly once的实现机制。

 

MQ如何保证消息不丢失?根据不同的消息丢失场景及不同的消息中间件回答

1. producer往MQ发送消息的时候不丢失:
kafka:消息发送+回调
Rocket:
a, 消息发送+回调;
b, 事务消息;
RabbitMQ:
a, 消息发送+回调
b, 手动事务:channel.txSelect()开启事务,channel.txCommit()提交事务,channel.txRollback()回滚事务,这种方式对channel是会产生阻塞的,造成吞吐量下降。
c, Publish Confirm。整个处理流程跟RocketMQ的事务消息,基本是一样的。

2. MQ主从消息同步不丢失:
RocketMQ:
a, 普通集群中,同步同步,异步同步。异步同步效率更高,但是有丢失消息的风险。同步同步就不会丢失消息。
b, Dledger集群-两阶段提交;
RabbitMQ:
a, 普通集群:消息是分散存储的,节点之间不会主动进行消息同步,是有可能丢失消息的;
b, 镜像集群:镜像集群会在节点之间进行数据同步,这样数据安全性得到提高。
kafka:通常都是用在允许消息少量丢失的场景。ack:0,1,all

3. MQ消息存盘不丢失:
RocketMQ:同步刷盘 异步刷盘:异步刷盘效率更高,但是有可能丢失消息。同步刷盘安全性更高,但是效率会降低。
RabbitMQ:将队列配置成持久化队列。新增的Quorum类型的队列,会采用Raft协议来进行消息同步。
kafka:允许消息存盘不丢失。

4. MQ消费者消费消息不丢失:
RocketMQ:使用默认的方式消费就行,不要采用异步方式。
RabbitMQ:关闭autoCommit.
kafka:手动提交offset。

rabbitmq的镜像队列原理

GM负责消息的广播,所有的GM组成gm_group,形成链表结构,负责监听相邻节点的状态,以及传递消息到相邻节点,master的GM收到消息时代表消息同步完成。
mirror queue _master/slave负责消息的处理,操作blockingQueue,Queue负责AMQP协议 (commit.rollback、ack等)。
master处理读写。

MQ的作用

异步:提高系统的响应速度和吞吐量。
解耦:解耦之后可以实现流量分发,生产者发送一个消息之后,可以由多个消费者处理。可以减少服务之间的影响,提高系统的响应速度和吞吐量。
削峰:以稳定的系统资源应对突发的流量冲击。
MQ的缺点:
1. 系统的可用性降低:一旦MQ宕机,整个业务就会产生影响,高可用
2. 系统的复杂性提高:引入MQ之后,数据链路就会变得很复杂。如何保证消息不丢失?消息不会重复调用?怎么保证消息的顺序性?
3. 数据一致性问题。。。

rabbitMQ的死信队列、延迟队列原理?

死信消息:
1.消息被消费方否定确认,使用 channel.basicNack 或 channe1.basicReject,并且此时requeue 属性被设置为false。
2.消息在队列的存活时间超过设置的TTL时间。
3.消息队列的消息数量已经超过最大队列长度。
那么该消息将成为死信消息。如果配置了死信队列信息,那么该消息将会被丢进死信队列中,如果没有配置,则该消息将会被丢弃。
为每个需要使用死信的业务队列配置一个死信交换机,同一个项目的死信交换机可以共用一个,然后为每个业务队列分配一个单独的routeKey,死信队列只不过是绑定在死信交换机上的队列,死信交换机也不是什么特殊的交换机,只不过是用来接受死信的交换机,所以可以为任何类型[Direct、Fanout、Topic]。
TTL:一条消息或者该队列中的所有消息的最大存活时间。
如果一条消息设置了TTL属性或者进入了设置TTL属性的队列,那么这条消息如果在TTL设置的时间内没有被消费则会成为“死信”。如果同时配置了队列的TTL和消息的TTL,那么较小的那个值将会被使用。
只需要消费者一直消费死信队列里的消息。

RabbitMQ事务消息

通过对信道的设置实现
1.channel.txSelect(); 通知服务器开启事务模式; 服务端会返回Tx.Select-Ok
2.channel.basicPublish; 发送消息,可以是多条,可以是消费消息提交ack
3.channel.txCommit()提交事务
4.channel.txRollback0回滚事务;
消费者使用事务:
1.autoAck=false,手动提交ack,以事务提交或回滚为准;
2.autoAck=true,不支持事务的,也就是说你即使在收到消息之后在回滚事务也是于事无补的,队列已经把消息移除了.
如果其中任意一个环节出现问题,就会抛出IOException异常,用户可以拦截异常进行事务回滚,或决定要不要重复消息。
事务消息会降低rabbitmg的性能。

RabbitMQ接收方确认机制?

消费者在声明队列时,可以指定noAck参数,当noAck=false时,RabbitMQ会等待消费者显式发回ack信号后才从内存(或者磁盘,持久化消息)中移去消息。否则,消息被消费后会被立即删除。
消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitmQ才能安全地把消息从队列中删除。
RabbitMQ不会为未ack的消息设置超时时间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消息的消费者连接是否已经断开。这么设计的原因是RabbitMQ允许消费者消费一条消息的时间可以很长。保证数据的最终一致性;
如果消费者返回ack之前断开了链接,RabbitMO 会重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)

RocketMQ 如何保证不丢消息

生产者:
。同步阻塞的方式发送消息,加上失败重试机制,可能broker存储失败,可以通过查询确认
。异步发送需要重写回调方法,检查发送结果
。ack机制,可能存储CommitLog,存储ConsumerQueue失败,此时对消费者不可见
broker:
。同步刷盘、集群模式下采用同步复制、会等待slave复制完成才会返回确认
消费者:
。offset手动提交,消息消费保证幂等

RocketMQ的底层实现原理

RocketMQ由NameServer集群、Producer集群、Consumer集群、Broker集群组成,消息生产和消费的大致原理如下:
1.Broker在启动的时候向所有的NameServer注册,并保持长连接,每30s发送一次心跳
2.Producer在发送消息的时候从NameServer获取Broker服务器地址,根据负载均衡算法选择一台服务器来发送消息
3.Conusmer消费消息的时候同样从NameServer获取Broker地址,然后主动拉取消息来消费

RocketMQ 事务消息原理

依赖于TransactionListener接口
executeLocalTransaction方法会在发送消息后调用,用于执行本地事务,如果本地事务执行成功,rocketmq再提交消息.
checkLocalTransaction用于对本地事务做检查,rocketma依赖此方法做补偿
通过两个内部的topic来实现对消息的两阶段支持.

简述kafka的reblance机制?

何时会产生rebalance:
。consumer group中的成员个数发生变化
。consumer消费超时
。group订阅的topic个数发生变化
。group订阅的topic的分区数发生变化

coordinator: 通常是partition的leader节点所在的broker,负责监控group中consumer的存活,consumer维持到coordinator的心跳,判断consumer的消费超时。
。 coordinator通过心跳返回通知consumer进行rebalance
。consumer请求coordinator加入组,coordinator选举产生leader consumer
。leader consumer从coordinator获取所有的consumer,发送syncGroup(分配信息)给到coordinator
。coordinator通过心跳机制将syncGroup下发给consumer
。完成rebalance

leader consumer监控topic的变化,通知coordinator触发rebalance。
如果C1消费消息超时,触发rebalance,重新分配后、该消息会被其他消费者消费,此时C1消费完成提交offset,导致错误。
解决: coordinator每次rebalance,会标记一个Generation给到consumer,每次rebalance该Generation会+1,consumer提交offset时,coordinator会比对Generation,不一致则拒绝提交。

简述kafka架构设计?

Consumer Group: 消费者组,消费者组内每个消费者负责消费不同分区的数据,提高消费能力。逻辑上的一个订阅者。
Topic: 可以理解为一个队列,Topic 将消息分类,生产者和消费者面向的是同一个 Topic。
Partition: 为了实现扩展性,提高并发能力,一个Topic 以多个Partition的方式分布到多个 Broker 上,每个Partition 是一个有序的队列。一个Topic 的每个Partition都有若干个副本(Replica),一个Leader 和若干个Follower。生产者发送数据的对象,以及消费者消费数据的对象,都是 Leader。Follower负责实时从 Leader 中同步数据,保持和 Leader 数据的同步。Leader 发生故障时,某个 Follower 还会成为新的 Leader。

rabbitmg的持久化机制

1、交换机持久化: exchange_declare创建交互机时通过参数指定;
2、队列持久化: queue_declare创建队列时通过参数指定;
3、消息持久化: new AMQPMessage创建消息时通过参数指定;
append的方式写文件,会根据大小自动生成新的文件,rabbitmg启动时会创建两个进程,一个负责持久化消息的存储,另一个负责非持久化消息的存储(内存不够时);
消息存储时会在ets表中记录消息在文件中的映射以及相关信息(包括id、偏移量,有效数据,左边文件,右边文件),消息读取时根据该信息到文件中读取、同时更新信息;
消息删除时只从ets删除,变为垃圾数据,当垃圾数据超出比例(默认50%) ,并且文件数达到3个,触发垃圾回收,锁定左右两个文件,整理左边文件有效数据、将右边文件有效数据写入左边,更新文件信息,删除右边,完成合并。当一个文件的有用数据等于0时,删除该文件。
写入文件前先写buffer缓冲区,如果buffer已满,则写入文件(此时只是操作系统的页存);
每隔25ms刷一次磁盘,不管buffer满没满,都将buffer和页存中的数据落盘;
每次消息写入后,如果没有后续写入请求,则直接刷盘;

简述RabbitMg的交换机类型

fanout: 扇形交换机,不再判断routekey,直接将消息分发到所有绑定的队列
direct: 判断routekey的规则是完全匹配模式,即发送消息时指定的routekey要等于绑定的routekey
topic:判断routekey的规则是模糊匹配模式
header: 绑定队列与交换器的时候指定一个键值对,当交换器在分发消息的时候会先解开消息体里的headers数据,然后判断里面是否有所设置的键值对,如果发现匹配成功,才将消息分发到队列中,这种交换器类型在性能上相对来说较差,在实际工作中很少会用到

简述RabbitMQ的普通集群模式?

集群节点分为磁盘节点和内存节点。切持久化依赖于磁盘节点。
并且节点之间同步数据依赖于元数据的同步,比如队列、消息、vhost元数据等;
只同步元数据,有如下原因:
存储空间的限制,每一个节点都存储全量数据,影响消息堆积能力;
对性能有影响,消息的发布者需要将消息复制到每一个集群节点;
另外普通集群模式还有一个特点:
客户端连接的是非队列数据所在节点:则该节点会进行路由转发,包括发送和消费。



如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
posted @   君莫笑我十年游  阅读(73)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· DeepSeek R1 简明指南:架构、训练、本地部署及硬件要求
· 2 本地部署DeepSeek模型构建本地知识库+联网搜索详细步骤
历史上的今天:
2020-02-27 springboot+thymeleaf项目中使用th:replace访问templates子目录下的模板,会报错找不到模板路径
2020-02-27 springboot启动项目报错:ERROR:o.s.b.d.LoggingFailureAnalysisReporter解决办法
点击右上角即可分享
微信分享提示