项目中MQ实现了流量削峰、延迟队列(过时取消)、业务解耦(异步发短信、发消息)
异步进行业务处理,仅发送消息到消息队列减少响应时间
限流
因为需求中需要延迟执行某个功能
比如下订单后如果用户未付款需要延迟对订单进行取消,这时候可以下订单后将订单信息放入延迟队列中,延迟队列的消息过期后加入取消队列(死信队列),消费取消队列中的消息并判断订单是否已付款,如果未付款的话进行取消订单。
因为需要将时间耗费将长、对核心业务不造成影响的非核心业务和核心业务进行解耦
比如下订单业务和下订单后发短信发优惠券等业务应该进行解耦,非核心业务不应该影响核心业务的结果,而且核心业务执行完后可以发送消息到MQ并立即响应结果,不需要非核心业务的响应
多线程实现异步会消耗cpu资源,当单机配置达到瓶颈时就会影响到核心业务线程,发生cpu竞争问题
而MQ实现异步是完全解耦的,可以在多个服务器上异步处理不同的业务,因此在分布式开发中经常使用
产生背景:
生产者投递消息的速率大于消费者消费的速率,消息堆积在MQ服务器中,没有及时被消费,产生消息堆积问题
解决办法:
1、提高消费者消费速率(对消费者实现集群)
2、消费者批量获取消息,减少网络传输次数
幂等性: 就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。
深入理解幂等性
比如订单付款业务,保证客户端与服务端的交易一致性,避免多次扣款。通过业务逻辑进行处理:
1、查询订单支付状态
2、如果判断状态已经支付,直接返回结果
3、如果判断状态未支付,支付扣款并保存流水并返回支付结果
问题:保证幂等的方案是分成两步的,第2步依赖第1步的结果,无法保证原子性
因此高并发情况下可能出现的情况:在第一次请求第2步订单状态还没有修改为已支付状态第二次请求就已经到来查询,就会出现第二次请求查询时状态还是未支付状态,进行了重复支付,破坏了幂等性
可以通过分布式锁来解决,实现支付时先查询redis中是否存在订单id的key,如果不存在则可以添加订单id的key获取锁并进行查询订单支付状态,进行支付等操作,支付完成后删除redis中订单id的key进行释放锁。
这样使用redis实现分布式锁,这次订单请求完成前,下次订单请求只能等待获取锁。
解决RabbitMQ消息丢失问题和保证消息可靠性
1、生产者
开启消息确认机制,并在发送前对消息进行持久化(redis)
2、MQ
开启MQ的持久化
3、消费者
关闭消息自动确认机制,进行手动确认
如果三个任务t1,t2,t3要按顺序消费,那就把他们放在一个队列中,然后只用一个消费者单线程的来处理消息。关键点就是一个队列、一个消费者、单线程,确保消息投递到同一个队列,同一个消费者消费,单线程
方案1:直接删除redis缓存,让下次查询时直接查询数据库–延迟双删策略
延迟双删图解
两次删除缓存都是为了清空缓存防止更新数据库期间出现查询再次进行缓存
延迟是为了让更新数据库期间的查询完全执行完再进行删除,保证下一次进行查询时是新数据
缺点:吞吐量低,如果大量进行数据库操作需要重复删除缓存
方案2:基于canal框架订阅binlog进行同步
canal的工作原理就是把自己伪装成MySQL slave,模拟MySQL slave的交互协议向MySQL Mater发送 dump协议,MySQL mater收到canal发送过来的dump请求,开始推送binary log给canal,然后canal解析binary log,再发送到存储目的地
canal的好处在于对业务代码没有侵入,因为是基于监听binlog日志去进行同步数据的
1、canal伪装成mysql从节点订阅mysql主节点的binlog文件
2、mysql主节点发生变化,推送binlog给canal
3、canal服务器将binlog文件转化成json对象给mq
4、消费者消费mq中的json实现异步更新缓存