微服务后端查询超时立即返回的处理方法

10 篇文章 0 订阅
10 篇文章 0 订阅

背景描述

    功能:向用户提供区间可查询,用户输入时间区间,或者其他特定参数,后端调用底层平台获取数据后再进行业务处理、分页等封装以便展示。
    问题:1. 当用户输入时间区间过大可能会导致跨平台查询底层数据量过大,查询速度慢、超时、撑爆内存,进而造成服务卡死或宕机。2. 由于数据量的多少和时间区间的长短并无之间联系(数据量多少与业务有关,有时候一秒内几千笔,而有时候数小时内都可能无记录),因此输入区间限制不宜在前端控制,而后端接收参数后也需跨平台调用接口,后端业务层入口也无法做范围限制。
    愿景:我们需要对查询接口做超时处理,即查询有结果立即返回,若查询耗时超过30秒之后无论是否有结果也立即返回。

解决方案

    该功能实现的业务层中开启线程池,启动两个线程来分别执行查询任务和监控超时打断任务。

技术思路
  • 设定一个查询任务,将有可能查询超时及复杂业务处理的逻辑写进任务中,该查询任务丢给子线程执行;
  • 再设定上述查询任务的守护任务,该任务先sleep 30秒,然后询问查询任务是否已完成,若未完成或未取消,则将查询任务终止。毫无疑问,这里的守护任务需持有查询任务的future对象;
  • 业务层中将这两个任务丢进线程池,若查询结果很快有结果则立即返回并关闭守护线程;若查询超过30秒,则查询线程被守护线程打断,主线程继续处理超时异常并关闭守护任务及线程池;
代码实现

    查询任务Task类

public class RangeQueryTask implements Callable<List> {

    //复杂耗时查询的句柄
    private ComplexRangeQuery complexRangeQuery;


    public RangeQueryTask(ComplexRangeQuery complexRangeQuery){
        this.complexRangeQuery = complexRangeQuery;
    }
    
    @Override
    public List call() throws Exception {
        try{
            //执行查询逻辑  略

            //返回数据处理及封装  略

        }catch (Exception e){
            //不同的异常的对应处理  略

        }
        return new ArrayList();
    }
}

    守护任务Task类

public class RangeQueryDemonTask  implements Callable<Integer> {

    private Future<List> complexRangeQryTaskFuture;

    public RangeQueryDemonTask(Future<List> complexRangeQryTaskFuture){
        this.complexRangeQryTaskFuture = complexRangeQryTaskFuture;
    }

    @Override
    public Integer call() throws Exception {
        try{
            sleep(30000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            if (!complexRangeQryTaskFuture.isDone()||!complexRangeQryTaskFuture.isCancelled()){
                complexRangeQryTaskFuture.cancel(true);
            }
            //此处将超时异常抛给前端处理
            throw new BussinessException(EnumErrorInfo.QRY_TIMEOUT.getCode(),EnumErrorInfo.QRY_TIMEOUT.getMsg());
        }
        return null;
    }
}

    业务层处理逻辑

ThreadPoolExecutor threadPoolExecutor = null;
List resultList;
try{
    //拟用ComplexRangeQuery指代耗时查询,输入参数为起始时间及终止时间
    ComplexRangeQuery complexRangeQuery = new ComplexRangeQuery("startTime","endTime");
    threadPoolExecutor = new ThreadPoolExecutor(2,2,10, TimeUnit.MILLISECONDS,new LinkedBlockingDeque<>());
    // 定义查询任务
    RangeQueryTask rangeQueryTask = new RangeQueryTask(complexRangeQuery);
    Future<List> qryTaskFuture = threadPoolExecutor.submit(rangeQueryTask);
    // 定义守护任务  并将查询任务结果句柄传入
    RangeQueryDemonTask rangeQueryDemonTask = new RangeQueryDemonTask(qryTaskFuture);
    Future<Integer> demonTaskFuture = threadPoolExecutor.submit(rangeQueryDemonTask);
    // 业务主线程一直阻塞于此,若30秒之后查询任务未结束,则查询任务线程被打断后向下继续执行
    resultList = qryTaskFuture.get();
    if (null != resultList){
        //对于查询之后立即返回结果的  也需将守护线程终止
        if (!demonTaskFuture.isDone()||!demonTaskFuture.isCancelled()){
            demonTaskFuture.cancel(true);
        }
    }

}catch (Exception e){
    // 各类异常的相应处理 略
}finally {
    //关闭线程池
    if (null!=threadPoolExecutor&&!threadPoolExecutor.isShutdown()){
        threadPoolExecutor.shutdownNow();
    }
}
return resultList;

本代码亲测有效,现已上线,运行稳定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值