一种基于springboot、redis的分布式任务引擎的实现(一)

总体思路是,主节点接收到任务请求,将根据任务情况拆分成多个任务块,将任务块标识的主键放入redis。发送redis消息,等待其他节点运行完毕,结束处理。接收到信息的节点注册本节点信息到redis、开启多线程、获取任务块、执行任务、结束处理。

1、主节点接收任务请求

java 复制代码
    @Override
    public void executeTaskInfo(PrepareDTO prepareDTO) {
        //异常标记
        String taskInfo = prepareDTO.getTaskId();
        //任务分组状态
        String taskStatus = "";
        try {
            log.info("数据准备任务并设定任务执行状态,{}", prepareDTO);
            this.dataPrepareBo.doStartGroupJobInfo(prepareDTO);
            //给redis集合中放计算对象
            log.info("开始放入计算任务:{}", prepareDTO);
            boolean getTaskFlag = this.dataPrepareBo.pushCalculationObject(prepareDTO);
            if (!getTaskFlag) {
                taskStatus = String.format("没有获取数据或计划已取消,%s", taskInfo);
                log.error(taskStatus);
                throw new Exception(taskStatus);
            }
            //发消息执行缓存中任务
            log.info("发消息执行任务:{}", prepareDTO);
            sendMessage(prepareDTO);
            //等待任务执行完毕
            log.info("等待任务执行结果");
            taskStatus = this.getGroupUpLoadTaskFinsh(prepareDTO);
        } catch (Exception e) {//捕获日志
            e.printStackTrace();
            taskStatus = "获取任务状态异常" + e;
            log.info(taskStatus);
            dataPrepareBo.putExceptionMsg2Cache(taskInfo, "数据准备分发计算任务线程异常:" + taskStatus);
        } finally {
            //做任务结束处理
            this.doGroupTaskFinshpPocess(prepareDTO, taskStatus);
        }
    }

2,发送消息

java 复制代码
    @Override
    public void sendMessage(String topic, String msg) {
        this.redisTemplate.convertAndSend(topic, msg);
    }

3,节点接收任务,并执行

java 复制代码
    public void doUpLoadTask(String msg) throws Exception {
        log.info("开始执行明细任务{}" + msg);
        String taskId = this.getTaskId(msg);
        try {
            Object cancelFlag = this.redisTemplate.opsForValue().get(String.format(EngineConstant.JOB_CANCEL_FLAG, taskId));
            if(cancelFlag != null && "1".equals(cancelFlag.toString())){
                log.info("本次任务已取消");
                return;
            }
            //上传本机执行信息到redis
            this.cacheBo.initGroupUpLoadTaskStats(taskId,ENGINE_DISTRIBUTION_RUNNING.getKey());
            //从缓存获取任务,获取任务后启线程执行任务。如果没获取到任务,则本节点任务执行完毕
            //循环获取任务
            this.groupTaskProcessBO.doGroupTaskProcess(taskId, null);
            //处理结束
            this.cacheBo.finishGroupUpLoadTaskStats(taskId,ENGINE_DISTRIBUTION_RUNNING.getKey());
        } catch (Exception e) {
			//记录日志
            taskUpldExeLogCDTO.setRunStas("-1");
            String exceptionInfo = this.taskLogUtils.getExceptionInfo(e) ;
            taskUpldExeLogCDTO.setAbnInfo(exceptionInfo);
            throw e;
        } finally {
			//记录日志
            taskUpldExeLogCDTO.setEndtime(DateUtil.getCurrentDate());
            if("-1".equals(taskUpldExeLogCDTO.getRunStas())){//异常结束
                this.taskLogUtils.sendLogInfo(taskUpldExeLogCDTO,"执行上传任务异常");
            } else {//正常结束
                taskUpldExeLogCDTO.setRunStas("1");
                this.taskLogUtils.sendLogInfo(taskUpldExeLogCDTO,"执行上传任务正常");
            }
        }
    }

4,开启线程执行任务

java 复制代码
    @Override
    public CalculationDTO doGroupTaskProcess(String taskId, TaskUpldExeLogCDTO taskUpldExeLogCDTO) throws Exception {
        List<Future> futureList = new ArrayList<>();
        //开始执行明细任务处理
        ThreadPoolTaskExecutor taskTransferExecutor = ToolUtil.getExecutor("engine-file-tasks-pool-", Math.min(parallelProcessNum,10), 8);
        ExecutorListHolder.putThreadPool(String.format(GroupConstant.PREPARE_ENGINE_POOL,taskId), taskTransferExecutor.getThreadPoolExecutor());
        for(int i = 0 ; i < parallelProcessNum ; i++) {
            DoGroupUpLoadTaskThread doGroupUpLoadTaskThread = new DoGroupUpLoadTaskThread(taskId
                    , redisTemplate, calculationBo, null, null);
            Future<?> future = taskTransferExecutor.submit(doGroupUpLoadTaskThread);
            futureList.add(future);
        }

        if (!CollectionUtil.isEmpty(futureList)) {
            futureList.forEach(f -> {
                try {
                    f.get(GroupTaskProcessBOImpl.maxTime, TimeUnit.SECONDS);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        log.info("本节点执行分组任务执行完毕{}", taskId + ":" + GroupConstant.IDENTITY);
        return null;
    }

5,线程执行明细

java 复制代码
    @Override
    public ResponseDTO call() throws Exception {
        //执行任务
        while(true) {
            FilterTableUniqueDTO filterTableUniqueDTO = (FilterTableUniqueDTO)this.redisTemplate.opsForList().leftPop(String.format(ENGINE_FILTERTABLEUNIQUE_QUEUE.getKey(), taskId));
            log.debug("取出任务:" + filterTableUniqueDTO);
            if(null == filterTableUniqueDTO) {
                break ;
            }
            long lastNum = this.redisTemplate.opsForList().size(String.format(ENGINE_FILTERTABLEUNIQUE_QUEUE.getKey(), taskId));
            log.info("生成文件剩余任务数量:" + lastNum);
//           处理任务
            calculationBo.GenerateFile(filterTableUniqueDTO, taskUpldDetlLogCDTO);
        }
        return null;
    }

以上是主要入口总体思路涉及代码,详细实现整理起来涉及内容比较繁多,将在第二部分分享。

相关推荐
掘金-我是哪吒7 分钟前
分布式微服务系统架构第129集:redis安装部署文档
redis·分布式·微服务·架构·系统架构
caihuayuan531 分钟前
生产模式下react项目报错minified react error #130的问题
java·大数据·spring boot·后端·课程设计
一只码代码的章鱼36 分钟前
Spring Boot- 2 (数万字入门教程 ):数据交互篇
spring boot·后端·交互
编程、小哥哥37 分钟前
Java大厂面试:从Web框架到微服务技术的场景化提问与解析
java·spring boot·微服务·面试·技术栈·数据库设计·分布式系统
计算机毕设定制辅导-无忧学长1 小时前
RabbitMQ 消息模式实战:从简单队列到复杂路由(一)
分布式·rabbitmq·ruby
阿四啊1 小时前
【Redis实战篇】秒杀优化
数据库·redis·缓存
小楠小楠小楠3 小时前
Redis的主从架构
数据库·redis·架构
编程、小哥哥3 小时前
互联网大厂Java面试场景:从缓存到容器化的技术问答
redis·docker·微服务·kubernetes·spring security·java面试·gitlab ci
MZWeiei3 小时前
Kafka 生产者工作流程详解
大数据·分布式·kafka
苹果酱05673 小时前
React方向:react脚手架的使用
java·vue.js·spring boot·mysql·课程设计