Rabbitmq
适用场景:低耦合度场景、削峰、异步操作、调用端不强依赖于结果的使用场景
例如:
1:用户注册成功后发送 邮件,短信,移动锁屏通知等
2:商城秒杀活动,配合redis使用:
1. redis存储商品库存,并在redis中设置每个用户每秒请求不得超过10次
2. 用户在抢购时直接查询redis中的库存,之后判断库存是否大于0,并且每个用户只能抢购成功1次
3. 之后将请求加入rabbitmq队列,
4. 消费者:
1. 消费者拿到消息后再次查询redis,保证有库存
无库存时给用户发送短信等,抢购失败。或者将状态存入redis,从redis中读取结果给用户发送抢购结果
2. 预减redis中的库存
3. 生成订单并插入
4. 减少数据库中的库存
- 对用户发送抢购结果
1) 交换机类型
性能排序:fanout > direct > topic 比例大约为 11:10:6
其中还有header交换机,实际应用中较少,这里不做介绍
交换机类型 | 作用 | 描述 |
---|---|---|
fanout | 无路由交换机 | 直接与交换机exchange发生关联,不用routingKey 广播模式,一个消息进来时,投递到与该交换机绑定的所有队列 |
direct | 直连交换机 | 需要绑定routingKey,完全根据绑定key进行投递,不支持模糊匹配 例如设置key 为order.key,那么投递或消费时必须使用 order.key |
topic | 匹配交换机 | 需要绑定routingKey,支持模糊匹配 *为模糊匹配,只匹配一个单词(order.test),#可以匹配多个单词 例如:order.test.test |
概念说明
Broker:消息队列服务器实体
Exchange:消息交换机,他指定消息按什么规则,路由到哪个队列
Queue:消息队列载体,每个消息都会被投入到一个或多个队列
Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来
Routing Key:路由关键字,Exchange根据这个关键字进行消息投递
Vhost:虚拟主机,服务器中多个项目可使用不能虚拟主机,用于区分不同项目
producer:消息生产者
consumer:消息消费者
channel:消息信道,在客户端的每个链接里,可建立多个channel,每个channel代表一个会话任务
消息队列使用过程
客户端连接到消息队列服务器,打开一个channel
客户端声明一个Exchange,并设置相关属性
客户端声明一个Queue,并设置相关属性
客户端使用 Routing Key,在Exchange和Queue之间建立好绑定关系
客户端投递消息到 Exchange
exchange接收到消息后,就根据消息的Routing Key和已经设置的binding,进行消息路由,将消息投递到一个或多个队列里
死信队列
“死信”消息会被RabbitMQ进行特殊处理,如果配置了死信队列信息,那么该消息将会被丢进死信队列中,如果没有配置,则该消息将会被丢弃
以下操作会使消息进入死信队列
1.消息被拒绝,并且requeue被设置为false
2.消息过期
3.队列到达最大长度
用户普通下单逻辑
消费端消费消息时:慎用消息重新投递
当消息重新投递到消息队列时,这条消息不会回到队列尾部,仍是在队列头部
消费者会立刻消费这条消息,业务处理再抛出异常,消息再重新入队,如此反复进行
最好解决方法直接丢弃该消息,未处理的业务在定时任务中增加补偿机制。或者将消息直接确认,然后重新插入到队列,这样消息会自动插入到尾部,不会影响到其他消息
- 用户下单,数据库存储订单信息,数据库记录消息数据(消息状态为待确认模式)
- 生产者发送消息
- 建立发送消息确认机制 confirmCallback,消息从生产者(producer)发送消息到交换机(exchange),不论是否成功,都会执行确认回调方法
(建议增加定时任务,处理db中状态为未处理的消息。此处ack为true时更新db中消息状态为成功。反之为false时,执行补偿机制或者不做任何处理,等待定时任务处理)
4.建立投递队列失败回调机制 ReturnsCallback,exchange投递到队列失败 (退回模式),只有当入队列失败时才会执行
(建议增加定时任务,处理db中状态为未处理的消息。执行补偿机制或者不做任何处理,等待定时任务处理)- 消费者处理消息,查询数据库订单是否有误异常,如有异常丢弃该消息 (增加定时任务,对订单执行补偿机制)。之后处理订单状态或者其他操作逻辑… 处理完毕使用ack确认该消息。
(处理业务逻辑时加入try catch,若出现catch直接丢弃该消息,等待定时任务处理)- 定时任务,增加幂等性。每隔一段时间扫描消息表,处理待确认状态的消息。当定时任务尽最大努力尝试3次后依旧失败,将消息状态更改为处理失败。之后处理订单失败逻辑… 。或者增加定时任务每隔一段时间,处理失败的订单