Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
为什么使用消息队列?
1、实现异步:批量数据的异步处理
2、实现解耦:串行任务的并行化
3、流量削峰:高负载任务的负载均衡
4、实现广播:基于生产者消费者模型
RabbitMQ 工作模型
1、RabbitMQ 通信协议为AMQP高级消息队列协议,区别于javaEE内部的通信协议JMS(Java Message Service),JMS耦合性强绑定于java语言
2、RabbitMQ 工作模型
说明:
Producer:生产者主要用于生产消息并且向Broker发送消息
Broker:RabbitMQ 所在主机
Virtual Host:虚拟主机,当一个公司有非常多的业务系统,这时所有的系统都需要创建自己的Exchange、Queue以及他们之间的绑定关系来实现基于MQ的消息通信,这种情况下一台主机会出现非常多的Exchange、Queue,这是非常混乱的,为了解决这个问题会在一个Broker创建很多的Virtual Host,不同Virtual Host之间是完全隔离,不可见的,通过这种方式解决了资源利用率和资源隔离的问题,类似于java的namespace
Exchange:交换机,实现消息灵活的分发,消息来了后Exchange会根据一定的规则把消息分发到不同的Queue,RabbitMQ 是不会把消息直接发送到Queue里,RabbitMQ 最常用只有三种交换机
Queue:队列,用来存储消息,是一个独立运行的进程,本身采用使用erlang语言编写的数据库,把消息保存在磁盘或者内存里面
Connection:TCP长连接,请求来了后,Broker会创建释放长连接当并发特别多的时候特别影响性能,考虑到资源利用率RabbitMQ 会在Connection里面引入Channel(虚拟连接)概念,当请求来了后只需要keep tcp长连接,在连接里面创建、释放Channel就可以了,可以大大节省系统资源
Channel:通道、虚拟连接
Consumer:消费者,当消息通过Producer发送到Exchange后,根据绑定规则查找消息路由到哪个Queue上去,此时Consumer会监听一个Queue,从中获取消息
交换机
1、Direct Exchange ——直连交换机
说明:
工作流程:首先创建一个直连类型交换机 GupaoDirectExchange,在创建四个队列 SPRING_QUEUE、DUBBO_QUEUE、MYSQL_QUEUE、TOMCAT_QUEUE,通过binding key把队列绑定到交换机上,发送消息的时候必须携带一个路由关键字,当消息到达直连类型交换机 GupaoDirectExchange时会根据路由关键字查询匹配队列。
实例:
// 向 GP_DIRECT_EXCHANGE交换机发送一个spring类型的消息为 today is sunday
channel.basicPublish("GP_DIRECT_EXCHANGE","spring","today is sunday ");
// 向 GP_DIRECT_EXCHANGE交换机发送一个tomcat类型的消息为 today is Saturday
channel.basicPublish("GP_DIRECT_EXCHANGE","tomcat","today is Saturday");
2、Topic Exchange ——主题类型交换机
说明:
工作流程:同直连交换机一样
实例:
# 代表0个或多个单词
* 代表1个单词
// "junior.netty"参数说明:绑定规则可以匹配的有 ‘junior.#’、‘#.netty’,向JUNIOR_QUEUE、NETTY_QUEUE队列发送消息
channel.basicPublish("GP_TOPIC_EXCHANGE","junior.netty","today is Saturday");
3、Fanut Exchange —— 广播类型交换机
工作流程:没有任何关键字,所有的队列绑定到交换机上。当交换机接收到消息后,会分发到所有和当前交换机绑定的队列上
消息可靠性投递分析
1、如何确保消息发送到RabbitMQ服务器?
答:需服务端确认,有两种方式可以实现
事务模式
//将channel(发送消息通道)设置成事务模式(和数据库的事务不是一回事,仅仅只是做事务确认)
channel.txSelect();
//提交事务
channel.txCommit();
//事务回滚
channel.txRollback();
该种方式的缺点很明显:1,阻塞的,发送一条消息必须在commit后才可以发送,基本会榨干rabbitmq服务器的资源,大大降低发送消息的速率,发送一条确认一条效率低下
Confirm模式
//将channel设置为confirm模式
channel.confirmSelect();
if(channel.waitForConfirms){
//消息发送成功
}
2、如何确保消息路由到正确队列?
答:当有路由映射失败的时候(这种情况非常少)设置路由保证,路由保证有两种方式可以实现
1、在channel上设置参数 mandatory = true + ReturnListener
2、备份交换机
3、如何确保消息正确的在队列存储?
答:队列是一个独立运行的进程,使用了elang编写的一个数据库将数据保存在内存中,而内存中的数据做持久化有以下三种方式
1、队列持久化
2、交换机持久化
3、消息持久化
4、如何确保消息从队列正确的透底到消费者?
答:需消费者应答,可以通过以下方式实现
channel.basicAck();//手工应答
channel.basicReject();//单条拒绝
channel.basicNack();//批量拒绝
5、如果以上四个措施都执行了能保证消息一定送达吗?
答:不能的,因为流程中各个环节是孤立的,如果这种情况发生了可以通过以下几种方式实现
1、消费者回调 (收到消息后告诉生产者,1、假如消息发送十分钟还无应答,可以用定时任务重发或者确认,2、应答消息)
2、补偿机制 (消费者挂了,1、消息重发(限制次数),2、异步对账)
3、消息幂等性
6、如何实现延时队列(比如电饭煲的定时,过期退款)?
答:1、延时队列插件
2、TTL(Time To Live )
1、队列里面消息过期时间
2、单条消息的过期时间
3、死信交换机,创建队列的时候指定一个死信交换机,如果消息没有设置过期时间,当消息过期后可以发送到死信交换机,通过死信交换机去处理消息,死信交换机也是一个普通的交换机可以绑定队列。
7、如何保障顺序消费(多个生产者发送消息队列,有多个消费者的时候)?
答:1、一个队列只有一个消费者
2、全局id(MsgID,parentID)
this is end ,编写不易,看完记得点赞哦!