多消费者并发竞争确保顺序消费的方案
1,模拟场景:(saveOrder和payOrder)
业务下:下单之后需要生成订单,再减库存。如果乱序先产成订单,会导致库存数据不一致的情况。
中间件下:1个生产者A,3个消费者B1,B2,B3。(消费者会并发竞争)。订单1,两个请求11(生成订单),12(减库存)。订单2和订单3同订单1。一共会发送6个消息到B1,B2,B3。此时如果11在B1,12在B2,B1队列发生堵塞,那么B2会先消费12。此时库存还没减少就产生了订单,产生了乱序消费的问题。
2,A方案 (算法层面):可扩展性高
原则:将6个消息请求11,12,21,22,31,32。利用先排序分组后分流的方式,依次将请求分发,11,12进入消费B1;21,22进入消费B2;31,32进入消费B3。
排序:对push进生产者队列的消息先利用数组或者list等数据结构进行排序(比如根据时间),对进行排序的数据结构根据空间或者时间来限定每次排序的数量。
分组:排序的过程中用单号和业务(减库存或者生成订单)当作标识如下数据结构{{11,12},{21,22},{31,32}}。如果其中存在{{11,12},{21},{31}}这中情况,将单数的21和31的消息存入另一个等待队列。定时对等待队列里面的数据进行双数的提取,将双数请求业务提取到排序的数据结构中。
分流:11,12进入消费B1;21,22进入消费B2;31,32进入消费B3。
![](https://i-blog.csdnimg.cn/blog_migrate/db0cfc328992dbfada93b1ec5ff3cdc3.png)
B方案(业务层面):实现简单
原则:发送消息时将11,12两个请求合并成一个请求1X,在消费者实际处理的业务中按照顺序再将1X进行分解成11和12,先执行11再执行12。
解决重复消费的方案
1, 模拟场景:
2, 方案:幂等校验,去redis校验幂等。(将处理过的请求存一个标识在redis中,每次消费请求的时候去redis进行校验)
幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。
![](https://i-blog.csdnimg.cn/blog_migrate/007d30bc1753a29e22afd32f5200f251.png)
解决消费队列堵塞的方案
1,模拟场景
2, 方案:设置消息过期时间,将过期的消息存入延时队列,再重试延时队列的消费。如果还是消费不了,写入日志触发邮件或者报警
实际业务场景解决方案:
业务下:下单操作流程有新单,改单1,改单2,结账1。
中间件下:11,12,13,14
备注:用锁解决乱序消费——分布式锁(互斥锁)+订单级别的版本号来判断(基于CAS算法的数据库锁)
![](https://i-blog.csdnimg.cn/blog_migrate/fe90db6fac48e6d3ef19ff4063268c8e.png)
ssp实际业务场景:下单操作流程有新单,改单1,改单2,结账。四个操作流程,其余场景和上面模拟尝尽类似。
最终方案:用基于CAS算法的数据库乐观锁+分布式写锁(排他锁)悲观锁;分别解决消费乱序问题和队列拥堵问题。
方案的流程图:
![](https://i-blog.csdnimg.cn/blog_migrate/f677db2cdbd0664579b2fce346de16fa.png)