rabbitmq

一.rabbitmq是什么

rabbitmq是一个异步处理消息的框架,在一个商品下单的项目中,用户成功下单后,需要扣除金额,产生订单信息,发送短信通知等等,如果整个业务逻辑都写再一个service层,则就会导致请求耗时大大增加,在高并发的场景下,这是不可取的。而rabbitmq则能帮我们解决这个问题。

当业务逻辑本身是异步操作时,我们就可以利用mq异步来处理来减少业务执行的耗时时间。就比如上面这个场景,当用户成功下完单时,我们可以利用mq来异步发送消息"成功下单啦",在另一个业务层来接受到这个消息异步的来执行产生订单信息和发送短信通知。当再异步操作的需求时,比如用户下单后我需要累加积分,我们只需要创建一个消费者来消费这个信息即可。

二.rabbitmq的执行流程

生产者将消息转发至交换机,然后路由到队列中,根据队列的先进先出特性,消费中从队列中取出消息通知然后执行一些列的操作。

mq的队列和交换机被称作为虚拟机,类似于MySQL中的数据库,在mq配置文件中,要表明是对哪个虚拟机操作。在发送消息时,可以直接发送至队列,也可以发送至交换机,由于交换机和队列存在着绑定关系,交换机在根据关系路由到队列中,记住交换机是没有存储功能的,他之负责转发。

交换机由三种,findout,direct,topic,findout交换机可以路由到他所绑定的所有队列中,direct交换机除了绑定关系外,会给每个队列设置一个boundingkey,boudingkey通常是一个单词。在发送消息时,除了要指定交换机,还要带上一个routkingkey,当boundingkey和routingkey相同时才路由到指定的队列上。。topic交换机和direct类似,只是routingkey可以是多个单词,单词之间以“ . ”分割,队列的bindingkey可以用通配符(“ * ”代表一个单词,“ # ”代表0个或多个单词)来接收多个消息。例如,当bindnigkey为*.new,则该队列会接收所有以new单词结尾的消息。

消费者从队列获取消息,消息只能被消费一次,当有多个执行能力相同的消费者监听同一个队列时,默认会轮询消费。当多个消费者执行能力不同时,由于轮询机制,可能会发生消息堆积,即消息还没来得及消费完,又监听到其他消息。此时可通过修改相关配置,确保每个消费者消费完信息才能监听。

三.消息的可靠性

1.生产者可靠性:

由于网络的波动,客户端可能与mq断开了连接,此时消息无法及时发送至mq而导致业务失效,为了避免这类问题,我们可以设置重试机制,当与mq断开连接时会自动重连。注意,这个重连机制只是针对连接mq失败,而不是消息发送失败。但springamq重试机制时阻塞的,即在重试的时候,该线程会阻塞,影响性能,一般不建议使用。如果一定要使用,合理配置重试时间 和次数,或者使用异步发消息也可以

还有一种方式就是生产者确认,当消息成功发送时,mq会返回一个ack回执给生产者,如果失败则返回nak。具体情况分为一下几种:

(1)路由失败的原因,消息已经到达交换机但routingkey错误导致无法入队,此时返回ack,如果配置中开启了publish-return,则会放回一个异常信息。

(2)临时消息成功入队,返回ack

(3)持久化消息成功入队,返回ack

(4)其他情况都返回nak,比如,消息在持久化时出现故障导致持久化失败,消息还没到达mq或者mq在处理时出现异常。

对nack消息可以有限次数重试,依然失败则会记入错误信息。

2.mq的可靠性:

数据持久性分为3个方面,交换机持久性,队列持久性,消息持久性,spring默认创建的交换机和队列都保证了持久性,消息持久性又分为临时消息和非临时消息,当发送的是临时消息的话,他会先存储在内存中,在并发情况下,内存爆时,他才会持久化到磁盘中,在持久化过程中,mq处于阻塞,即内存将不在接收消息。若发送的时非临时消息,消息会异步写入磁盘当中,这种方式可以提高消息的发送和接收效率,因为消息可以首先被快速地写入到内存中,而不用等待磁盘写入完成。

惰性队列:接收消息直接保存到磁盘(内存只保留最近的2000条数据),消费者要消费消息时,读从磁盘中读取并加载到内存中,支持百万叫数据存储。

3.消费者可靠性:

为了消费者能够成功处理消息,mq提供消费者确认机制,当消费者处理完消息,应该项mq发送一个回执告诉mq自己消息处理的状态。回执有三种:

1.ack成功处理消息,mq从队列中删除消息

2.nack处理消息失败,mq重新投递消息

3.reject拒绝处理消息,mq从队列删除。

springAMQ已经实现了消息确认功能,允许我们通过配置文件选择ack处理方式,有三种方式

.none:不处理,即消息投递给消费者后立刻ack,消息会立刻从mq删除,非常不安全

不建议使用、

maual:手动模式,需要自己在业务代码中调用api发送ack或reject,存在业务入侵,但更灵活

auto:自动模式,springAMQP利用aop对我们的消息处理逻辑做了环绕增强,当业务正常

执行时会自动返回ack,当业务抛异常时会根据异常返回不同结果:

如果时业务异常,会返回ack,如果时消息处理异常,比如消息转换异常或者校验异常自动返回reject

在极端情况下消息传递失败,在消费者和mq互相投递,这显然是不合理的,我们通过设置重试机制,来控制重试的次数从而避免这样的情况,仍然失败时,我们有三种不同的消息处理失败的策略:

RejectAndDontRequeueRecoverer:重试耗尽后。直接reject,丢弃消息,默认是这一种

immediaterequeuemessagerecoverer:重试耗尽后,返回nack,消息重新入队

republishmessagerecoverer:重试耗尽后,将失败消息投递到指定的交换机

保证消费者可靠性还有一点,保证业务幂等性有两种方法:

(1)唯一消息ID
给每个消息都生成一个唯一ID,利用ID区分是否是重复消息:每一条消息都生成一个唯一的ID,与消息一起投递给消费者。消费者接收到消息后处理自己的业务,业务处理成功后将消息ID保存到数据库。如果下次收到相同消息,去数据库查询判断是否存在,存在则为重复消息放弃处理

(2)业务判断
结合业务逻辑,基于业务本身做判断

四.延迟消息

有一种商城业务的场景,用户在下单之后会创建订单并锁单,但迟迟没有支付,这时候我们会留点时间给用户取支付产品。预留这些时间就要用到延迟消息,指消息在发送到队列后,不会立即被消费者接收和处理,而是会延迟一段时间后才会被消费者获取。

延迟消息有两种实现方案:

1.基于死信实现延迟消息
使用RabbitMQ来实现延迟消息必须先了解RabbitMQ的两个概念:
消息的TTL(存活时间)和死信交换机Exchange,通过这两者的组合来实现延迟队列

1.1 消息的TTL(Time To Live)
消息的TTL就是消息的存活时间。RabbitMQ可以对队列和消息分别设置TTL。对队列设置就是队列没有消费者连着的保留时间,也可以对每一个单独的消息做单独的设置。超过了这个时间,我们认为这个消息就死了,称之为死信。
如何设置TTL:
我们创建一个队列queue.temp,在Arguments 中添加x-message-ttl 为5000 (单位是毫秒),那所在压在这个队列的消息在5秒后会消失。

1.2 死信交换机 Dead Letter Exchanges
一个消息在满足如下条件下,会进死信路由,记住这里是路由而不是队列,一个路由可以对应很多队列。
(1) 一个消息被Consumer拒收了,并且reject方法的参数里requeue是false。也就是说不会被再次放在队列里,被其他消费者使用。
(2)上面的消息的TTL到了,消息过期了。
(3)队列的长度限制满了。排在前面的消息会被丢弃或者扔到死信路由上。
Dead Letter Exchange其实就是一种普通的exchange,和创建其他exchange没有两样。只是在某一个设置Dead Letter Exchange的队列中有消息过期了,会自动触发消息的转发,发送到Dead Letter Exchange中去。

2.基于延迟插件实现延迟消息

2.1 插件安装

  RabbitMQ官方推出的插件,原生支持延迟消息的功能。其原理是设计了一种支持延迟消息功能的交换机,当消息投递到交换机后可以暂存一定时间,到期后再投递到队列。

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值