认识同步调用和异步调用:
同步调用: 一条龙,只有所有服务全部完成才返回数据 。 一 个服务依赖于多个服务,例如支付服务,需要用户服务,交易服务,通知服务。如果使用同步调用,就会使用户等待时间为所有服务完成后时间总和。者会导致用户体验较差,而且后续也会有功能添加进去,这就需要程序员重新构建项目。 但时效行高
优缺点: 优点:时效性强,等待到结果才返回数据
缺点:拓展性差 性能下降 级联失败问题(一级失败全部歇菜)
异步调用:异步调用的方式是基于消息通知的方式来实现的,一般包含三个角色
· 消息发送者:投递消息的人,就是调用方
·消息代理: 管理,暂存,转发消息
·消息接受者:接受和处理消息的人,就是原来的服务提供者
优缺点:
优点:耦合度低,拓展性强。异步调用,无需等待,性能好,故障隔离,下游服务故障不影响上游业务,缓存消息,流量削峰
存在问题:不能立即得到结果,时效性差,不确定下游业务是否成功,业务安全依赖于Broker的可靠性
AMQP:是用于在应用程序之间传递业务的开放准则
WorkQueue,任务模型。简单来说就是让多个消费者绑定到一个队列,共同消费队列中的消息
Work模型的使用:·多个消费者绑定到一个消息队列,可以加快消息处理的速度
·同一个消息只会被一个消费者处理
·通过设置prefetch来控制消费者预取的消费数量,处理完一条消息后在进行接
受
Fanout Exchange会接受到消息广播到每一个跟其绑定的queue,所以也叫做广播模式
Direct Exchange 会将接受到的消息根据规则路由到指定的queue,因此成为定向路由。
每一个queue都会与Exchange设置一个Bindingkey
发送者发送消息时,指定消息的RoutingKey Exchange将消息路由到BindingKey与Routingkey一致的队列
Topic Exchange 与DirectExchange用法相似 有一下区别
一:TopicExchange就收到的消息RoutingKey可以是多个单词
二:与队列绑定时的bindingkey可以指定通配符
三:#:代表0或多个单词 *:代表1个单词
二 RabbitMq高级篇
1,生产者可靠性:
①生产者重连:
template: retry: enabled: true #开启重试机制 multiplier: 1 #重试后 时间变为几倍 connection-timeout: 1s #超时多少秒重试
注意:当网络不稳定的时候,利用重试机制可以有效的提高消息发送的成功率,不过SpringAMQP提供的重试机制时阻塞式的,也就是说多次重试等待的过程中,当前线程是被阻塞的,会影响业务性能。如果对于业务性能有要求的话,建议禁用重试机制,如果一定要使用,请合理配置等待时长和重试次数,当然也可以考虑使用异步线程来执行发送消息的代码
②生产者确认:
RabbitMq有Publisher Confirm和Publisher Return两种确认机制。开启确认机制后,在MQ成功收到消息后会返回确认消息给生产者,返回的结果有以下几种情况:
消息投递到MQ,但路由失败,此时会通过publisherReturn返回路由异常的原因,然后返回ACK,告知投递成功。
临时消息投递到MQ,并且入队成功,返回ACK,告知投递成功。
持久消息投递到MQ,并且入队完成持久化,返回ACK,告知投递成功
其他情况都会返回NACK,告知投递失败(磁盘满了)
实现生产者确认机制:
配置:publisher-confirm-type三种模式可选:none:关闭confirm机制
simple:同步阻塞等待MQ的回执消息,correlated:MQ异步回调方式返回回执消息
如何处理生产者的确认消息:
生产者确认需要额外的网络和系统资源开销,尽量不要使用
如果一定要使用,无需开启Publisher-Return机制,因为一般路由失败是自己的业务问题
对于nack消息可以有限次数重试,依然失败则记录异常消息
③数据持久化:
在默认情况下,RabbitMq会将接受到的信息保存在内存中以降低消息收发的延迟
一旦MQ宕机,内存中的消息会丢失,内存空间有限,当消费者故障或处理过慢时,会导致消息积压,引发MQ阻塞
Lazy Queue:
要设置一个队列为惰性队列,只需要在声明队列时指定x-queue-mode属性为lazy即可:
return QueueBuilder.durable("lazy.queue").lazy().build()
RabbitMq如何保证消息的可靠性:
首先通过配置可以让交换机,队列,以及发送的消息都持久化,这样队列中的消息会持久化到磁盘,MQ重启消息依然存在。
RabbitMQ在3.6版本引入了LazyQueue,并且在3.12版本后会称为队列的默认模式,lazyQueue会将所有消息都持久化
开启持久化和生产者确认时,rabbitmq只有在消息持久化完成后才会给生产者返回ACK回执
④消费者确认机制
为了确认消费者是否成功处理消息,RabbitMq提供了消费者确认机制。当消费者处理消息结束后,应该向rabbitmq发送一个回执,告知RabbitMQ自己消息的处理状态,回执有三种可选项:
ack:成功处理消息,rabbitmq从队列中删除该消息
nack:消息处理失败,rabbitmq需要再次投递消息
reject:消息处理失败并拒绝该消息,rabbitmq从队列中删除该消息
SpringAMQP已经实现了消息确认功能,并允许我们通过配置文件选择ACK处理方式,有三种
none:不处理,即将消息投递给消费者后立刻ACK,消息会立刻从MQ删除,非常不安全,不建议使用
manual:手动模式。需要自己在业务代码中掉用api,发送ack或reject,存在业务入侵,但更灵活
auto:自动模式,SpringAMQP利用AOP对我们的消息处理逻辑做了环绕增强每当业务正常执行时则自动返回ACK,当业务出现异常时,根据异常判断返回不同的结果
⑤失败重试机制
当消费者出现异常后,消息会不断requeue(重新入队)到队列,再重新发送给消费者,然后再次异常,再次requeue,无限循环,导致mq的消息处理飙升,带来不必要的压力。
我们可以利用Spring的retry机制,在消费者出现异常时利用本地重试,而不是无限制的requeue到mq队列。在配置中 retry.enabled:ture
在开启重试模式后,重试次数耗尽,如果消息依然失败,则需要有messageRecoverer接口来处理,它包含三种不同的实现:
RejectAndDontRequeueRecoverer:重试耗尽后,直接reject,丢弃消息,默认就是这种方式
ImmdeiateRequeueMessageRecoverer:重试耗尽后,返回,nack,消息重新入队
RepublishMessageRecoverer:重试耗尽后,将失败消息投递到指定的交换机
消费者如何保证消息一定被消费???
开启消费者确认机制为auto,由spring确认消息处理成功后返回ack,异常时返回nack
开启消费者失败重试机制,并设置MessageRecoverer,多次重试失败后将消息投递到异常交换机,交由人工处理
延迟消息:
实现:
①死信交换队列:当一个队列中的消息满足下列情况之一时,就会成为死信(dead letter)
消费者使用basic.reject或者basic.nack声明消费失败,并且消息的requeue参数设置为false
消息时一个过期时间,超时无人消费
要投递的队列消息堆满了,最早的消息可能成为了死信
如果队列通过dead-letter-exchange属性指定了一个交换机,那么该队列中的死信就会投递到这个交换机中,这个交换机就称为死信交换机