📚目录
- 📚简介
- 💨优化前原代码:
- ⚙️ 函数编程简化
- 🎄 JDK自带的函数式接口
- ✨ 改造调用方式
- 🎊 时间范围执行
- 🎉时间范围每天执行
📚简介
因为公司的使用Xxl-Job作为任务调度平台,其中我们大部分的报表查询数据量太大,字段又多,实时查询效率太低,等待时长也久,我们只能把常用的数据进行定时任务处理,每几分钟自动运行程序跑对应报表的Sql插入缓存表中,页面端在通过缓存表进行渲染,使用这点有个不好之处就是有个定时任务的执行时间差,关于订单业绩相关的我们任务设置的时间就短,尽量不影响使用者.
所以这个时间我们需要传递查询指定时间范围内,或者是不指定时间而是指定运行前多少天的数据进行重跑处理.最终参数定义了常用的三个参数
{"beginTime":"","endTime":"","beforeDays":"10"
}
xxl-job 的参数接收类
💨优化前原代码:
伪代码:缓存定时任务
/*** @Author itmei* @Date 2023/12/22 20:48* @description: 任务处理器* @Title: CacheTaskHandle* @Package com.itmei.xxl.task*/
@Component
public class CacheTaskHandle {@XxlJob("bxCacheTableTask")public ReturnT<String> bxCacheTableTask() throws Exception {long start = System.currentTimeMillis();//获取xxl-job调度传递过来的数据String param = XxlJobHelper.getJobParam();XxlJobHelper.log("###### Job参数 ######\n{}",param);XxlJobParam jobParam;if (ObjectUtil.isNotEmpty(param)) {jobParam = JSONUtil.toBean(param, XxlJobParam.class);} else {jobParam = new XxlJobParam();}DateTime beginTime;DateTime endTime;if (ObjectUtil.isEmpty(jobParam.getBeginTime()) || ObjectUtil.isEmpty(jobParam.getEndTime())) {DateTime startDateTime;DateTime endDateTime = DateUtil.date();Integer beforeDays = jobParam.getBeforeDays();if (ObjectUtil.isEmpty(beforeDays)) {//默认开始时间是当前时间向之前偏移46天startDateTime = DateUtil.offsetDay(new Date(), -46);} else {startDateTime = DateUtil.offsetDay(new Date(), -Integer.valueOf(beforeDays));}beginTime = DateUtil.beginOfDay(startDateTime);endTime = DateUtil.endOfDay(endDateTime);} else {//处理时间范围beginTime = DateUtil.beginOfDay(jobParam.getBeginTime());endTime = DateUtil.endOfDay(jobParam.getEndTime());}bxCacheHandle(beginTime,endTime);long end = System.currentTimeMillis();XxlJobHelper.log("###### Job执行完成,耗时: {} 秒 ######",(end-start)/1000);return ReturnT.SUCCESS;}@XxlJob("bwmCacheTableTask")public ReturnT<String> bwmCacheTableTask() throws Exception {long start = System.currentTimeMillis();//获取xxl-job调度传递过来的数据String param = XxlJobHelper.getJobParam();XxlJobHelper.log("###### Job参数 ######\n{}",param);XxlJobParam jobParam;if (ObjectUtil.isNotEmpty(param)) {jobParam = JSONUtil.toBean(param, XxlJobParam.class);} else {jobParam = new XxlJobParam();}DateTime beginTime;DateTime endTime;if (ObjectUtil.isEmpty(jobParam.getBeginTime()) || ObjectUtil.isEmpty(jobParam.getEndTime())) {DateTime startDateTime;DateTime endDateTime = DateUtil.date();Integer beforeDays = jobParam.getBeforeDays();if (ObjectUtil.isEmpty(beforeDays)) {//默认开始时间是当前时间向之前偏移46天startDateTime = DateUtil.offsetDay(new Date(), -46);} else {startDateTime = DateUtil.offsetDay(new Date(), -Integer.valueOf(beforeDays));}beginTime = DateUtil.beginOfDay(startDateTime);endTime = DateUtil.endOfDay(endDateTime);} else {//处理时间范围beginTime = DateUtil.beginOfDay(jobParam.getBeginTime());endTime = DateUtil.endOfDay(jobParam.getEndTime());}bwmCacheHandle(beginTime,endTime);long end = System.currentTimeMillis();XxlJobHelper.log("###### Job执行完成,耗时: {} 秒 ######",(end-start)/1000);return ReturnT.SUCCESS;}public void bxCacheHandle(Date startTime, Date endTime) {/*** 伪代码,不能泄密,单纯打印看效果*/XxlJobHelper.log("{} {}-{} {} ", "bxCacheTableTask",startTime.toString(),endTime.toString(),"查询缓存数据...");XxlJobHelper.log("{} 插入数据 ", "bxCacheTableTask");}public void bwmCacheHandle(Date startTime, Date endTime) {/*** 伪代码,不能泄密,单纯打印看效果*/XxlJobHelper.log("{} {}-{} {} ", "bwmCacheTableTask",startTime.toString(),endTime.toString(),"查询缓存数据...");XxlJobHelper.log("{} 插入数据 ", "bwmCacheTableTask");}
}
这些代码都是复用的,除了bxCacheHandle(beginTime,endTime);
是处理对应缓存表的逻辑代码,当我需要创建新的缓存表时,我都会把上面的代码复制下来修改@XxlJob("执行名称")
,在把bxCacheHandle(beginTime,endTime);
修改成对应缓存表查询处理的逻辑,而且他们共同点就是传入的参数都是开始时间和结束时间
,并且没有返回值,那么我们就可以使用BiConsumer<T, U>函数式接口来解决
.
其实除了时间范围内进行查询,我们还有用到按照传入的时间范围,在按照一天一天维度进行查询,这样的效果就是有些报表一次性查询范围30天会比较久,而且数据库的压力也会提升,我们就需要按照模式去选择,一次性查询一个月还是一个月分30次一天一天的去查询数据.
⚙️ 函数编程简化
我们创建一个专门处理时间查询的类
/*** @Author itmei* @Date 2023/12/23 14:27* @description: 时间范围处理* @Title: DateQueryHandle* @Package com.itmei.xxl.task*/
public class DateQueryHandle {/*** 执行* @param syncLogic 是一个函数式接口 BiConsumer<Date, Date> 的实例,用于执行具体的逻辑。该函数式接口接受两个 Date 类型的参数,分别表示开始时间和结束时间* @param queryType true 一天一天查询 , false 日期区间查询*/public static void execute(BiConsumer<Date,Date> syncLogic,Boolean queryType){long start = System.currentTimeMillis();//获取xxl-job调度传递过来的数据String param = XxlJobHelper.getJobParam();XxlJobHelper.log("###### Job参数 ######\n{}",param);XxlJobParam jobParam;if (ObjectUtil.isNotEmpty(param)) {jobParam = JSONUtil.toBean(param, XxlJobParam.class);} else {jobParam = new XxlJobParam();}DateTime beginTime;DateTime endTime;if (ObjectUtil.isEmpty(jobParam.getBeginTime()) || ObjectUtil.isEmpty(jobParam.getEndTime())) {DateTime startDateTime;DateTime endDateTime = DateUtil.date();Integer beforeDays = jobParam.getBeforeDays();if (ObjectUtil.isEmpty(beforeDays)) {//默认开始时间是当前时间向之前偏移46天startDateTime = DateUtil.offsetDay(new Date(), -46);} else {startDateTime = DateUtil.offsetDay(new Date(), -Integer.valueOf(beforeDays));}beginTime = DateUtil.beginOfDay(startDateTime);endTime = DateUtil.endOfDay(endDateTime);} else {//处理时间范围beginTime = DateUtil.beginOfDay(jobParam.getBeginTime());endTime = DateUtil.endOfDay(jobParam.getEndTime());}if (queryType) {// 按天查询long days = DateUtil.between(beginTime, endTime, DateUnit.DAY);for (int i = 0; i <= days; i++) {DateTime dateTime = DateUtil.offsetDay(beginTime, i);DateTime begin = DateUtil.beginOfDay(dateTime);DateTime end = DateUtil.endOfDay(dateTime);//调用传入的函数式接口实例的方法来执行具体的逻辑syncLogic.accept(begin, end);}} else {//调用传入的函数式接口实例的方法来执行具体的逻辑syncLogic.accept(beginTime, endTime);}long end = System.currentTimeMillis();XxlJobHelper.log("###### Job执行完成,耗时: {} 秒 ######",(end-start)/1000);}/*** 执行* @param syncLogic*/public static void execute(BiConsumer<Date,Date> syncLogic){//重载方法 使用时间范围内的查询execute(syncLogic,false);}
}
🎄 JDK自带的函数式接口
唯一不一样的是使用了 JDK 1.8 中函数式接口BiConsumer
,这个接口默认可以传递2个参数.除了这个JDK 1.8 中还提供了这些常用的函数式接口:
Consumer<T>:接受一个输入参数 T,没有返回值。
Supplier<T>:不接受任何输入参数,返回一个结果 T。
Function<T, R>:接受一个输入参数 T,返回一个结果 R。
Predicate<T>:接受一个输入参数 T,返回一个布尔值。
UnaryOperator<T>:接受一个输入参数 T,返回一个结果 T,输入类型和返回类型相同。
BinaryOperator<T>:接受两个输入参数 T,返回一个结果 T,输入类型和返回类型相同。
BiConsumer<T, U>:接受两个输入参数 T 和 U,没有返回值。
BiFunction<T, U, R>:接受两个输入参数 T 和 U,返回一个结果 R。
BiPredicate<T, U>:接受两个输入参数 T 和 U,返回一个布尔值。
由于我们简化的代码中并没有返回值,而且需要传入2个参数,这样我们就选择了BiConsumer<T, U>
做为本次使用到的函数式接口.
✨ 改造调用方式
🎊 时间范围执行
我们改造
bwmCacheTableTask
代码,改成函数式接口方式调用
运行结果
改造后,我们只需要一行代码就可以,完成了代码的复用性
改造后的运行结果和改造前运行结果是一样的,这样是不是代码就更简化
🎉时间范围每天执行
改造
bxCacheTableTask
代码为,时间范围内,一天一天的执行效果
执行效果
按照时间范围内,进行一天一天查询