前边一篇《基于状态变更的分页查询另辟幽径》讲述了基于状态变更的分页查询存在的问题以及解决方案,但是细扣一些还是存在一些问题的,接下来继续分析并给出解决方案。
问题分析
上一篇《基于状态变更的分页查询另辟幽径》中出问题的代码是:
改造后的代码变成了:
改造后的代码能够解决绝大部分场景的状态变更的分页查询问题,但是我们基于当前任务分配场景考虑一下,任务创建和分配是典型的 生产--消费 模式:
问题一
那如果任务生成的入口比较多且生成的速度比较快(大促),这时候如果任务分配调度消费的速度比较慢(外部查询接口慢等),那么改造后的代码:
while(true) {
pageResult = this.taskService.page(pageQuery,params);
List<Task> list = pageResult.getList();
if(CollectionUtils.isEmpty(list)) {
System.out.println("分配完成");
break;
}
for(Task task : list) {
this.taskService.doAssign(task);
total ;
}
}
可能无限循环下去,从而影响下一次调度,导致后续所有调度整体顺延,或者出现并发问题。
问题二
如果由于依赖服务的问题,导致图中标注代码执行任务分配失败,也就是大批量的任务没有分配下去,那么也会产生任务分配卡在这里,因为每一次分页查询到数据但是分配失败,状态没有变更,从而每一次都是捞取同样的数据,从而导致分配阻塞,程序出现死循环,占用应用资源不释放。
解决方案
对于上述的问题一,理论上,每一次调度分配的任务有个区间,开始节点可以自定义,但是结束节点就是调度开始执行的时间,而不是每次都拉取新产生的数据,在开始时候指定createTime<=new Date(),改造后如下:
对于问题二,使用上一篇中的第一种解决方案已经行不通,因为始终差第一页的话,如果第一页始终分配失败,就会出现死循环。那么我们可以换一种思路,既然是调度来执行任务分配,那么如果某个任务在第一次调度中分配失败,那么第二次调度中分配掉对于这种纯离线的业务模型,处理人是基本无感知可以接受的,那么上一篇中的第二种解决方案的优势就出来了,我们第一次查询把id>0当作查询条件,不使用传统的分页查询方式,使用偏移量来替代分页查询:
改造后的代码如下:
这样我们就彻底解决了基于状态变更的分页查询中遇到的各种奇葩的场景和问题,希望给大家带来帮助,如有不当请海涵和指出。