xxl-job 定时任务(多线程执行)获取任务执行进度

xxl-job 定时任务(多线程执行)获取任务执行进度

具体思路

  • 前提:需要将任务按照一定条件进行分组,并处理成列表的形式(任务与任务之间不存在交叉关系,保持列表中每一项的唯一性)
  • 在使用xxl-job进行任务调度多线程执行时,获取到任务具体执行进度;首先将需要进行多线程处理的数据,取出分组条件信息组成一个list对象,然后根据分组条件进行分页,进而划分为多批次(多页),从而实现任务的批次处理。

完整工具代码

  • JobExecutor执行具体任务逻辑的方法
import java.util.Map;
import java.util.concurrent.Future;
@FunctionalInterface
public interface JobExecutor<T> {
    /**
    * 使用 Future<T> 进行多线程的执行 参数 curr当前页、limit 当前批次大小, args当前批次中可能用到的具体额外参数信息
    * */
    public Future<T> execute(int curr, int limit, Job args) throws Exception;
}
  • JobExecutorEnd任务执行完成后,自定义的回调方法,定义任务结束后,要进行的下一步操作、打印日志之类的
@FunctionalInterface
public interface JobExecutorEnd<T> {
    /**
    * 使用 Future<T> 进行多线程的执行 参数 curr当前页、limit 当前批次大小, args当前批次中可能用到的具体额外参数信息
    * */
    public void executorEnd();
}
  • 用于传递执行任务过程中需要常用的参数,可以自定义添加参数,需要在JobHelp类的startJob、startMapJob 方法中进行自定义参数的处理逻辑,也可以在具体的任务处理中进行修改
@Setter
@Getter
public class Job {
    private String mapKey;
    private String time;
}
  • 实现具体获取任务执行进度的帮助工具类
/**
 * 定时任务执行辅助工具,按照分组条件进行数据分割,进行多线程同步执行job任务,预防因一次性执行数据太多导致内存溢出
* */
@Setter
@Getter
@Slf4j
public class JobHelp<T> {
    /**
    * 每一批次的数据大小
    * */
    private int limit = 500;
    /**
     * 分组的数据长度
     * */
    private long total;
    /**
     * 要执行多次批,才能完成任务
     * */
    private long pages;
    /**
     * 每一批次所占的总数据的百分比
     * */
    private double pro;
    /**
     * 当前任务完成到第几批次
     * */
    private int progress = 0;
    /**
     * 同时执行几个批次的任务,默认同时执行5个批次的任务,执行完5个批次后然后才开始执行下一个批次的任务
     * */
    private Integer step = 5;

    private List<String> mapIndex;

    private JobExecutorEnd<T> jobExecutorEnd;

    /**
     * redis存储当前任务进度的键值对信息
     * 命名规范为:任务名称:执行时间
     * */
    private String progressRedisKey;
    /**
     * redis操作的Repository对象,进行任务执行进度显示
     * redis操作的Repository对象,进行任务执行进度显示
     * */
    private static RedisRepository redisRepository;
    /**
     * 注入redis操作的Repository对象,进行任务执行进度显示
     * */
    public void setRedisRepository(RedisRepository redisRepository) {
        if (JobHelp.redisRepository == null){
            JobHelp.redisRepository = redisRepository;
        }
    }
    public JobHelp(long total) {
        this.total = total;
        this.pages = total % limit == 0 ? total / limit : (total / limit) + 1;
        this.pro = 1.00 / pages;
    }

    public JobHelp(int limit, int total) {
        this.limit = limit;
        this.total = total;
        this.pages = total % limit == 0 ? total / limit : (total / limit) + 1;
        this.pro = 1.00 / pages;
    }

    /**
     * 当分组条件为map以后,每次发送的limit必须为1,否则 执行任务时会进行进行等差式数列发单
    * */
    public JobHelp(Set<String> set){
        this.mapIndex = new ArrayList<String>(set);
        this.limit = 1;
        this.total = set.size();
        this.pages = total % limit == 0 ? total / limit : (total / limit) + 1;
        this.pro = 1.00 / pages;

    }
    /**
     * 任务进行分组后,具体处理数据的内容,用户自定义逻辑代码
     * */
    public void startJob(Job args, JobExecutor<T> jobExecutor) throws Exception{
        for (int i = 0; i < pages; i += step) {
            List<Future<T>> taskList = new ArrayList<>();
            long last = Math.min(i + step, pages);
            for (int j = i; j < last; j++) {
                int curr = j + 1;
                Future<T> future = jobExecutor.execute(curr, limit,args);
                taskList.add(future);
            }
            for (Future item : taskList) {
                log.info((String) item.get());
                progress++;
               // 进行任务进度的更新 可以根据项目中使用的redis操作对象来进行调整
                redisRepository.set("system:job:"+ progressRedisKey + ":progress" , String.format("%.6f", progress * pro));
            }
        }
        jobEnd();
    }

    /**
     * 任务进行分组后,具体处理数据的内容,用户自定义逻辑代码
     * */
    public void startJob(JobExecutor<T> jobExecutor) throws Exception{
        for (int i = 0; i < pages; i += step) {
            List<Future<T>> taskList = new ArrayList<>();
            long last = Math.min(i + step, pages);
            for (int j = i; j < last; j++) {
                Job args = new Job();
                int curr = j + 1;
                Future<T> future = jobExecutor.execute(curr, limit,args);
                taskList.add(future);
            }
            for (Future item : taskList) {
                log.info((String) item.get());
                progress++;
                redisRepository.set("system:job:"+ progressRedisKey + ":progress" , String.format("%.6f", progress * pro));
            }
        }
        jobEnd();
    }

    /**
     * 任务进行分组后,具体处理数据的内容,用户自定义逻辑代码
     * */
    public void startMapJob(Job args, JobExecutor<T> jobExecutor) throws Exception{
        for (int i = 0; i < pages; i += step) {
            List<Future<T>> taskList = new ArrayList<>();
            long last = Math.min(i + step, pages);
            for (int j = i; j < last; j++) {
                int curr = j + 1;
                args.setMapKey(mapIndex.get(j));
                Future<T> future = jobExecutor.execute(curr, limit,args);
                taskList.add(future);
            }
            for (Future item : taskList) {
                log.info((String) item.get());
                progress++;
                redisRepository.set("system:job:"+ progressRedisKey + ":progress" , String.format("%.6f", progress * pro));
            }
        }
        jobEnd();
    }

    public void startMapJob(JobExecutor<T> jobExecutor) throws Exception{
        for (int i = 0; i < pages; i += step) {
            List<Future<T>> taskList = new ArrayList<>();
            long last = Math.min(i + step, pages);
            for (int j = i; j < last; j++) {
                int curr = j + 1;
                Job args = new Job();
                args.setMapKey(mapIndex.get(j));
                Future<T> future = jobExecutor.execute(curr, limit,args);
                taskList.add(future);
            }
            for (Future item : taskList) {
                log.info((String) item.get());
                progress++;
                redisRepository.set("system:job:"+ progressRedisKey + ":progress" , String.format("%.6f", progress * pro));
            }
        }
        jobEnd();
    }

    public void jobEnd() {
        if (jobExecutorEnd !=null){
            jobExecutorEnd.executorEnd();
        }
    }
}

使用例子

@Autowired
private UserMapper userMapper;
@Autowired
private UserBlackMapper userBlackMapper;
@Autowired
private RedisRepository redisRepository;
// 进行任务数据的筛选 分组
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
LocalDate now = LocalDate.now();
String time = now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
queryWrapper.ge(User::getCjsj, now);
//获取指定日期新增的用户数
Page<User> resPage = userMapper.selectPage(new Page<>(1, 500), queryWrapper);
// 指定任务执行的额外参数
Job args = new Job();
args.setTime(time);
// 准备进行分组  进行本次任务数据总量,依据总量进行分页
JobHelp<String> jobHelp = new JobHelp<>(resPage.getTotal());
// 设置redis 操作工具类
jobHelp.setRedisRepository(redisRepository);
// 设置存储在redis任务的进度信息的 key
jobHelp.setProgressRedisKey("user:" + time);
try {
    // 根据分页进行任务具体任务处理
    jobHelp.startJob(args, (curr, limit, map) -> {
        // 进行具体的任务处理逻辑
        long execStartTime = System.currentTimeMillis();
        Page<User> page = userMapper.selectPage(new Page<>(curr, limit), queryWrapper);
        List<User> insertList = page.getRecords();
        userBlackMapper.saveBatch(insertList);
        long execEndTime = System.currentTimeMillis();
        return new AsyncResult<String>("每日新建用户备份" + curr + "页结束,总耗时" + (execEndTime - execStartTime) / 1000 + "s");
    });
} catch (Exception e) {
    e.printStackTrace();
    throw e;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值