生产者消费者异步调用

场景:
全量同步上游酒店数据,需要同步酒店数据量近百万,
上游接口一家酒店需要调用2次接口才能获取完整的酒店数据,并且一次只能查询一家酒店

本地需要更新的表有9张表,7张百万级别,2张千万级别
如果一个一个酒店查询,然后更新就需要更新 近千万次(100w*9),如果一次更新耗时100ms 那么一千万次耗时:270多个小时,那么开了10个线程 也要27个小时
如何加快查询处理速度?

比如有a,b,c,d,e,f,g,h,i这九张表
处理方案:
1.如果存储空间足够可以考虑采取读写分离(每一个表都有备份表, 一个是当前查询使用,一个是当前全量更新使用,全量更新前truncate掉,然后批量插入,全量处理完把 当前全量更新的表切换为 读表) 因为我们这边运维出于成本考虑,不同意数据库扩容
故此方案不可行
2.
业务特点:只有全量更新时候才会更新这些记录
(暂不考虑数据的强一致性,我们可以采取表的维度去处理,失败重试3次,
重试工具类
仍处理失败的插入到补偿处理表,后续补偿处理保证数据最终一致性)
批量处理+多线程
把调用上游的结果存储起来,然后积攒到一定数量,批量更新数据库,按照表维度更新
,以前是数据强一致性,其中 h,i 是我们聚合上游后
的表 只能更新不能删除

// 伪代码
@Transation
public void update(WjsHotelInfo hotelInfo){
  // 插入前批量删除 a,b,c,d,e,f,g 
  delateBeforeInsert(hotelInfo);
  // 插入a,b,c,d,e,f,g 表
  insert(hotel);
  // 处理 h,i 
  dealAfterUpdate(hotel);
}

批量处理采取方案:
伪代码

public void update(List<WjsHotelInfo> list){
  // 插入前批量删除 a,b,c,d,e,f,g 
  delateBeforeInsert(list);
  // 插入a,b,c,d,e,f,g 表
  insert(list);
  // 处理 h,i 
  dealAfterUpdate(list);
}

为了解耦查询上游和把查询上游结果存储起来,参考生产者和消费者写了个
工具类

@Slf4j
public abstract class BatchDealUtil<T,R> {

    protected ThreadPoolExecutor threadPoolExecutor = null;
    protected  int corePoolSize;
    //批量处理大小
    protected  int batchSize = 200;

    protected volatile ArrayBlockingQueue<R> resultQueue = null;
    //生产者队列容量
    protected int capacity = 2000;
    //消费者最后一次判断
    protected volatile boolean lastFlag = false;


    protected Consumer<List<R>> consumer = null;
    protected Function<T,R> function = null;
    Thread thread = null;



    protected abstract Consumer<List<R>> initConsumer();

    protected abstract Function<T,R>  initFunction();
    public BatchDealUtil(){
       this(4);
    }

    public BatchDealUtil(int corePoolSize){
       this(corePoolSize,100);
    }

    public BatchDealUtil(int corePoolSize,int batchSize){
        this(corePoolSize,batchSize,2000);
    }

    public BatchDealUtil(int corePoolSize,int batchSize,int capacity){
        if(batchSize > capacity){
            throw new BaseException("批次处理的条数不能大于队列的大小");
        }

        this.corePoolSize = corePoolSize;
        this.batchSize = batchSize;
        this.capacity = capacity;
        resultQueue = new ArrayBlockingQueue<>(capacity);
        threadPoolExecutor =  new ThreadPoolExecutor(corePoolSize,corePoolSize,0, TimeUnit.SECONDS,new ArrayBlockingQueue<>(corePoolSize+1),
                new ThreadPoolExecutor.CallerRunsPolicy());

        consumer = initConsumer();
        function = initFunction();

        thread = new Thread(() -> dealValue());
        thread.start();

    }



    public void producer(List<T> list){
        if(CollectionUtils.isEmpty(list)){
            return;
        }
        for (T t : list) {
            threadPoolExecutor.submit(() -> {
                R result = function.apply(t);
                try {
                    resultQueue.put(result);
                    if(resultQueue.size()>=batchSize){
                        LockSupport.unpark(thread);
                    }
                }catch (Exception err){
                    log.info("发生异常",err);
                    err.printStackTrace();
                }
            });
        }

    }

    /**
     * 消费者
     */
    private  void dealValue(){
        try {
           while (true){
               int size = 0;
               if(lastFlag && resultQueue.size() == 0){
                   break;
               }
               while (!lastFlag && resultQueue.size() < batchSize){
                   LockSupport.park();
               }

               List<R> list = new ArrayList<>(batchSize);
               int total=0;
               size = Math.min(resultQueue.size(),batchSize);
               while(!resultQueue.isEmpty() && total++ < size){
                   list.add(resultQueue.poll());
               }
               consumer.accept(list);
           }
        }catch (Exception e){
            log.info("消费者处理出现异常",e);
            e.printStackTrace();

        }
    }

    /**
     * 必须调用shutDown方法
     */
    public void shutDown(){
        threadPoolExecutor.shutdown();
        while (threadPoolExecutor.getActiveCount()>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        LockSupport.unpark(thread);
        lastFlag = true;
    }
    
}

使用例子:

static class  MyBatchDealUtil extends BatchDealUtil<Integer,Integer>{

        public MyBatchDealUtil(int corePoolSize, int batchSize, int capacity) {
            super(corePoolSize, batchSize, capacity);
        }
        //消费者,模拟批量插入数据库
        @Override
        protected Consumer<List<Integer>> initConsumer() {
            return (t)->{
                for (Integer integer : t) {
                    System.out.print(integer+"\t");
                }
                System.out.println("\t-------");
            };
        }
        // 生产者,模拟调用上游接口
        @Override
        protected Function<Integer, Integer> initFunction() {
            return e->{
                sleep(100);
                return  e;
            };
        }
    }

    public static void main(String[] args) {
        MyBatchDealUtil dealUtil = new MyBatchDealUtil(4,10,100);

        for (int i=0;i<10;i++){
            List<Integer> list = getList(5);
//            System.out.println(list);

            dealUtil.producer(list);
        }
        dealUtil.shutDown();
    }

    private static List<Integer> getList(int i) {
        Random random = new Random();
        return random.ints(0,100).limit(i).boxed().collect(Collectors.toList());
    }


    public static void sleep(long time){
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值