分布式调度、Elastic-Job

在工作中我们会遇到定时任务的需求(定时任务形式:每隔⼀定时间/特定某⼀时刻执⾏某个任务/逻辑)

例如:
订单审核、出库
订单超时⾃动取消、⽀付退款
礼券同步、⽣成、发放作业
物流信息推送、抓取作业、退换货处理作业
数据积压监控、⽇志监控、服务可⽤性探测作业
定时备份数据
⾦融系统每天的定时结算
数据归档、清理作业
报表、离线数据分析作业
 
分布式调度
 

什么是分布式任务调度?有两层含义

1)运⾏在分布式集群环境下的调度任务(同⼀个定时任务程序部署多份,只应该有⼀个定时任务在执⾏)

2)分布式调度—>定时任务的分布式—>定时任务的拆分(即为把⼀个⼤的作业任务拆分为多个⼩的作 业任务,多个实例同时执⾏)

分布式调度、Elastic-Job_第1张图片

 

定时任务的实现⽅式 -分布式调度框架Elastic-Job

Elastic-Job介绍

Elastic-Job是当当⽹开源的⼀个分布式调度解决⽅案,基于Quartz⼆次开发的,由两个相互独⽴的⼦项 ⽬Elastic-Job-LiteElastic-Job-Cloud组成。我们要学习的是 Elastic-Job-Lite,它定位为轻量级⽆中⼼化解决⽅案,使⽤Jar包的形式提供分布式任务的协调服务,⽽Elastic-Job-Cloud⼦项⽬需要结合Mesos 以及Docker在云环境下使⽤。

Elastic-Jobgithub地址:https://github.com/elasticjob

主要功能介绍
 

1、分布式调度协调

      在分布式环境中,任务能够按指定的调度策略执⾏,并且能够避免同⼀任务多实例重复执⾏

2、丰富的调度策略 基于成熟的定时任务作业框架Quartz cron表达式执⾏定时任务

3、弹性扩容缩容 当集群中增加某⼀个实例,它应当也能够被选举并执⾏任务;当集群减少⼀个实例 时,它所执⾏的任务能被转移到别的        实例来执⾏。

4、失效转移 某实例在任务执⾏失败后,会被转移到其他实例执⾏

5、错过执⾏作业重触发 若因某种原因导致作业错过执⾏,⾃动记录错过执⾏的作业,并在上次作业 完成后⾃动触发。

6、⽀持并⾏调度 ⽀持任务分⽚,任务分⽚是指将⼀个任务分为多个⼩任务项在多个实例同时执⾏。

7、作业分⽚⼀致性 当任务被分⽚后,保证同⼀分⽚在分布式环境中仅⼀个执⾏实例。

Elastic-Job-Lite应⽤

Elastic-Job 依赖于 Zookeeper 进⾏分布式协调,所以需要安装 Zookeeper 软件( 3.4.6 版本以上)

 

安装Zookeeper此处单例配置)

1)我们使⽤3.4.10版本,在linux平台解压下载的zookeeper-3.4.10.tar.gz

2)进⼊conf⽬录,cp zoo_sample.cfg zoo.cfg

3) 进⼊bin⽬录,启动zk服务

启动 ./zkServer.sh start

停⽌ ./zkServer.sh stop

查看状态 ./zkServer.sh status

 

引⼊Jar

 com.dangdang
 elastic-job-lite-core
 2.1.5

定时任务实例

 

需求:每隔两秒钟执⾏⼀次定时任务(resume表中未归档的数据归档到resume_bak表中, 每次归档1条记录)

1resume_bakresume表结构完全⼀样

2resume表中数据归档之后不删除,只将state置为"已归档"

 

数据表结构

DROP TABLE IF EXISTS `resume`;CREATE TABLE `resume` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`name` varchar(255) DEFAULT NULL,

`sex` varchar(255) DEFAULT NULL,

`phone` varchar(255) DEFAULT NULL,

`address` varchar(255) DEFAULT NULL,

`education` varchar(255) DEFAULT NULL,

`state` varchar(255) DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=1001 DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

程序开发

定时任务类

import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;

import javax.swing.*;
import java.sql.Connection;
import java.util.List;
import java.util.Map;

/**
 * ElasticJobLite定时任务业务逻辑处理类
 * 定义一个任务类实现 SimpleJob接口
 */
public class BackupJob implements SimpleJob {

    // 定时任务每执行一次都会执行此方法逻辑
    // execute方法 写我们自己的定时任务业务逻辑
    @Override
    public void execute(ShardingContext shardingContext) {

        //  分片数 当前是哪个分片
        int shardingItem = shardingContext.getShardingItem();
        System.out.println("当前分片:"+shardingItem);// 当前分片:0
        // 获取分片参数
        String shardingParameter = shardingContext.getShardingParameter();//
        System.out.println("分片参数:"+shardingParameter);// 分片参数:doctor
        // 1.从resume中查出一条记录
        String sql="select * from resume where state='未归档' and education='"+shardingParameter+"' limit 1";
        List> list = JdbcUtil.executeQuery(sql);
        if (list==null || list.size()==0){
            System.out.println("数据处理完毕");
            return;
        }
        Map stringObjectMap = list.get(0);
        Long id = (Long) stringObjectMap.get("id");
        String address = (String) stringObjectMap.get("address");
        String education = (String) stringObjectMap.get("education");
        System.out.println("查询数据:id:"+id+",address:"+address+",education:"+education);

        // 2修改数据
        String updateSql = "update resume set state='已归档' where id=?";
        JdbcUtil.executeUpdate(updateSql,id);

        // 3.向resume_bak中插入数据 归档
       // String insertSql = "insert into resume_bak  select * from resume where id=?";
        String insertSql="insert into resume_bak select * from resume where id=?";
        JdbcUtil.executeUpdate(insertSql,id);
    }
}

 

主类

 

import com.dangdang.ddframe.job.config.JobCoreConfiguration;
import com.dangdang.ddframe.job.config.simple.SimpleJobConfiguration;
import com.dangdang.ddframe.job.lite.api.JobScheduler;
import com.dangdang.ddframe.job.lite.config.LiteJobConfiguration;
import com.dangdang.ddframe.job.reg.base.CoordinatorRegistryCenter;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperConfiguration;
import com.dangdang.ddframe.job.reg.zookeeper.ZookeeperRegistryCenter;

public class ElasticJobMain {

    public static void main(String[] args) {
        // 配置分布式协调服务 zookeeper ,不能让任务重复执行
        // zookeeper 地址,zookeeper命名空间,空间下可以有多个任务
        ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration("192.168.206.131:2181","data-archive-job");
        // 注册中心对象
        CoordinatorRegistryCenter coordinatorRegistryCenter = new ZookeeperRegistryCenter(zookeeperConfiguration);
        coordinatorRegistryCenter.init();

        //配置 任务
          // 时间事件

        JobCoreConfiguration jobCoreConfiguration = JobCoreConfiguration
                // 任务名字 ,任务执行时间 -每两秒执行一次,分片数量
                .newBuilder("backupJob", "*/2 * * * * ?", 3)
                // 每个分片处理的关键信息 0=doctor,表示给0号分片传递的参数,
                .shardingItemParameters("0=doctor,1=bachelor ,2=master")
                .build();

        // 关联任务逻辑
        SimpleJobConfiguration simpleJobConfiguration = new SimpleJobConfiguration(jobCoreConfiguration, BackupJob.class.getName());

        // 调度器
        JobScheduler jobScheduler = new JobScheduler(coordinatorRegistryCenter, LiteJobConfiguration.newBuilder(simpleJobConfiguration).overwrite(true).build());
        jobScheduler.init();
    }

}

 

测试

1)可先启动⼀个进程,然后再启动⼀个进程(两个进程模拟分布式环境下,通⼀个定时任务 部署了两份在⼯作)

2)两个进程逐个启动,观察现象

    一个进程启动后,任务执行,打印出日志,第二个进程也启动后,开始执行定时任务,同时第二个进程停止执行定时任务。

(  在分布式环境中,任务能够按指定的调度策略执⾏,并且能够避免同⼀任务多实例重复执⾏)

3)关闭其中执⾏的进程,观察现象

     执行的进程关闭后(模拟宕机),另一个进程开始 执行定时任务

(弹性扩容缩容 当集群中增加某⼀个实例,它应当也能够被选举并执⾏任务;当集群减少⼀个实例 时,它所执⾏的任务能被转移到别的        实例来执⾏。)

此时可以用可视化工具查看zookeeper的结构目录

分布式调度、Elastic-Job_第2张图片

Leader节点选举机制

每个Elastic-Job的任务执⾏实例App作为Zookeeper的客户端来操作ZooKeeperznode

1)多个实例同时创建/leader节点

2/leader节点只能创建⼀个,后创建的会失败,创建成功的实例会被选为leader节点, 执⾏任务

 

 

Elastic-Job-Lite轻量级去中⼼化的特点

先说一下什么是中心化:指有一个统一任务分发器,定时给服务1分配任务,定时给服务2分发任务。

elasticjob去中心化:是服务自己在zookeeper上同步数据,自己拿到一些数据,然后去执行,自我驱动,不是被动分发的。

每个服务节点是一样的包括ElasticJob的jar和我们自己写的程序,唯一不同的可能是分片。

分布式调度、Elastic-Job_第3张图片

 

任务分⽚

(即为把⼀个⼤的作业任务拆分为多个⼩的作业任务,多个实例同时执⾏)

⼀个⼤的⾮常耗时的作业Job,⽐如:⼀次要处理⼀亿的数据,那这⼀亿的数据存储在数据库中,如果 ⽤⼀个作业节点处理⼀亿数据要很久,在互联⽹领域是不太能接受的,互联⽹领域更希望机器的增加去 横向扩展处理能⼒。所以,ElasticJob可以把作业分为多个的task(每⼀个task就是⼀个任务分⽚),每 ⼀个task交给具体的⼀个机器实例去处理(⼀个机器实例是可以处理多个task的),但是具体每个task 执⾏什么逻辑由我们⾃⼰来指定。

分布式调度、Elastic-Job_第4张图片

分布式调度、Elastic-Job_第5张图片

一个实例执行结果:一个实例执行3片

分布式调度、Elastic-Job_第6张图片

增加一个实例执行,一共两个实例执行3片

一个实例执行一片任务,结果如下:

分布式调度、Elastic-Job_第7张图片

第二个实例执行两个分片,结果如下:

分布式调度、Elastic-Job_第8张图片

 

弹性扩容

再增加一个实例,共三个实例:

第0个实例执行第2个分片,结果 如下:

分布式调度、Elastic-Job_第9张图片

第1个实例执行第1个分片,结果如下:

分布式调度、Elastic-Job_第10张图片

第二个实例执行第0个分片,结果 如下:

分布式调度、Elastic-Job_第11张图片

 

分布式调度、Elastic-Job_第12张图片

新增加⼀个运⾏实例,它会⾃动注册到注册中⼼,注册中⼼发现新的服务上线,注册中⼼会通知 ElasticJob 进⾏重新分⽚,那么总得分⽚项有多少,那么就可以搞多少个实例机器,⽐如完全可以分 1000⽚ ,那么就可以搞1000台机器⼀起执⾏作业

 

注意:

1)分⽚项也是⼀个JOB配置,修改配置,重新分⽚,在下⼀次定时运⾏之前会重新调⽤分⽚算法,那么 这个分⽚算法的结果就是:哪台机器运⾏哪⼀个⼀⽚,这个结果存储到zookeeper中的,主节点(leader)会把分⽚给分好(比如,第0个机器运行第2个分片,第1个机器运行第0个分片,第2个机器运行第1个分片) 放到注册中⼼去,然后执⾏节点从注册中⼼获取信息(执⾏节点在定时任务开启的时候获取相应的分⽚)

2)如果所有的节点挂掉值剩下⼀个节点,所有分⽚都会指向剩下的⼀个节点,这也是ElasticJob的⾼可⽤

 

你可能感兴趣的:(ElasticJob)