RabbitMQ完整复习流程
用户分布式系统之间进行通信;
概念
订单-----将消息放到mq,消费者从mq中拉取;
把mq当作服务器;生产者和消费者当作客户端;
优势
- 应用解耦:系统之间交互多;
- 异步提速:将数据放到mq之后就可以直接返回;
- 消峰填谷:将请求放到mq,系统重mq中获取请求处理;提高系统稳定性;
缺点
系统可用性降低;必须保证mq的高可用;
系统复杂度大大提升;如何保证消息不丢失?
MQ 产品
RabbitMQ简介
基础架构图:
安装使用
-
安装erlang 环境
rpm -ivh erlang-18.3-1.el7.centos.x86_64.rpm
- 安装rabbitMq
rpm -ivh socat-1.7.3.2-1.1.el7.x86_64.rpm --force --nodeps
rpm -ivh rabbitmq-server-3.6.5-1.noarch.rpm
- 开启管理界面
rabbitmq-plugins enable rabbitmq_management
vim /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.5/ebin/rabbit.app # 比如修改密码、配置等等,例如:loopback_users 中的 <<"guest">>,只保 留guest
- 启动
service rabbitmq-server start # 启动服务
service rabbitmq-server stop # 停止服务
service rabbitmq-server restart # 重启服务
工作模式
第二种与第三种的区别:后者可以重复消费;
- 简单模式:一对一
- work queue:一对多;
- 发布订阅:
- 路由模式:
- topic通配符模式:
总结:
springboot 整合rabbitmq
-
导入依赖
-
编写yml配置文件,指定基本信息
-
定义交换机、队列、以及绑定的关系
-
注入RabbitTemplate调用方法完成消息发送
高级特性
- 消息可靠性投递:
消息发送方杜绝消息发送丢失或投递失败;RabbitMq提供了两种模式来解决这个问题;
- confirm确认模式: 交换机异常,消息发送不到指定交换机
- 退回模式Return:队列异常,通常出现在路由模式或topic模式;数据到达交换机但是没有指定的队列接受该数据;
abbitmq 整个消息投递的路径为:producer—>rabbitmq broker—>exchange—>queue—>consumer
消息从 producer 到 exchange 则会返回一个 confirmCallback 。消息从 exchange–>queue 投递失败则会返回一个 returnCallback 。我们将利用这两个 callback 控制消息的可靠性投递
- Consumer ACK:消费者确认消息
rabbitmq提供了三种确认消息方法
- 手动确认:acknowledge=“manual” 消息到达消费者手中完成相关业务后手动签收,调用channel.basicAck()
- 自动确认:acknowledge=“none” 消息一到消费者手中就自动签收;
- 根据异常情况确认:acknowledge=“auto”
消息拒绝签收问题:就像包裹拒绝签收一样,到底是退回还是丢弃?
channel.basicNack("消息id","","是否重新放回队列")
;
- 消费端限流:
消息必须手动确认;
配置prefetch每次抓取多少消息;
- TTL:配置过期时间
- 当消息到达存活时间后,还没有被消费,会被自动清除
- RabbitMQ可以对消息设置过期时间,也可以对整个队列(Queue)设置过期时间
- 设置队列过期时间使用参数:x-message-ttl,单位:ms(毫秒),会对整个队列消息统一过期。
- 设置消息过期时间使用参数:expiration。单位:ms(毫秒),当该消息在队列头部时(消费时),会单独判断这一消息是否过期。
- 如果两者都进行了设置,以时间短的为准。
- 死信队列DLX:当消息成为dead message后,会发送到另一个交换机;叫做DLX(Dead Letter Exchange)
为什么会有死信?
- 队列消息长度到达限制;
- 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
- 原队列存在消息过期设置,消息到达超时时间未被消费;
消息存放在队列中,那么就需要为消息绑定死信队列;给队列设置参数: x-dead-letter-exchange
和 x-dead-letter-routing-key
其中死信队列和死信交换机没有区别;消息成为死信后会被重新路由到配置好的DLX,DLX再路由到死信队列;
- 延迟队列:消息到到队列后不会被立马消费,而是到指定时间后才会被消费;
打个比方:1. 下单后,30分钟未支付,取消订单,回滚库存。 2.新用户注册成功7天后,发送短信问候。
如何实现? TTL + 延迟队列
但是!RabbitMQ 并没有提供延迟队列功能;所以就只能使用ttl+死信队列来实现了!
思路;过期的消息会放到死信队列;那么只要创建消费者监听死信队列中的消息即可;死信队列中出现消息就会被消费,就能巧妙的实现延迟队列的效果!
-
消息幂等性: 乐观锁
幂等性指一次和多次请求某一个资源,对于资源本身应该具有同样的结果。也就是说,其任意多次执行对资源本身所产生的影响均与一次执行的影响相同。 在MQ中指,消费多条相同的消息,得到与消费该消息一次相同的结果。
如何防止消息重复消费,消息积压
- 消息积压:
- 消费者宕机积压
- 消费者消费能力不足积压
- 发送者发流量太大
解决方案:上线更多的消费者,进行正常消费;
上线专门的队列消费服务,将消息先批量取出来,记录数据库,再慢慢处理
RabbitMQ如何保证消息顺序性
比方现在有三条消息:插入,更新,删除;
将这三条消息发送到消息队列,如何才能保证消息的顺序呢?
首先可以确定的是,多个消费者肯定不行,会出问题,那就简单点,
- 使用一个消费者消费同一个队列就行;
这样就ok了嘛?并不是 ;如果该消费者里面进行多线程操作的话,依旧保证不了顺序;
所以:需要明确两个点:
- 队列中的消息必须有序
- 消费者获取的消息必须有序;
第一点不用考虑,因为消息的推送本来就是有序的,所以不需要考虑它,只需要将问题在消费这消费这里解决就行;
消费者要想保证顺序,就得保证拿到的消息是有顺序的,也就是消费者只能上一个队列里面拿消息;拿到消息之后本身也得按照顺序执行,也就是顺不同异步,只能同步,处理完一条后在处理下一条,这样就能完美解决了!
可是这样会产生一个问题:这样岂不是需要很多队列?也就是需要很多消费者?如何解决这种问题?
其实也简单,不希望产生多个队列,那就只用一个队列呗;反正队列里面的消息始终都是有序的,我们只需要保证消费者拿到的消息有序就行,那这样就只有一个消费者,我们可以将获取到的消息在消费者内部进行排序不就好了;将具有同样属性的消息放到一个list集合里面(比如对同一条记录操作,那他们的主键ID必然是一样的);这样产生不同的list,在将这些list分发给不同的work执行不就好了;
执行思考就会发现,第二种方式无非就是把消费者又当成了生产者,将消息进行过滤后放到list集合,然后又启用不同的消费者来消费罢了,换汤不换药!