JobScheduler是elastic-job作业调度的关键类,也是起始类,在包com.dangdang.ddframe.job.lite.api下。调度任务的执行需要包含两大步骤:任务的配置和任务的注册。JobScheduler的构造函数除了任务配置和注册相关信息之外还有事件和监听。后两者是elastic-job的扩展功能,我们后续再介绍。
任务的配置
由于内部使用quartz作为任务调度框架,任务的配置的相关的基础信息也是和quartz一致的。elastic-job的任务配置类在quartz的基础上(执行方法,cron表达式等)额外封装了分片策略,监控作业相关参数以及与注册中心的时间误差秒数等配置项。
任务的注册
elastic-job是通过zookeeper进行任务协调和故障转移的,任务的注册也就是把任务注册到zookeeper里面去。任务的注册包含在任务的启动过程中。根节点是项目的名称,下面一级是任务的名称。任务一旦创建则不能修改任务的名称,如果修改名称将视为新的任务,创建新的节点。任务名称节点下又包含5个数据子节点,分别是config, instances, leader, servers和sharding。如下图:
1. config节点:
任务的配置信息,包含执行类,cron表达式,分片算法类,分片数量,分片参数等等。config节点的数据是通过ConfigService持久化到zookeeper中去的。默认状态下,如果你修改了Job的配置比如cron表达式,分片数量等是不会更新到zookeeper上去的,除非你把参数overwrite修改成true。
2. instances节点:
同一个Job下的elastic-job的部署实例。一台机器上可以启动多个Job实例,也就是Jar包。instances的命名是IP+@-@+PID。
3. leader节点:
任务实例的主节点信息,通过zookeeper的主节点选举,选出来的主节点信息。下面的子节点分为election,sharding和failover三个子节点。分别用于主节点选举,分片和失效转移处理。election下面的instance节点显式了当前主节点的实例ID:jobInstanceId。latch节点也是一个永久节点用于选举时候的实现分布式锁。sharding节点下面有一个临时节点,necessary,是否需要重新分片的标记。如果分片总数变化,或任务实例节点上下线或启用/禁用,以及主节点选举,都会触发设置重分片标记,主节点会进行分片计算。
4. servers节点:
任务实例的信息,主要是IP地址,任务实例的IP地址。如果多个任务实例在同一台机器上运行则只会出现一个IP子节点。可在IP地址节点写入DISABLED表示该任务实例禁用。 在新的cloud native架构下,servers节点大幅弱化,仅包含控制服务器是否可以禁用这一功能。为了更加纯粹的实现job核心,servers功能未来可能删除,控制服务器是否禁用的能力应该下放至自动化部署系统。
5. sharding节点:
任务的分片信息,子节点是分片项序号,从零开始,至分片总数减一。分片个个数是在任务配置中设置的。分片项序号的子节点存储详细信息。每个分片项下的子节点用于控制和记录分片运行状态。最主要的子节点就是instance。举例来说,上图有三个分片,每个分片下面有个instance的节点,也就说明了这个分片在哪个instance上运行。如上文所说如果分片总数变化,或任务实例节点上下线或启用/禁用,以及主节点选举,都会触发设置重分片标记,主节点会进行分片计算。分片计算的结果也就体现在这instance上。
上文介绍的节点信息并不全面,还有一些会在特定的情况下出现的临时节点,更多详细的介绍可以请参看官方文档:http://dangdangdotcom.github.io/elastic-job/elastic-job-lite/03-design/lite-design/
任务的启动
任务的启动过程,就是任务实例和zookeeper进行交互的过程。每个实例在启动过程中,会把自身的信息注册到zookeeper中去。并完成选举和分片策略的设置,也就是完成上文一些zookeeper节点的创建和持久化。
整个启动的过程都在JobScheduler.init()方法中完成。其中最重要的方法registerStartUpInfo完成了监听,选举持久化数据,以及设置分片标志位(为了任务执行是主节点进行分片算法)等工作。init()方法中完成了配置和注册之后,相关的参数被传递给了JobScheduleController类,这个类就是quartz的封装。之前配置的任务执行类和cron表达式被转换成JobDetail和Trigger这两个quartz的类,然后通过quartz的scheduler.start触发任务。等待任务的执行。
整体流程图如下:
任务的执行
任务的执行依赖于quartz job的触发。elastic-job的LiteJob类继承自quartz的Job类,在任务触发的时候,添加额外的逻辑处理。LiteJob的执行器AbstractElasticJobExecutor有两个具体的实现,SimpleJobExecutor和DataflowJobExecutor,各自执行SimpleJob和DataflowJob两种Job类型。Job触发的时候,SimpleJobExecutor或者DataflowJobExecutor会被new一次,重新从缓存中加载Job配置并执行。
LiteJob把任务的执行分为执行前,执行(业务代码的执行),和执行后三个阶段。在执行前阶段中主要实现分片策略的执行(shardingIfNecessary方法),记录事件,执行监听事件等等。而执行后阶段主要处理错过执行的相关任务以及执行监听事件。
整个执行流程图如下: