7_RabbitMQ

RabbitMQ

1. MQ是干什么的?

MQ:
使用场景
在我们秒杀抢购商品的时候,系统会提醒我们稍等排队中,而不是像几年前一样,页面直接卡死或报错给用户。

	像这种排队结算就用到了消息队列机制,放入通道里面一个一个结算处理,而不是某个时间断突然涌入大批量的查询新增等操作直接把数据库给搞宕机掉,
	
	所以RabbitMQ的本质上起到的作用就是:削峰填谷,为业务保驾护航。

2. RabbitMQ介绍.

RabbitMQ是三大中间件之一mq中的一个产品.

MQ的产品:
		activeMQ - RocketMQ - RabbitMQ - Kafka(大数据领域)

RabbitMQ的优势:
	一般的项目的数据量不是特别的大,不追求吞吐量,而追求稳定性,安全性,扩展性,高性能.而这些正是Rabbitmq的特点。  
	RabbitMQ 是用 erlang语言写的,因此性能极好。

Kafka特点 -> 追求高吞吐量.	-> 基于(大数据中的组件)Zookeeper管理.

RabbitMQ完全基于一个规范,amqp规范.
	Advanced Message Queuing Protocol,高级消息队列协议
	
而spring针对amqp的整合,只整合了RabbitMQ.

3. MQ的应用场景/三大优势.

MQ三大优势:
          1. 解耦
          2. 异步处理
          3. 削峰

-----------------------------------------

1. 解耦.
	有了MQ之后,各个组件、服务模块之间,通过向MQ发布和订阅消息来完成通信,大大的降低了系统的耦合度。

    

2. 异步处理.
比如:
	a服务调用b服务,b调用c,c调用d,假设都是1秒,则整个服务调用过程下来也至少需要4秒;
	有了MQ之后,如果说a服务调用完成后,主线的业务已经完成可以给用户响应了,则a服务直接发送消息到MQ中,然后去给用户做响应就可以了。
	其他的服务直接在MQ中订阅需要的消息就可以了,这样就以异步的方式完成了处理。
	 

3. 削峰.
比如:外卖业务
	8 ~ 11点风平浪静.
	11 ~ 14点,并发量突然的剧增.
	这时zuul网关来接收请求.在不做限流的情况下,zuul每秒可以接收10000个请求.
	而order订单模块,每秒只能处理100个请求.
	如果不用MQ的话,zuul一下子分发给order订单模块10000个请求,那么order订单模块很可能就崩掉了。
     而在有MQ的情况下,zuul将消息发布到MQ中,然后让order这个消费者服务模块来做削峰,即;规定让order每秒只从MQ中订阅100个消息来处理。从而避免了order服务模块发生崩溃。
     这就是削峰。

4. RabbitMQ的架构.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h8r7T7BN-1578474371912)(mdpic/RabbitMQ.PNG)]

RabbitMQ的基本组件以及执行流程:

如上图所示:

	RabbitMQ的一个进程、一个server服务称为:一个server;
	一个server可以有多个virtual host虚拟机(此虚拟机非Linux中的那种虚拟机),默认的virtual host虚拟机叫做:/  ,是由guest用户去管理。

RabbitMQ的3个主要组件:
		1. exchange:交换机
		
		2. connections和channels: 负责将生产者、消费者连接到MQ上;	细分就是生产者或消费者先通过connections和RabbitMQ建立起连接,然后生产者或消费者再通过channels去发布和订阅消息。
		
		3. queue:队列;
		

流程:
	1. 生产者通过channels和connections连接到RabbitMQ之后,直接把消息发布到exchange交换机上;

	2. 一个exchange绑定着多个队列queue,绑定的规则叫做Routes或Routing key,绑定的方式有很多种,我们这里用的这个方式叫做:通配符绑定,

	3. 通配符绑定:
		每一个queue队列们都是有一个唯一的标识的,比如:橙色、blue、queue;
		生产者发布的消息(msg)里边也会带有一个指定的标识(比如:*色),如果这个消息的标识与某个或多个队列的标识匹配成功了,那么exchange交换机会把这个消息(msg)发送到该队列或匹配到的多个队列里边;

	4.  而消费者就比较简单一些了,它只需要订阅了某一个队列即可,队列里边只要有消息它就可以消费到,队列里边没有消息它就一直等待着消息。

 
------------ 
 注意事项1: 
 
	需要注意的是:生产者和RabbitMQ通过connections建立连接的时候,就需要指定好要连接到哪一个virtual host虚拟机。


------------- 
注意事项2: (一个队列中的数据只能被一个消费者消费到!)

	可以有多个消费者去对应这一个队列,但是这一个队列中的消息一次只能被这多个消费者中的一个消费者消费到,其他的消费者这一次是没有消费到这个消息的。
	
	如果说你想让这个消息可以一次被多个消费者消费到,那你就去准备多个队列,让这个消息去匹配到这多个队列上,然后通过exchange将这个消息发送到多个队列中,这样就可以一次有多个消费者消费到这个消息了。

5. RabbitMQ的7种通讯方式/工作模式.

第一个,简单模式: 一个生产者,一个队列,一条消息,一个消费者;
	 简单的通信,一个生产者发布一个消息,然后这个消息被一个消费者消费,就是简单的你那边发一个、我这边收一个。
第二个,work模式:一个生产者,一个队列,一条消息,多个消费者;
	 一个生产者发布一个消息,exchange交换机再将这个消息放入到一个队列中,多个消费者共同监听这一个队列,队列中有消息时,这多个消费者开始争抢消息,谁先抢到谁消费。没抢到的接着等待。
	 还是保证一条消息只能被一个消费者使用。
	 
--- 前两种是简单的方式,你压根就不需要指定交换机,MQ会帮你自动生成  ------------- 

第三个,发布与订阅模式。一个生产者,多个队列,多条消息,多个消费者;
	 生产者把消息发送到exchange交换机,交换机把这个消息发送到所有的队列中,每一个队列中的消息还是对应一个消费者消费到。
第四个,routing路由模式,一个生产者,多个队列,多条消息,多个消费者;
	 生产者将消息发送给exchange交换机,交换机再按照routing路由判断,
	 路由是字符串, 生产者提供的消息里面携带有路由字符(即,对象的方法),
	 交换机根据路由的key,匹配上对应的队列,
	 然后那些队列对应的消费者才能消费到消息;
第五个,topic通配符模式,一个生产者,多个队列,多条消息,多个消费者;
	  生产者将消息发送给exchange交换机,
	  交换机再按照通配符 匹配上对应的队列,
	  然后那些队列对应的消费者才能消费到消息;
	在RabbitMQ中做RPC是很简单的。客户端发送请求消息,服务器回复响应的消息。
流程:	
	1、消费者 向RPC请求队列发送RPC调用消息,同时监听RPC响应队列。
	2、消费者监听RPC请求队列的消息,收到消息后,进行处理,并生成返回结果
	3、生产者将RPC方法的结果发送到RPC响应队列
	4、消费者监听RPC响应队列,接收到RPC调用结果。
第七种,ack机制模式(消费者应答模式)
	 当消费者收到消息后,再通过回调函数给MQ一个ACK应答,表示已经收到了消息,然后MQ收到ack应答之后,会将这个消息从队列中删除掉,避免该消息被重复消费。

6. Rabbitmq的几种交换机模式

最新版本的RabbitMQ有四种交换机类型,分别是:Direct exchange、Fanout exchange、Topic exchange、Headers exchange。
注意: 消息始终都是先发送到交换机,由交换级经过路由传送给队列,消费者再从队列中获取消息的!
(一) Direct Exchange
 – 处理路由键。(对应的工作模式:routing路由模式)
 
 	需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。这是一个完整的匹配。
 	如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记为“dog”的消息才被转发,不会转发dog.puppy,也不会转发dog.guard,只会转发dog。(一对一的匹配才会转发)
(二) Fanout Exchange
 – 不处理路由键。(对应的工作模式:发布与订阅模式)
	你只需要简单的将队列绑定到交换机上,一个发送到交换机上的消息都会被转发到所有与该交换机绑定的队列上。
(三) Topic Exchange
– 将路由键和某模式进行匹配。(对应的工作模式:topic通配符模式)
	此时队列绑定到交换机需要一个模式的匹配。
	符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。
	因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。(匹配才会转发)
(四)Headers Exchanges
 - 不处理路由键。
 	队列绑定到交换机,是根据交换机中的消息的内容中的headers属性进行匹配的。
 	在绑定Queue与Exchange时指定一组键值对;当消息发送到RabbitMQ时会取到该消息的headers与Exchange绑定时指定的键值对进行匹配;如果完全匹配则消息会路由到该队列,否则不会路由到该队列。
 	
 	headers属性是一个键值对,可以是Hashtable,键值对的值可以是任何类型。而fanout,direct,topic 的路由键都需要要字符串形式的。

7. 如何保证生产者的消息送达MQ?

1)基于AMQP的事务 来保证:
使用基于amqp的事务.
     amqp规范提供三个方法.
                         开启事务.
                             
                         提交事务.
                             
                         回滚事务.


缺陷很明显:
	AMQP的事务操作,确实可以保证消息的送达.
	但事务操作严重的影响到了RabbitMQ的性能,所以不建议使用。
2)RabbitMQ的Confirm机制 (来保证)
引言:
	事务是amqp协议中已经提供了规范的,RabbitMQ只需要实现即可;

	而confirm是RabbitMQ特有的一个机制,在amqp规范中是没有提及到的;

--------------------------------------------

confirm:确认;

生产者在发布消息前,开启confirm机制,confirm机制和txSelect(事务)不能同时使用)

confirm的三种实现方式:

	第一种方式:普通confirm模式,
			生产者publish了一条消息后,等待MQ服务器端来确认是否收到了消息;
			如果MQ服务端返回给生产者一个false或者因为超时未返回,
			生产者会进行消息重传。 

	第二种方式:批量confirm模式,
			在普通模式的基础上,改为生产者批量发送消息,通过confirm去判断批量处理是否成功,如果有一个失败,直接抛出异常.
			然后生产者会进行消息重传。
			

----------- 上面这两个是同步的,第三种才是异步的 ----------------------------

	第三种方式:异步confirm模式, 
			异步confirm模式的优点,就是执行效率高,生产者不需要 一直等待着 确认消息是否发出去了,它只需要开启一个监听器即可。
		 	MQ服务器会给生产者一个回调函数:ack或者nack
				->如果生产者收到了ack函数
							意味着消息发送成功.
                	->如果生产者收到了nack函数
							意味着消息发送失败.
                
 	总体情况:第二种Confirm批量确定和第三种Confirm异步模式性能相差不大,Confirm模式要比事务快10倍左右。
3)相关面试题:
1. 如何避免生产者给MQ发送重复消息?

	答:	首先呢,无论什么样的MQ都存在这种问题,其实消息重复并不可怕,可怕的是没有考虑到消息重复之后,如何保证幂等性。
	幂等性,通俗点说,对于同一种行为,如果执行不论多少次,最终的结果都是一致相同的,就称这种行为是幂等的。
	所以关键在于怎么保证消息队列消费的幂等性?
	
	其实这需要结合具体的业务来做处理:
	
业务场景1:
	比如,如果这个操作本来就是幂等性的,比如:删除或者查询这样的操作,那么么得问题,重复消费也无所谓。

业务场景2:
	比如,你是通过MQ,但最终是要将数据给写入Redis中的话,那根本就不用管,Redis具有天然幂等性,每次都是set。

业务场景3:
	比如,你是通过MQ,但最终是要将数据给写入数据库中的话,那就更好办了,数据库可以设置唯一键,就是用来防止重复数据的插入,所以可以先根据唯一键查一下,如果这数据已经存在了,那就不要插入了,仅仅update 一下、或者直接丢弃掉该消息,就完事儿了。 

业务场景4:
	比如,以上场景都不适用,那你可以使出杀手锏,让生产者每次给MQ发送消息的时候,都给消息加上一个全局唯一id(比如UUID)。
	然后MQ在收到了这个消息之后,先把这个 id 给存入到Redis。
	之后MQ每次收到生产者发来的消息时,就先去Redis中查一下,是否已存在这个全局唯一id;
	如果这个id不存在,也意味着不是重复消息,于是MQ就处理这个消息,并把这个消息的 全局唯一id 写入Redis。
	如果这个id在Redis中已经存在了,就说明已经消费过了,那就直接丢弃掉该重复消息,从而避免重复消费。
	

2. 如何保证消息不丢失,比如MQ在接收到生产者的消息之后,直接宕机了:
	答:开启队列的消息持久化机制即可.
		
		
3. 生产者在发送消息时,可能由于网络延迟,导致消息丢失; 或者(如果消息刚刚到达MQ,MQ还没持久化就宕机了),该怎么办?
	答:1. amqp包含事务操作,可以启动amqp的事务操作;
	   2. 如果对性能有要求的话,那么可以使用RabbitMQ的confirm机制(即,消息确认机制,使用异步回调函数:ack和nack函数。
	   	MQ中的消息持久化成功,则MQ返回给生产者一个ack函数,不成功则返回一个nack函数,如果返回给生产者的是一个nack函数,那么生产者会再次重试去发消息,如果重试很多次还是失败的话,那么生产者就转而做日志记录,然后进行人工处理);		

8. RabbitMQ如何保证将消息推送给消费者?

1)ACK消息确认机制:
1. 为什么有ACK消息确认机制?
	如果在处理消息的过程中,如果消费者的服务器在处理消息的时候出现异常,那么可能这条正在处理的消息就没有被消费掉,从而会造成数据的丢失。
	为了确保数据不会因此而丢失,RabbitMQ支持一种机制:消息确定机制-ACK。


2. ACK的消息确认机制:
  	答: ACK机制是指:消费者从RabbitMQ收到消息并消费完成后,需要给RabbitMQ一个反馈,即ACK消息确认,RabbitMQ在收到ACK消息确认以后,才会将此消息从队列中删除掉。
     如果一个消费者由于网络不稳定、服务器异常等现象,没有真正消费掉该消息,那么也就不会给MQ一个ACK消息确认,RabbitMQ没有收到ACK,就明白了该消息并没有被正常的消费掉,于是就会重新将该消息放入队列中,从而推送给其他的消费者。
     这样就可以保证了,万一某个消费者发生了故障,不会因此而丢失掉消息。

2)相关面试题:
----面试题:---------------------------

1、如果消费者确实已经消费了消息,而由于网络等情况,没有成功的给MQ一个ACK反馈,这时该怎么办?    
	答:首先呢,只有当消费者正确的发送了ACK反馈,RabbitMQ收到之后,才会将消息给删除掉。否则,消息永远不会从RabbitMQ中删除。
	刚才这种情况造成的问题就是,RabbitMQ没有收到ACK反馈,它就会认为该消息没有被消费掉,于是就会将该消息再次放入到队列中,推送给其他的消费者,这样的话就造成了重复消费。

办法就是:
		可以开启RabbitMQ的重试机制,重试次数默认为3次。如果MQ把该消息一直发送给该消费者3次,都没有收到该消费者的ACK反馈,那么RabbitMQ就会将这条消息给删除掉。
    
    
    
    
2、如果忘记设置让消费者进行ACK,会产生什么问题:
	答:忘记设置让消费者进行ACK,会产生内存泄漏的问题。
	解释:忘记设置让消费者进行ACK反馈,则MQ一直没有收到ACK确认,那么MQ会一直把该消息发送给其他的消费者,但,无论如何,该消息就是无法从RabbitMQ中给删除掉,所以也就意味着形成了内存泄漏的问题;
	
	
	
3、消费者在接收到消息后,默认是自动应答MQ,那么如果在消费者执行逻辑操作时,出现异常,怎么办?
	答:改为手动ack应答;
	   只有在消费者把业务的逻辑全部成功执行之后,才手动的给MQ一个ack应答。
	   这样就确保了消费者端不会出现数据丢失的问题。	

9. 如何保证消息的顺序性.

答:

方式1:
	可以在本地内存中声明一个队列结构.
	然后这个本地队列的一端只负责保存消息,另一端只负责将消息分发给不同的消费者。这样即可实现消息的有序性。
	在本地实现队列结构.用LinkedList即可实现.

方式2:
	其实保证消息的有序性的最彻底的方式还是:一个队列只对应着一个消费者这种方式;
	即,将一个queue对应一个consumer,不要一个queue对应多个consumer.

10. RabbitMQ和feign的区别:

举例:
	就相当于 打电话和发短信的区别,feign是打电话, mq是发短信。(不完全类似)

	电话,你必须得同步接听,两人开始直接聊天;

	发短信是生产者先发短信,短信到达MQ服务器,MQ服务器再将短信推送给消费者;

	发短信这个操作,生产者不知道消费者收到了短信没有,也不知道消费者具体是哪一位;

	打电话是,对方必须同步的接听,而且打电话的人也必须清楚它要给谁打电话;
	
-------------------	

总结: A与B
1.
	feign:
		A和B只能是服务与服务;
	MQ:
		A和B可以是服务与服务、服务与SpringCloud组件、SpringCloud组件与SpringCloud组件;

2.
	feign:
		同步操作,A服务直接调用B服务,B必须同步接收到A的调用;A十分明确的知道它要调用B这个服务;
	MQ:
		异步操作,A先将消息发到MQ服务器,MQ服务器再将消息推送给所有需要该消息的人,比如:B、C、D等;A根本不知道它发的消息是被具体的谁消费了、被几个人消费了;
		与生产者和消费者直接交互的对象都只是mq服务器而已。

人开始直接聊天;

发短信是生产者先发短信,短信到达MQ服务器,MQ服务器再将短信推送给消费者;

发短信这个操作,生产者不知道消费者收到了短信没有,也不知道消费者具体是哪一位;

打电话是,对方必须同步的接听,而且打电话的人也必须清楚它要给谁打电话;

总结: A与B
1.
feign:
A和B只能是服务与服务;
MQ:
A和B可以是服务与服务、服务与SpringCloud组件、SpringCloud组件与SpringCloud组件;

  1. feign:
    同步操作,A服务直接调用B服务,B必须同步接收到A的调用;A十分明确的知道它要调用B这个服务;
    MQ:
    异步操作,A先将消息发到MQ服务器,MQ服务器再将消息推送给所有需要该消息的人,比如:B、C、D等;A根本不知道它发的消息是被具体的谁消费了、被几个人消费了;
    与生产者和消费者直接交互的对象都只是mq服务器而已。



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值