Rabbit Mq 底层原理

为什么要用MQ?MQ有什么好处?

1、异步通信:通过异步通信,可以减少客户端等待时间,实现接口快速响应

2、系统解耦:对于复杂的系统,减小系统与系统之间的依赖

3、流量削峰:对于瞬时访问增大,缓解服务器压力,保护服务,同时保证消息不丢失

MQ的主要特点?

1、独立运行的服务

2、使用队列数据结构进行存储数据消息(既然mq使用队列存储消息,为什么不直接使用队列进行通信,因为queue是不能跨进程的)

3、发布、订阅模型,即生产者生产消息,消费者消费消息

使用MQ带来的问题?

1、系统可用性降低了(因为mq需要部署在服务器,如果服务器出问题,系统可用性降低了)

2、系统变复杂了(需要了解mq实现原理,以及解决消息丢失、重复消费等问题)

Mq的基本特性:

1、高可靠:通过发送确认,接收确认、持久化等保证了高可靠

2、灵活的路由:通过交换机4种路由方式

3、支持多客户端:支持多语言

4、集群与扩展性:支持集群、负载均衡

5、高可用队列:镜像集群实现队列消息的复制

6、权限管理:提供给了基于用户与vhost的权限管理

7、插件系统:可视化插件

8、与spring 集成:spring Amqp实现简单

MQ工作模型?

消费者、生产者与broker进行连接采用TCP长连接方式channel虚拟信道,减少服务器资源的消耗

Rabbit Mq 4种路由方式:

binding key与rounting key

direct直连:

2、topic主题类型的交换机

#号代表:0个或者多个单词(通过英文的.来隔开单词)

*号代表:不多不少1个单词(通过英文的.来隔开单词)

3、fanout广播

4、header模式

header模式与routing不同的地方在于,header模式取消routingkey,使用header中的 key/value(键值对)匹配队列。

 

TTL过期时间:

队列的消息过期时间x-message-ttl = 10  10s钟过期

单条消息的过期时间?

如果同时设定队列消息过期时间和消息本身的过期时间,哪一个会最先失效?

队列和消息,哪个过期时间设置的小,哪个先过期。

什么是死信交换机和死信队列呢?

设置普通队列的死信交换机,死信交换机与死信队列绑定,当普通队列的消息成为死信消息就会进入死信交换机。

消息什么时候会变成死信呢?

1、消息被消费者拒绝

2、消息过期

3、队列到达了最大长度,将对头的消息丢到死信交换机上(队列到底能存储多少条消息?通过x-max-length、x-max-length-bytes参数可以设置)

 

死信队列如何使用?

 

延迟队列

延迟队列的总体方案:

1:先将消息入库,通过定时任务扫描,达到延迟时间后,将消息投递出去

2:死信队列的实现:设置消息过期时间,到达时间后消息进入死信交换机,进入死信队列,消费者消费消息

3:rabbitmq-delayed-message-exchange的实现(Liunx):实现起来简单,死信队列的延迟时间不是很精准

 

服务端流控

通过x-max-length、x-max-length-bytes这2个参数能实现服务端限流?不能,因为当这2个参数设置的值满足后,会将队头的消息移除

如果服务端产生消息的速度比消费端消费消息的速度快,那就会造成内存空间的大量占用(Mq内存节点),会占用磁盘空间(磁盘节点),如何解决?

默认如果内存低于40%的时候,mq拒绝服务端连接Conn,通过配置参数vm_memory_high_watermark控制

当占用磁盘空间低于30%时候,拒绝服务端连接,通过配置文件参数进行配置disk_free_limit.relative=3.0

当占用磁盘空间低于2G时候,拒绝服务端连接,通过配置文件参数进行配置disk_free_limit.absolute=2G

消费者消费消息会将消息缓存到本地,如果消息过多,而没有消费完,消费端限制通过设置prefetch count属性控制,如果消费端接收消息后比如5条,5条都没有给服务端应答的时候,就暂时不会进行消费消息了

 

Spring AMQP:

Spring 集成AMQP时,它做了什么?

1、帮助我们去管理对象,通过标签的方式进行配置

2、封装了rabbitMqTemplate来简化配置(rabbitTemplate)实现消息的收发

Spring AMQP包括什么?

ConnectFactory:定义虚拟机的ip、端口等等信息 连接工厂

RabbitAdmin:存放绑定关系、交换机、队列等

Message:消息对象

RabbitTemplate:消息的收发,通过定义不同的RabbitTemplate实现对不同服务器的mq进行发送消息

MessageListenerContainerFactory:动态创建队列、动态消除队列

MessageConvertor:主要是定义传输序列化格式,比如json、xml、object等等

 

Rabbit Mq可靠性投递与实践经验!!!!

1、掌握Rabbit Mq可靠性消息的投递发送

1)服务端确认机制:

1、tranction模式(不建议使用,这种方式存在阻塞,只有一条消息成功后,才会进行下一条消息)

2、confirm模式(发送一条确认一条,建议使用批量确认)

2到3过程,如何确保交换机可以把消息路由到队列呢?

路由保证:

1、mandatory = true + ReturnListener

2、指定交换机备份交换机

在当前交换机指定备份交换机去保证消息可靠性投递

交换机才有备份交换机,队列才有死信交换机

第3阶段,存储消息持久化:

1)消息持久化

2)队列持久化

3)交换机持久化

4)集群

第4步骤:

消费者确认机制ack:AutoAck

自动ack:接收到消息就发送ack

手动ack:接收到消息到处理完消息,才给服务器发送ack确认消息机制

none:自动应答

manual:手工应答

auto:自动应答,方法没发生异常才会自动应答,如果出错,则发送nack拒绝、requeue重新入队

rabbit Mq 保证消息的顺序性,一定是生产者发送到路由器和队列中的消息,消费者也需要取路由器和队列的消息,这样才能保证消息的顺序性

2、掌握Rabbit Mq集群原理与高可用架构的搭建

rabbit Mq端口:5672(单机端口)、15672(插件端口)、25672(集群端口)

erlang 语言    .erlang.cookie  进行集群通讯

集群节点的类型:disc:将交换机、队列、绑定关系、消息等放到磁盘上(主要进行数据的备份)

                             RAM:原数据放到内存中(内存节点更快速,适合应用连接)

1. 消息队列的作用与使用场景
异步:批量数据异步处理(批量上传文件)
削峰:高负载任务负载均衡(电商秒杀抢购)
解耦:串行任务并行化(退货流程解耦)
广播:基于Pub/Sub实现一对多通信

2. 多个消费者监听一个队列时,消息如何分发
轮询:默认的策略,消费者轮流,平均地接收消息
公平分发:根据消费者的能力来分发消息,给空闲的消费者发送更多消息

//当消费者有x条消息没有响应ACK时,不再给这个消费者发送消息
    channel.basicQos(int x)

3. 无法被路由的消息去了哪里
无设置的情况下,无法路由(Routing key错误)的消息会被直接丢弃
解决方案:
将mandatory设置为true,并配合ReturnListener,实现消息的回发

声明交换机时,指定备份的交换机

 Map<String,Object> arguments = new HashMap<String,Object>();
    arguments.put("alternate-exchange","备份交换机名");

4. 消息在什么时候会变成死信
消息拒绝并且没有设置重新入队
消息过期
消息堆积,并且队列达到最大长度,先入队的消息会变成DL

5. RabbitMQ如何实现延时队列
利用TTL(队列的消息存活时间或者消息存活时间),加上死信交换机

 // 设置属性,消息10秒钟过期
 AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .expiration("10000") // TTL

 // 指定队列的死信交换机
 Map<String,Object> arguments = new HashMap<String,Object>();
 arguments.put("x-dead-letter-exchange","DLX_EXCHANGE");

6. 如何保证消息的可靠性投递
发送方确认模式:
将信道设置成confirm模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的ID。
一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一ID)。
如果RabbitMQ发生内部错误从而导致消息丢失,会发送一条nack(not acknowledged,未确认)消息。
发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。

接收方确认机制
接收方消息确认机制:消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ才能安全地把消息从队列中删除。
这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ给了Consumer足够长的时间来处理消息。保证数据的最终一致性;
下面罗列几种特殊情况:
如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)
如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙,将不会给该消费者分发更多的消息。

7. 消息幂等性
生产者方面:可以对每条消息生成一个msgID,以控制消息重复投递

 AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
 porperties.messageId(String.valueOF(UUID.randomUUID()))

消费者方面:消息体中必须携带一个业务ID,如银行流水号,消费者可以根据业务ID去重,避免重复消费

8. 消息如何被优先消费

//生产者
 Map<String, Object> argss = new HashMap<String, Object>();
        argss.put("x-max-priority",10);

//消费者
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .priority(5) // 优先级,默认为5,配合队列的 x-max-priority 属性使用

9. 如何保证消息的顺序性
一个队列只有一个消费者的情况下才能保证顺序,否则只能通过全局ID实现(每条消息都一个msgId,关联的消息拥有一个parentMsgId。可以在消费端实现前一条消息未消费,不处理下一条消息;也可以在生产端实现前一条消息未处理完毕,不发布下一条消息)

10. RabbitMQ的集群模式和集群节点类型

mq集群节点分为内存节点和磁盘节点


**普通模式:**默认模式,以两个节点(rabbit01,rabbit02)为例来进行说明,对于Queue来说,消息实体只存在于其中一个节点rabbit01(或者rabbit02),rabbit01和rabbit02两个节点仅有相同的元数据,即队列结构。当消息进入rabbit01节点的Queue后,consumer从rabbit02节点消费时,RabbitMQ会临时在rabbit01,rabbit02间进行消息传输,把A中的消息实体取出并经过B发送给consumer,所以consumer应尽量连接每一个节点,从中取消息。即对于同一个逻辑队列,要在多个节点建立物理Queue。否则无论consumer连rabbit01或rabbit02,出口总在rabbit01,会产生瓶颈。当rabbit01节点故障后,rabbit02节点无法取到rabbit01节点中还未消费的消息实体。如果做了消息持久化,那么等到rabbit01节点恢复,然后才可被消费。如果没有消息持久化,就会产生消息丢失的现象。

**镜像模式:**把需要的队列做成镜像队列,存在与多个节点属于RabibitMQ的HA方案,该模式解决了普通模式中的问题,其实质和普通模式不同之处在于,消息体会主动在镜像节点间同步,而不是在客户端取数据时临时拉取,该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉,所以在对可靠性要求比较高的场合中适用

节点分为内存节点(保存状态到内存,但持久化的队列和消息还是会保存到磁盘),磁盘节点(保存状态到内存和磁盘),一个集群中至少需要一个磁盘节点

11.如何自动删除长时间没有消费的消息

// 通过队列属性设置消息过期时间
        Map<String, Object> argss = new HashMap<String, Object>();
        argss.put("x-message-ttl",6000);

 // 对每条消息设置过期时间
        AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
                .expiration("10000") // TTL

12.消息基于什么传输
AMQP协议,RabbitMQ使用信道的方式来传输数据。信道是建立在真实的TCP连接内的虚拟连接,且每条TCP连接上的信道数量没有限制

13.如何确保消息不丢失
消息持久化,当然前提是队列必须持久化
RabbitMQ确保持久性消息能从服务器重启中恢复的方式是,将它们写入磁盘上的一个持久化日志文件,当发布一条持久性消息到持久交换器上时,Rabbit会在消息提交到日志文件后才发送响应。
一旦消费者从持久队列中消费了一条持久化消息,RabbitMQ会在持久化日志中把这条消息标记为等待垃圾收集。如果持久化消息在被消费之前RabbitMQ重启,那么Rabbit会自动重建交换器和队列(以及绑定),并重新发布持久化日志文件中的消息到合适的队列。

 

 

 

 

channel和vhost的作用是什么?

channal是减少连接数

vhost节省硬件资源

交换机与队列、队列与消费者的绑定关系?

多对多

队列与消费者为1对多的时候,会采用轮训机制调用消费者,每次拿几条是prefetch count来决定的

无法被路由的消息,去哪里了?

直接被丢弃   解决是备份交换机、回发

消息在什么情况下会进入死信?

消息过期、消息被拒绝、消息超过了队列的长度

 

rabbit Mq如何实现延迟队列?

1、将消息放入数据库,通过定时任务去扫描,发送消息

2、通过TTl加死信队列来实现延迟队列

3、通过delay-message插件

哪些情况会导致消息丢失?如何解决?

一个队列最多可以存放多少条消息?取决于硬件配置,如果参数做了持久化,无论是内存节点还是磁盘节点都会放到磁盘上

如何提高消息的消费速率呢?

增加消费者

如何动态的创建队列和消费者?listenerContainer

大量消息堆积如何处理?增加消费者、直接把队列清空掉、重启服务

设计一个mq?

存储、转发

  • 8
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值