一、什么RabbitMq
- MQ就是message queue,即消息队列
- 一个处理即时消息/延时消息的中间件
- 一个传递消息的中间站
二、实际应用场景
- 消息通讯
如短信通知,实时聊天等 - 数据量大的时效性任务
如订单到时自动失效,定时任务,延时通知等 - 限制流量
在双十一,618等期间订单的暴增会对服务器造成极大的负担,通过rabbitMq的消息队列的限流可以为服务器减载
三、最基本的框架
网络异常,图片无法展示
|
最简单的理解就是,producer生产者,即产生消息方,发送到中间件rabbitmq,通过交换机,路由等放入指定queue队列中,然后然后consumer消费者,即消息处理/接受方,获取到队列中的消息
四、轮询与不公平分发
1. 轮询
- 原理图
网络异常,图片无法展示
|
此处的工作线程其实就是一个个的消费者,队列中的消息一开始会随机给到一个线程的消费者,然后下一个消息就会给下一个工作线程,如下图,若先是a收到消息,则下一条消息会给b,然后c,接着继续a,就轮着来
- 缺点:太理想,效率低
2. 不公平分发
- 理解:相比于轮询分发来说的(即helloworld那种消费方式),简单来见就是能者多劳,处理消息快的消费者就会多收消息,处理慢的少收消息
- 原理
channel.basicQos(int prefetchCount)方法:假设此处设置为1,那么当该消费者接收到一个消息后,还没应答前不会再接受消息了,而消息会被发到那些应答成功的消费者去
- prefetchCount为服务会分发的消息的最大数量
- basicQos:qos即quality of server,即服务的质量
- 因此通过channel.basicQos(数量)可以设置每个消费者一定会收到多少条消息
- 实例演示前提:消费者C1处理消息比较慢(线程睡眠来模拟快慢),消费者C2处理消息比较快1、在消费者C1消费前调用channel.basicQos(1)
2、则在连续发送多条消息后,可以发现除了第一条消息是C1处理的,其他的都是C2处理的。原因:第一条消息的分发是多线程随机分发的,因此发给了C2,由于C2设置了最大通道量为1,因此在收到了aa第一个消息后,由于处理时间较长,还没处理完的这个期间,C2就不再接受消息了,即阻塞,因此消息全部由C1接受
五、防止消息丢失
1. 路线
网络异常,图片无法展示
|
2. 手动应答
- 消息应答的概念
- 基本应答方法
- 消息自动重新入队机制一个消费者处理失败的消息会重新进入mq继续排队然后给下一个空闲的消费者处理,使消息不丢失
3. 持久化
- 作用:前面我们保证了消息不丢失是在rabbitmq正常运作而消费者出问题的情况下,若rabbitmq宕机了,那么消息和队列都会不见!此时我们就要对队列和消息分别设置持久化
- 持久化的方法不多赘述,这里讲几个要点
- 已经声明(已存在)的没有设置持久化的队列在mq重启时就会丢失,对已经存在的队列就设置不了持久化,需要通过面板把它删掉,然后再创建
4. 发布确认
- 前面我们讲到的防止因宕机而导致消息丢失的两个方法,消息持久化&队列持久化,做到这两点还是有可能导致数据丢失,即消息刚好要持久化之前就宕机了(还没来得及保存到磁盘上),这样还是会导致消息丢失
- 因此发布确认的出现就是防止消息的这一部分丢失
- 概念
- 简单来讲就是,为了防止消息未存入磁盘之前就宕机导致数据丢失,需要在存入磁盘后通过mq通知生产者消息已经发布成功了,不成功生产者再进一步处理,这样才能真的保证所有消息真真正正地不丢失!
- 发布确认三种方式,其中异步确认性能最好,效率最高
- 三种发布确认的原理举例
- 单个确认/批量确认:寄件人(生产者)自己打电话问快递站包裹(消息)到站(mq)了没,没确认前无法继续寄包裹(发消息不丢是)
- 异步确认:寄件人(生产者)发包裹(消息)后,快递站(broker)主动通知包裹是否到达(确认),寄件人专心发剩下的包裹,不需要打电话确认
- 消息队列可以看作一个map,当发布成功后会通过回调函数返回消息的key值,未确认的也会通过回调函数返回消息的key值,回调函数实际上是一个监听器,监听broker,是另开了一条线程,因此对原来线程的发布消息没有影响
六、交换机
- 是什么、rmq中,生产者的消息从不直接发给队列,而是发给交换机,并由交换机发给队列,交换机通过routingkey这个标识找到绑定的队列然后将消息发给它们
- 注意生产者类关注交换机和路由键(routingkey)消费者类关心队列名、交换机、路由键的绑定!最好两个都声明交换机
七、其他
- 延迟队列死信的另一种表现形式
- fanout只能群发,direct只能发给对应的队列,而不能将某个消息同时发给两个指定的消费者(当然可以使其routingkey一致,但是使其一致后又无法单独发送给指定的了,限制很大)
- 交换机如果已经创建过了,且在页面中绑定过,在可视化页面可以找到的话,那么在java中使用都不用声明了,可以直接通过名称使用