任务调度中心实现方式(三) 事件驱动异步任务分发

 

1、初步设计的应用任务结构。jobkey设计为前缀“app_job-”+id. jobkey=triggerkey

任务管理表 折叠原码

DROP TABLE IF EXISTS `APPLICATION_TASK`;

CREATE TABLE `APPLICATION_TASK` (

  `id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT 'id',

  `job_type` varchar(20) NOT NULL COMMENT 'cron、simple两种模式',

  `task_name` varchar(50) NOT NULL COMMENT '应用任务名称',

  `task_type` varchar(200) NOT NULL COMMENT '任务类型,可以直接使用TriggerMessage中的TriggerEvent定义的。需代码维护与Verticle的address的对应关系',

  `is_deleted` TINYINT(3) NOT NULL COMMENT '删除标识',

  `status` INT NOT NULL COMMENT '启停标识',

  `param_json` VARCHAR(500) COMMENT '不同任务需要的参数json,如数据归档需要制定数据表id',

  `ip` VARCHAR(20) NOT NULL COMMENT '执行任务的ip',

  PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

任务记录表 折叠原码

DROP TABLE IF EXISTS `TASK_LOG`;

CREATE TABLE `TASK_LOG` (

  `id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT 'id',

  `task_id` bigint(10) NOT NULL COMMENT 'task id',

  `task_name` varchar(50) NOT NULL COMMENT '应用任务名称',

  `task_type` varchar(200) NOT NULL COMMENT '任务类型,比如归档任务为DATAFILER',

  `start_time` DATETIME NOT NULL COMMENT '任务开始时间',

  `end_time` DATETIME NOT NULL COMMENT '任务结束时间',

  `execute_status` INT COMMENT '执行状态:成功、异常',

  `fail_message` VARCHAR(500) COMMENT '异常信息',

  `ip` VARCHAR(20) NOT NULL COMMENT '执行任务的ip',

  PRIMARY KEY (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2、参数介绍。

1)、task_type.任务类型。对于开发好的不同后端逻辑的任务。比如:任务归档的任务类型为DATAFILER.task_type要与对应job的实现对应。

TaskType示例 折叠原码

public enum TaskType {

 

    DATAFILER(DataFilerJob.class);

    Class<? extends AbstractJob> jobClass;

    TaskType(Class<? extends AbstractJob> jobClass){

        this.jobClass = jobClass;

    }

    public Class<? extends AbstractJob> getJobClass() {

        return jobClass;

    }

}

上面的TaskType中用到了自定义Job。Job初步设计如下:

AbstractJob中统一完成落库操作 折叠原码

package com.back.task;

 

import com.back.model.AppTask;

 

/**

 * 这里思考对于任务记录操作能否统一处理

 * 如果能够统一处理,代表也可以在这里加控制。做一个是否持久化任务痕迹的配置

 * @author Administrator

 *

 */

public abstract class AbstractJob implements Job {

 

    private AppTask task;

    

    private TaskLog taskLog;

     

    public AbstractJob(AppTask task){

        this.task = task;

    }

    /**

     * 对于call内部逻辑中的异常 ,自定义一个异常,单独记录一个message

     * 在这里的catch后 拿到message 后在after()逻辑中落库

     * 这里是为了完成比如归档任务中任务分为两个(或多个)阶段,message可以帮助我们记录哪些阶段成功,在哪个阶段失败。

     */

    @Override

    public void execute() {

        before();

        try{

            call();

        }catch(Exception e){

             

        }

        after();

    }

 

    //前置逻辑 初始化taskLog

    private void before() {

        taskLog =

    }

 

    public abstract void call();

 

    //这里完成任务记录taskLog落库逻辑

    private void after() {

         

    }

}

2)、schedule_type调度类型。暂时支持SIMPLE、CRON两种。SIMPLE是每隔多长时间触发一次的(可指定开始何时触发)。CRON是支持填写CRON表达式触发。

      3)、参数param_json。当schedule_type为SIMPLE时paramJson中存在interval(代表执行周期)和timeunit(代表时间单位:day、hour、minute、second)

   不同任务需要的差异化参数都存在这个json中。

3、集群下任务执行。

1)任务执行过程。产生任务对象AppTask send到TaskVerticle,taskVerticle完成调度触发统一Job DispatcherJob ,DispatcherJob再分发给不同节点上的JobScheduler,来完成任务逻辑

TaskVerticle大致逻辑 折叠原码

package com.back.verticle;

 

import static com.back.model.quartz.DispatchJob.EVENTBUS;

import static com.back.model.quartz.DispatchJob.PARAM;

import static com.back.model.quartz.DispatchJob.VERTICLECLASS;

 

import java.sql.Timestamp;

import java.text.ParseException;

import java.util.Date;

import java.util.concurrent.atomic.AtomicInteger;

 

import org.apache.http.client.utils.DateUtils;

import org.quartz.JobDataMap;

import org.quartz.JobKey;

import org.quartz.Scheduler;

import org.quartz.SchedulerException;

import org.quartz.impl.JobDetailImpl;

import org.quartz.impl.StdSchedulerFactory;

import org.quartz.impl.triggers.CronTriggerImpl;

import org.quartz.impl.triggers.SimpleTriggerImpl;

 

import com.back.extend.ProtobufObject;

import com.back.inject.ConfigUtils;

import com.back.model.AppTask;

import com.back.model.ScheduleType;

import com.back.model.quartz.DispatchJob;

import com.back.util.DateUtil;

import com.back.util.JsonUtils;

import com.fasterxml.jackson.databind.JsonNode;

 

import io.vertx.core.AbstractVerticle;

import io.vertx.core.Handler;

import io.vertx.core.eventbus.EventBus;

import io.vertx.core.eventbus.Message;

 

public class TaskVerticle extends AbstractVerticle{

 

    private final static String address = ConfigUtils.ignite.cluster().localNode().id().toString()

            + TaskVerticle.class.getSimpleName();

    private final static String publicAddress = TaskVerticle.class.getSimpleName();

    private Scheduler scheduler;

    private AtomicInteger counter = new AtomicInteger();

    @Override

    public void start() throws Exception {

        super.start();

        this.scheduler = StdSchedulerFactory.getDefaultScheduler();

        scheduler.start();

        EventBus eventBus = vertx.eventBus();

         

        eventBus.consumer(address , new Handler<Message<ProtobufObject>>(){

            public void handle(Message<ProtobufObject> event) {

                handlerMessage(event);

            }

        });

    }

     

    private void handlerMessage(Message<ProtobufObject> event) {

        if(event == null){

            //抛出异常

        }

        AppTask task = (AppTask)event.body().getObject();

         

        Long id = task.getId();

        String paramJson = task.getParamJson();

        String jobKey = id == null "appJob-" this.counter.incrementAndGet() : id+"";

        JobDetailImpl jobDetail = new JobDetailImpl();

        jobDetail.setJobClass(DispatchJob.class);

        jobDetail.setKey(new JobKey(jobKey));

        JobDataMap jobDataMap = new JobDataMap();

        jobDataMap.put(EVENTBUS, vertx.eventBus());

        jobDataMap.put(VERTICLECLASS, JobVerticle.class);

        jobDataMap.put(PARAM, paramJson);

        jobDetail.setJobDataMap(jobDataMap);

        JsonNode jsonNode = JsonUtils.getJsonNode(paramJson);

        ScheduleType type = ScheduleType.valueOf(task.getScheduleType());

        if(type == ScheduleType.CRON){

            CronTriggerImpl cronTrigger = new CronTriggerImpl();

            cronTrigger.setName(id+"");

            try {

                cronTrigger.setCronExpression(jsonNode.get("cron").asText());

            catch (ParseException e1) {

                e1.printStackTrace();

            }

            try {

                scheduler.scheduleJob(jobDetail, cronTrigger);

            catch (SchedulerException e) {

                e.printStackTrace();

            }

             

        }else{

            SimpleTriggerImpl simpleTrigger = new SimpleTriggerImpl();

            simpleTrigger.setName(id+"");

            Date date = DateUtils.parseDate(jsonNode.get("startTime").asText());

            simpleTrigger.setStartTime(date);

            int day = jsonNode.get("interval").asInt();

            simpleTrigger.setRepeatInterval(day * 24 60 60 1000);

            try {

                scheduler.scheduleJob(jobDetail, simpleTrigger);

            catch (SchedulerException e) {

                e.printStackTrace();

            }

        }

         

    }

}

DispatcherJob分发逻辑 折叠原码

public class DispatcherJob implements Job {

 

    public static final String EVENTBUS = "EVENTBUS_KEY";

    public static final String VERTICLECLASS = "VERTICLECLASS_KEY";

    public static final String PARAM = "PARAM_KEY";

    @Override

    public void execute(JobExecutionContext context) throws JobExecutionException {

        JobDataMap dataMap = context.getMergedJobDataMap();

        EventBus eventbus = (EventBus)dataMap.get(EVENTBUS);

        Class verticleClass = (Class)dataMap.get(VERTICLECLASS);

        String address = verticleClass.getSimpleName();

        eventbus.send(address, new ProtobufObject(dataMap.get(PARAM)));

    }

}

JobVerticle逻辑 折叠原码

public class JobVerticle extends AbstractVerticle {

  

    private final static String address = ConfigUtils.ignite.cluster().localNode().id().toString()

            + TaskVerticle.class.getSimpleName();

    @Override

    public void start() throws Exception {

  

        EventBus eventBus = vertx.eventBus();

        eventBus.consumer(address, new Handler<Message<ProtobufObject>>() {

            public void handle(Message<ProtobufObject> event) {

                handlerMessage(event);

            }

        });

    }

    private void handlerMessage(Message<ProtobufObject> event) {

        AppTask task = (AppTask)event.body().getObject();

        TaskType type = TaskType.valueOf(task.getTaskType());

        Class<? extends AbstractJob> jobClass = type.getJobClass();

        try {

            Job job = jobClass.getConstructor(AppTask.class).newInstance(task);

            job.execute();

        catch (Exception e) {

            e.printStackTrace();

        }

          

    }

}

新增任务类型时需要继承的抽象类设计 折叠原码

package com.back.task;

  

import com.back.model.AppTask;

  

/**

 * 这里思考对于任务记录操作能否统一处理

 * 如果能够统一处理,代表也可以在这里加控制。做一个是否持久化任务痕迹的配置

 * @author Administrator

 *

 */

public abstract class AbstractJob implements Job {

  

    private AppTask task;

     

    private TaskLog taskLog;

      

    public AbstractJob(AppTask task){

        this.task = task;

    }

    /**

     * 对于call内部逻辑中的异常 ,自定义一个异常,单独记录一个message

     * 在这里的catch后 拿到message 后在after()逻辑中落库

     * 这里是为了完成比如归档任务中任务分为两个(或多个)阶段,message可以帮助我们记录哪些阶段成功,在哪个阶段失败。

     */

    @Override

    public void execute() {

        before();

        try{

            call();

        }catch(Exception e){

              

        }

        after();

    }

  

    //前置逻辑 初始化taskLog

    private void before() {

        taskLog =

    }

  

    public abstract void call();

  

    //这里完成任务记录taskLog落库逻辑

    private void after() {

          

    }

}

4、增删改查、启停任务。

1)

 任务新增:Mysql新增Apptask,send到TaskVerticle中

  任务删除:mysql中的任务对应的ip与verticle拼接,可以使请求send到之前任务执行的节点上,进而通过quartz删除任务

  任务修改:删除后新增。ip重新获得,mysql中现有任务中ip对应任务最少的(min).

 任务的启停为应用层面的逻辑,实际也对应任务删除与新增。

4)项目启动。在根据存储的ip+Verticle来确定每个任务运行的节点。所有未删除且启动标识为启动的任务

5、对于脚本任务等部分节点才能生效的任务。

1)配置文件中配置标识。比如schedule.node.use = ["DATAFILTER"]。

2)可以通过将标识拼入Verticle的address来完成。

6、应用任务的基础功能

1)在web提供手动触发任务。直接send ApplicationTask到TaskVerticle.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值