目录:
(1)SpringAMQP的基本介绍
(2)SpringAMQP-入门案例的消息发送
(3) SpringAMQP-入门案例的消息接收
(4)SpringAMQP-WorkQueue模型
(5)SpringAMPQ-发布订阅模型介绍
(6)SpringAMQP-FanoutExchange
(7)SpringAMQP-DirectExchange
(8)SpringAMQP-TopicExchange
(9)SpringAMQP-消息转换器
(10)面试知识点:
(1)SpringAMQP的基本介绍
上面的官方提供的简单队列模型消息发送与接收的api,写起来非常的麻烦,而SpringAMQP可以大大简化消息发送和接收的api
(2)SpringAMQP-入门案例的消息发送
这里我们使用单元测试来做的,将来肯定不是单元测试,是在微服务里做业务,有人支付成功了,我想发送一个消息,在支付之后的业务里加上,发送消息的代码就可以了。
首先配置文件进行配置:
编写测试类:发送消息 :是Springboot项目,单元 测试需要加两个注解:
对列中接收了一条消息:
(3) SpringAMQP-入门案例的消息接收
第一步引依赖,在父工程已经做了,这里不用在引了
消费者去监听消息,Spring已经帮助我们跟MQ建立了连接,连接的事不用管了,唯一需要管的事是,我要去监听那个对列,监听到这个队列后干什么事
首先进行配置配置文件:
创建类:
这个类是Spring中的Bean,接收消息是有Spring来处理的,所以启动主启动类,就可以了:
(4)SpringAMQP-WorkQueue模型
这里有两个消费者,有一条消息,消费者一接收到,消费完就会删除,消费者二就不会拿到这个消息,如果有50条消息,不会每个人50条,而是他两各自处理一部分,合起来是50条
只有一个消费者不行吗?比如这个消费者每秒处理40条消息,发送者每秒发送50条,那么这个消费者消费不完,每秒会多出来10个消息没人处理,这个消息怎么办呢?只能堆积在队列当中,那么队列是有存储上限的,最终会堆积满的,有两个消费者的话会不会堆积消息
从测试类里面,定义第二个方法:
消费者让他们分被睡ji
启动消息的发送者的代码:消息平均的分配给这两个消费者,消费者1拿的是偶数,消费者二拿的是奇数,它没有考虑消费者的消费能力,平均接收,这是MQ的一种机制决定的,消息的预取机制
消息预取:是先把消息都拿过来,有多小消息拿多少,不管能不能消费 这样消息会先平均的分配到消费者
在消费者的配置文件中加这个配置:每次都取一条消息,取完再拿,这样就不会出现消息先预取过来的情况了
重新启动消费者的启动类:
重新发送消息:消费者一消费了大量的消息,消费者二消费的比较慢,它只消费了几条消息
这样就起到了能者多劳的效果了,消费者一消费能力块,所以消费的消息多
(5)SpringAMPQ-发布订阅模型介绍
简单模型、工作对列模型发送的消息,他们有一个共同的特点,就是它发送的消息只能被一个消费者消费,一旦消费完就会从对列中删除
它是无法满足课程开始时的需求,用户支付完,去通知其他服务:订单服务、存储服务、短信服务,这三个服务各自去完成自己的业务,也就是说你发送的这条消息,需要被三个消费者服务都接收到 ,那怎么办?就需要用到发布与订阅
以前的模型是直接发送给对列,而这里是发送给交换机,交换机再把消息转发到队列当中,消息的发送者不需要队列的存在,交换机可以把消息转发给多个队列,如果转发给多个对列,就实现了一个消息被多个消费者消费了,到底交换机发给一个对列还是多个队列,是由交换机的类型决定的,分为三种交换机,他们只负责消息的转发
(6)SpringAMQP-FanoutExchange
交换机:
队列:
添加:
(7)SpringAMQP-DirectExchange
消费者:
发送者:
发送red的签名key,消费者都能接收到
(8)SpringAMQP-TopicExchange
运行:
(9)SpringAMQP-消息转换器
查看消息:发现数据不对,进行了序列话,我们rabbitMQ只支持字节,Spring允许我们发object的对象,说明啊,它会将我们的对象做序列化,用的是jdk的序列化方式,这个序列化有缺点:性能比较差、安全性有问题,数据长度长
在父工程中引入依赖:
发送消息:
在主启动类中声明Bean,他也是一个配置Bean
先清除消息:
重新发送消息:
接收消息:
引来已经在父工程引入过了,创建Bean:
面试知识点:
(10)消息队列-如何顺序消费?
(11)消息队列如何解决消息丢失问题?
因此如何保证MQ不丢失消息,可以从这三个阶段阐述:
生产者保证不丢消息
存储端不丢消息
消费者不丢消息
3.1 生产者保证不丢消息
生产端如何保证不丢消息呢?确保生产的消息能到达存储端。
如果是RocketMQ消息中间件,Producer生产者提供了三种发送消息的方式,分别是:
同步发送
异步发送
单向发送
生产者要想发消息时保证消息不丢失,可以:
采用同步方式发送,send消息方法返回成功状态,就表示消息正常到达了存储端Broker。
如果send消息异常或者返回非成功状态,可以重试。
可以使用事务消息,RocketMQ的事务消息机制就是为了保证零丢失来设计的
3.2 存储端不丢消息
如何保证存储端的消息不丢失呢?确保消息持久化到磁盘。大家很容易想到就是刷盘机制。
刷盘机制分同步刷盘和异步刷盘:
生产者消息发过来时,只有持久化到磁盘,RocketMQ的存储端Broker才返回一个成功的ACK响应,这就是同步刷盘。它保证消息不丢失,但是影响了性能。
异步刷盘的话,只要消息写入PageCache缓存,就返回一个成功的ACK响应。这样提高了MQ的性能,但是如果这时候机器断电了,就会丢失消息。
Broker一般是集群部署的,有master主节点和slave从节点。消息到Broker存储端,只有主节点和从节点都写入成功,才反馈成功的ack给生产者。这就是同步复制,它保证了消息不丢失,但是降低了系统的吞吐量。与之对应的就是异步复制,只要消息写入主节点成功,就返回成功的ack,它速度快,但是会有性能问题。
3.3 消费阶段不丢消息
消费者执行完业务逻辑,再反馈会Broker说消费成功,这样才可以保证消费阶段不丢消息。
(12)消息队列如何保证消息的顺序性?
消息的有序性,就是指可以按照消息的发送顺序来消费。有些业务对消息的顺序是有要求的,比如先下单再付款,最后再完成订单,这样等。假设生产者先后产生了两条消息,分别是下单消息(M1),付款消息(M2),M1比M2先产生,如何保证M1比M2先被消费呢。
为了保证消息的顺序性,可以将M1、M2发送到同一个Server上,当M1发送完收到ack后,M2再发送。如图:
这样还是可能会有问题,因为从MQ服务器到消费端,可能存在网络延迟,虽然M1先发送,但是它比M2晚到。
那还能怎么办才能保证消息的顺序性呢?将M1和M2发往同一个消费者,且发送M1后,等到消费端ACK成功后,才发送M2就得了。
消息队列保证顺序性整体思路就是这样啦。比如Kafka的全局有序消息,就是这种思想的体现: 就是生产者发消息时,1个Topic只能对应1个Partition,一个 Consumer,内部单线程消费。
但是这样吞吐量太低,一般保证消息局部有序即可。在发消息的时候指定Partition Key,Kafka对其进行Hash计算,根据计算结果决定放入哪个Partition。这样Partition Key相同的消息会放在同一个Partition。然后多消费者单线程消费指定的Partition。
.如何保证数据一致性,事务消息如何实现?
一条普通的MQ消息,从产生到被消费,大概流程如下:
生产者产生消息,发送带MQ服务器
MQ收到消息后,将消息持久化到存储系统。
MQ服务器返回ACk到生产者。
MQ服务器把消息push给消费者
消费者消费完消息,响应ACK
MQ服务器收到ACK,认为消息消费成功,即在存储中删除消息。
我们举个下订单的例子吧。订单系统创建完订单后,再发送消息给下游系统。如果订单创建成功,然后消息没有成功发送出去,下游系统就无法感知这个事情,出导致数据不一致。
生产者产生消息,发送一条半事务消息到MQ服务器
MQ收到消息后,将消息持久化到存储系统,这条消息的状态是待发送状态。
MQ服务器返回ACK确认到生产者,此时MQ不会触发消息推送事件
生产者执行本地事务
如果本地事务执行成功,即commit执行结果到MQ服务器;如果执行失败,发送rollback。
如果是正常的commit,MQ服务器更新消息状态为可发送;如果是rollback,即删除消息。
如果消息状态更新为可发送,则MQ服务器会push消息给消费者。消费者消费完就回ACK。
如果MQ服务器长时间没有收到生产者的commit或者rollback,它会反查生产者,然后根据查询到的结果执行最终状态。
如何保证数据一致性呢?可以使用事务消息。
消息队列的应用场景?
为什么使用消息队列。你可以回答以下这几点:
1.应用解耦
2.流量削峰
3.异步处理
4.消息通讯
5.远程调用