队列同步 - 跨平台接口调用及值同步处理

   年前遇到这样一种情况,记录一下,以防后续出现同样的错误,影响后续的处理及兼容。

   场景如下:

     

   有几个限制条件:

      1. 需求指明不可以对店铺分库存,因为无法确认店铺售卖哪个卖的多,哪个少;

      2. 调用三方平台API新增商品时,SKU量、库存量、运费模板都有限制。库存量不可超过1W;

      3. 三方平台商品存在几种状态:待审核、审核待修改、审核通过(自动为上架)、审核拒绝、下架、上架、删除等;

      4. 三方平台API操作商品库存信息,仅且上架才可修改库存,其余状态均异常【重点考虑】

      5. 因自身平台商品,存在对应多平台情况,需要考虑不同平台商品的库存一致性处理,避免超卖【重点考虑:试想一下能否绝对避免】;

 

方案

   前因:场景梳理

      1.  由限制条件2得出,库存超过需要额外同步;

      2. 下单库存变更,存在下单扣减、取消支付(下单)回增;

      3. 运营后台增减库存;

      4. 运营手动更改三方店铺库存;

  

   大略:

      1. 实时同步

          a. 以三方最高库存值进行同步,多余部分队列补偿;
          b. 商品同步时,库存全部为零,待三方店铺商品上架后,在同步库存;

      2. 定时补偿

          a. 定时任务补偿所有商品的店铺库存值,进行全量覆盖的操作,避免直接操作三方库存的情况发生;

   

   详细:

      每次库存变更,使用队列(Queue、redis ...)等方式,将变更增量加入队列,增量值采用加锁方式,保证库存变更最终值的准确性;

         如:可以使用redis中的set结构,对 [ 三方店铺-商品ID-SKUID ] 进行缓存变更信息,再存储对应的sku增量值

       ① 第一种方式::

          

          如图,使用单线程从队列中 pop 除元素,进行执行处理,失败则再次添加在队中。

 

         思考若①方案中,某个数据执行异常,则一直在存取之间处理,那么这种方式就会一直耗费CPU资源,如何解决这种处理呢?

 

      ② 第二种方式:

         分两类型线程执行:

             i:分发任务,将set中的member进行分发至队列 ( Queue ),判断当前被分发的队列是否为空且当前元素可以被操作,否需要睡眠N秒,是则继续分发,注意线程执行幂等;

            ii:执行任务,从队列中poll / pop出元素,获取并移除 [ 原子操作、加锁保证或redis-lua脚本 ] 增量值,调用三方库存变更接口,失败了,判断原因,若为三方库存少于扣减值,则清零,否则缓存1H ( 不可操作标识 ),并再次添加队列中,等待1H后的再次触发;

         

 

      ③ 第三种方式:

          思考 !!!

 

后续调整

   队列调整 ?    方案调优 ?     超卖绝对避免  ?

      1.  库存分发时,注意幂等性; 

      2. 分发执行时,避免死循环造成的CPU,需测试该操作对性能的影响,避免线上CPU爆满的情况发生;

      3. 可以合理利用Thread.sleep(0L);  或者线程礼让,避免CPU一直被占用的情况发生,从而导致CPU爆满;

      4. 可以使用redis - hash结构替换,hash中也存在increment的操作,也可以保障库存增量变更时的值的最终一致;

         使用 redis-set + string 可以避免对同一SKU多次变更操作; hash结构同理

      5. 活动前能否将涉及所有商品统一处理,减少异常情况发生;

      6. 记录库存的变更流水,便于后续核对;

      7. 除上述方式外,是否还有其他实现的可能,并且能够避免超卖的风险?

      

 

     whatever is worth doing is worth doing well   --- fn  

 

posted @ 2021-02-28 16:29  fn-f  阅读(228)  评论(0编辑  收藏  举报