查看原文
其他

分布式作业系统 Elastic-Job-Cloud 源码分析 —— 作业配置

老艿艿 芋道源码 2019-05-13

点击上方“芋道源码”,选择“设为星标”

做积极的人,而不是积极废人!

源码精品专栏

 

摘要: 原创出处 http://www.iocoder.cn/Elastic-Job/cloud-job-config/ 「芋道源码」欢迎转载,保留摘要,谢谢!

本文基于 Elastic-Job V2.1.5 版本分享

  • 1. 概述

  • 2. 云作业App

    • 2.1 云作业App配置类

    • 2.2 操作云作业App配置

  • 3. 云作业

    • 3.1 云作业配置

    • 3.2 本地云作业配置

    • 3.3 云作业配置总结

  • 666. 彩蛋


1. 概述

本文主要分享 Elastic-Job-Cloud 作业配置

如果你阅读过以下文章,有助于对本文的理解:

  • 《官方文档 —— RESTFUL API》

  • 《Elastic-Job-Lite 源码分析 —— 作业配置》

  • 《由浅入深 | 如何优雅地写一个Mesos Framework》

😈 另外,笔者假设你已经对 《Elastic-Job-Lite 源码分析系列》 有一定的了解。

本文涉及到主体类的类图如下( 打开大图 ):

  • 黄色的类在 elastic-job-common-core 项目里,为 Elastic-Job-Lite、Elastic-Job-Cloud 公用作业配置类。

  • 紫色的类在 elastic-job-cloud 项目里,为 Elastic-Job-Cloud 作业配置类。

  • 红色的类在 elastic-job-lite 项目里,为 Elastic-Job-Lite 作业配置类。

你行好事会因为得到赞赏而愉悦 
同理,开源项目贡献者会因为 Star 而更加有动力 
为 Elastic-Job 点赞!传送门

2. 云作业App

首先,理解下 云作业App 的定义:

FROM http://dangdangdotcom.github.io/elastic-job/elastic-job-cloud/02-guide/cloud-concepts/

作业APP指作业打包部署后的应用,描述了作业启动需要用到的CPU、内存、启动脚本及应用下载路径等基本信息,每个APP可以包含一个或多个作业。

简单来说,一个云作业App可以理解成由多个作业打在一起的 jar

2.1 云作业App配置类

CloudAppConfiguration,云作业App配置。实现代码如下:

public final class CloudAppConfiguration {

    /**
     * 应用名
     */

    private final String appName;
    /**
     * 应用包地址
     */

    private final String appURL;
    /**
     * 应用启动脚本
     */

    private final String bootstrapScript;
    /**
     * cpu 数量
     */

    private double cpuCount = 1;
    /**
     * 内存 大小
     */

    private double memoryMB = 128;
    /**
     * 每次执行作业时是否从缓存中读取应用。禁用则每次执行任务均从应用仓库下载应用至本地
     */

    private boolean appCacheEnable = true;
    /**
     * 常驻作业事件采样率统计条数,默认不采样全部记录。
     * 为避免数据量过大,可对频繁调度的常驻作业配置采样率,即作业每执行N次,才会记录作业执行及追踪相关数据
     */

    private int eventTraceSamplingCount;
}
  • 在 Elastic-Job-Lite 里,打包作业,部署到服务器里启动。而在 Elastic-Job-Cloud 里,打包作业上传至可下载的地址。作业被调度时,Mesos 会从 appURL 下载应用包,使用 bootstrapScript 启动应用进行执行。实际情况会更加复杂一丢丢,在《Elastic-Job-Cloud 源码解析 —— 作业调度(一)》详细解析。

  • cpuCountmemoryMB 配置云作业App自身占用的资源情况。其包含的每个作业占用的资源情况,使用作业对应的云作业配置( CloudJobConfiguration ) ,下文也会看到。

  • appCacheEnable:每次执行作业时是否从缓存中读取应用。禁用则每次执行任务均从应用仓库下载应用至本地。

  • eventTraceSamplingCount:常驻作业事件采样率统计条数,默认采样全部记录。为避免数据量过大,可对频繁调度的常驻作业配置采样率,即作业每执行N次,才会记录作业执行及追踪相关数据。

2.2 操作云作业App配置

云作业App配置有多种操作:

  1. 添加 / 更新 / 删除

  2. 开启 / 禁用

有两种方式进行操作,以添加举例子:

  • 调用 HTTP 接口:

    curl -l -H "Content-type: application/json" -X POST -d '{"appName":"foo_app","appURL":"http://app_host:8080/yourJobs.gz","cpuCount":0.1,"memoryMB":64.0,"bootstrapScript":"bin/start.sh","appCacheEnable":true,"eventTraceSamplingCount":0}' http://elastic_job_cloud_host:8899/api/app
  • 运维平台

运维平台是对调用 HTTP 接口的UI封装,实现代码如下:

// CloudAppRestfulApi
@Path("/app")
public final class CloudAppRestfulApi {
    /**
     * 注册应用配置.
     * 
     * @param appConfig 应用配置
     */

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public void register(final CloudAppConfiguration appConfig) {
        Optional<CloudAppConfiguration> appConfigFromZk = appConfigService.load(appConfig.getAppName());
        if (appConfigFromZk.isPresent()) {
            throw new AppConfigurationException("app '%s' already existed.", appConfig.getAppName());
        }
        appConfigService.add(appConfig);
    }
}

// CloudAppConfigurationService.java
/**
* 添加云作业APP配置.
*
* @param appConfig 云作业App配置对象
*/

public void add(final CloudAppConfiguration appConfig) {
   regCenter.persist(CloudAppConfigurationNode.getRootNodePath(appConfig.getAppName()), CloudAppConfigurationGsonFactory.toJson(appConfig));
}

// CloudAppConfigurationNode.JAVA
public final class CloudAppConfigurationNode {

    public static final String ROOT =  "/config/app";

    private static final String APP_CONFIG =  ROOT + "/%s"// %s = ${APP_NAME}
}
  • CloudAppRestfulApi,云作业应用的REST API,实现了云作业App配置的多种操作的 HTTP 接口。

  • CloudAppConfigurationService,云作业App配置服务,实现了云作业应用的存储功能。

  • 调用 AppConfigService#add(…) 方法,存储 CloudAppConfiguration 到注册中心( Zookeeper )的持久数据节点 ${NAMESPACE}/config/app/${APP_NAME},JSON 格式化对象。使用 zkClient 查看如下:

[zk: localhost:2181(CONNECTED) 1] get /elastic-job-cloud/config/app/exampleApp
{"appName":"exampleApp","appURL":"http://785j8w.com1.z0.glb.clouddn.com/elastic-job-example-cloud-2.1.5.tar.gz","bootstrapScript":"bin/start.sh","cpuCount":1.0,"memoryMB":128.0,"appCacheEnable":true,"eventTraceSamplingCount":0}

3. 云作业

一个云作业应用可以包含一个或多个云作业。云作业有两种作业配置:云作业配置、本地云作业配置。下面来分别分享它们。

3.1 云作业配置

CloudJobConfiguration,云作业配置。实现代码如下:

public final class CloudJobConfiguration implements JobRootConfiguration {

    /**
     * 作业应用名称 {@link com.dangdang.ddframe.job.cloud.scheduler.config.app.CloudAppConfiguration}
     */

    private final String appName;
    /**
     * 作业类型配置
     */

    private final JobTypeConfiguration typeConfig;
    /**
     * 单片作业所需要的CPU数量,最小值为0.001
     */

    private final double cpuCount;
    /**
     * 单片作业所需要的内存MB,最小值为1
     */

    private final double memoryMB;
    /**
     * 作业执行类型
     */

    private final CloudJobExecutionType jobExecutionType;
    /**
     * Spring容器中配置的bean名称
     */

    private String beanName;
    /**
     * Spring方式配置Spring配置文件相对路径以及名称,如:META-INF\applicationContext.xml
     */

    private String applicationContext; 
}
  • JobTypeConfiguration,作业类型配置,在 elastic-job-common-core 项目里,为 Elastic-Job-Lite、Elastic-Job-Cloud 公用作业配置类。在《Elastic-Job-Lite 源码分析 —— 作业配置》的「2.2.1 作业类型配置」有详细解析。

  • cpuCountmemoryMB 配置单片作业占用的资源情况。这里一定要注意是单片作业,例如一个作业有三个分片( shardingTotalCount = 3 ),则占用资源为 3 * cpuCount + 3 * memoryMB

  • 作业执行类型( CloudJobExecutionType )有两种:常驻作业( DAEMON ),瞬时作业( TRANSIENT )。在《Elastic-Job-Cloud 源码解析 —— 作业调度(一)》详细解析。Elastic-Job-Cloud 独有,非常有趣。👍👍👍

  • beanNameapplicationContext 实现 Spring 启动方式作业。在《Elastic-Job-Cloud 源码解析 —— 作业调度(一)》有详细解析。

3.1.1 操作云作业配置

云作业配置有多种操作:

  1. 添加 / 更新 / 删除

  2. 开启 / 禁用

有两种方式进行操作,以添加举例子:

  • 调用 HTTP 接口:

    // Java启动方式作业注册
    curl -l -H "Content-type: application/json" -X POST -d '{"jobName":"foo_job","appName":"foo_app","jobClass":"yourJobClass","jobType":"SIMPLE","jobExecutionType":"TRANSIENT","cron":"0/5 * * * * ?","shardingTotalCount":5,"cpuCount":0.1,"memoryMB":64.0,"appURL":"http://app_host:8080/foo-job.tar.gz","failover":true,"misfire":true,"bootstrapScript":"bin/start.sh"}' http://elastic_job_cloud_host:8899/api/job/register

    // Spring启动方式作业注册
    curl -l -H "Content-type: application/json" -X POST -d '{"jobName":"foo_job","jobClass":"yourJobClass","beanName":"yourBeanName","applicationContext":"applicationContext.xml","jobType":"SIMPLE","jobExecutionType":"TRANSIENT","cron":"0/5 * * * * ?","shardingTotalCount":5,"cpuCount":0.1,"memoryMB":64.0,"appURL":"http://file_host:8080/foo-job.tar.gz","failover":false,"misfire":true,"bootstrapScript":"bin/start.sh"}' http://elastic_job_cloud_masterhost:8899/api/job/register
  • 运维平台

运维平台是对调用 HTTP 接口的UI封装,实现代码如下:

// CloudJobRestfulApi.java
public final class CloudJobRestfulApi {
    /**
     * 注册作业.
     * 
     * @param jobConfig 作业配置
     */

    @POST
    @Path("/register")
    @Consumes(MediaType.APPLICATION_JSON)
    public void register(final CloudJobConfiguration jobConfig) {
        producerManager.register(jobConfig);
    }
}

// ProducerManager.java
public final class ProducerManager {
    /**
     * 注册作业.
     * 
     * @param jobConfig 作业配置
     */

    public void register(final CloudJobConfiguration jobConfig) {
        if (disableJobService.isDisabled(jobConfig.getJobName())) {
            throw new JobConfigurationException("Job '%s' has been disable.", jobConfig.getJobName());
        }
        Optional<CloudAppConfiguration> appConfigFromZk = appConfigService.load(jobConfig.getAppName());
        if (!appConfigFromZk.isPresent()) {
            throw new AppConfigurationException("Register app '%s' firstly.", jobConfig.getAppName());
        }
        Optional<CloudJobConfiguration> jobConfigFromZk = configService.load(jobConfig.getJobName());
        if (jobConfigFromZk.isPresent()) {
            throw new JobConfigurationException("Job '%s' already existed.", jobConfig.getJobName());
        }
        // 添加云作业配置
        configService.add(jobConfig);
        // 调度作业
        schedule(jobConfig);
    }
}

// CloudJobConfigurationService.java
/**
* 添加云作业配置.

* @param jobConfig 云作业配置对象
*/

public void add(final CloudJobConfiguration jobConfig) {
   regCenter.persist(CloudJobConfigurationNode.getRootNodePath(jobConfig.getJobName()), CloudJobConfigurationGsonFactory.toJson(jobConfig));
}

// CloudJobConfigurationNode.java
public final class CloudJobConfigurationNode {

    public static final String ROOT =  "/config/job";

    private static final String JOB_CONFIG =  ROOT + "/%s";  // %s = ${JOB_NAME}
}
  • CloudJobRestfulApi,作业云Job的REST API,实现了作业云Job配置的多种操作、查询运行中 / 待运行 / 失效转移作业列表等 HTTP 接口。

  • ProducerManager,发布任务作业调度管理器。这是一个很重要的类,在《Elastic-Job-Cloud 源码解析 —— 作业调度(一)》详细解析。

  • CloudJobConfigurationService,作业配置服务,实现了作业配置的存储功能。

  • 调用 CloudJobConfigurationService#add(...) 方法,存储 CloudJobConfiguration 到注册中心( Zookeeper )的持久数据节点 ${NAMESPACE}/config/job/${JOB_NAME},JSON 格式化对象。使用 zkClient 查看如下:

    [zk: localhost:2181(CONNECTED) 3] get /elastic-job-cloud/config/job/test_job_simple
    {"jobName":"test_job_simple","jobClass":"com.dangdang.ddframe.job.example.job.simple.JavaSimpleJob","jobType":"SIMPLE","cron":"0/10 * * * * ?","shardingTotalCount":1,"shardingItemParameters":"","jobParameter":"","failover":false,"misfire":false,"description":"","jobProperties":{"job_exception_handler":"com.dangdang.ddframe.job.executor.handler.impl.DefaultJobExceptionHandler","executor_service_handler":"com.dangdang.ddframe.job.executor.handler.impl.DefaultExecutorServiceHandler"},"appName":"exampleApp","cpuCount":0.1,"memoryMB":64.0,"jobExecutionType":"TRANSIENT"}
  • 调用 #schedule(…) 方法,调度作业。这是个很有趣的方法,在《Elastic-Job-Cloud 源码解析 —— 作业调度(一)》详细解析。

3.2 本地云作业配置

LocalCloudJobConfiguration,本地云作业配置。实现代码如下:

public final class LocalCloudJobConfiguration implements JobRootConfiguration {

    private final JobTypeConfiguration typeConfig;

    /**
     * 分片作业序号
     */

    private final int shardingItem;

    private String beanName;

    private String applicationContext;
}
  • shardingItem,分片作业序号,用于本地调试指定分片作业项。

到底有什么用呢?

在开发Elastic-Job-Cloud作业时,开发人员可以脱离Mesos环境,在本地运行和调试作业。可以利用本地运行模式充分的调试业务功能以及单元测试,完成之后再部署至Mesos集群。 
本地运行作业无需安装Mesos环境。

在《官方文档 —— 本地运行模式》有详细解析。

3.3 云作业配置总结

  • CloudJobConfiguration:生产运行使用。

  • LocalCloudJobConfiguration:本地开发调试。

666. 彩蛋

芋道君:本文主要为《Elastic-Job-Cloud 源码解析 —— 作业调度(一)》做铺垫,这会是一篇长文。读懂 Elastic-Job-Cloud 作业调度后,整个人脑洞又开的不行不行的! 
旁白君:支持+1024。

另外,推荐资料如下,对理解 Elastic-Job-Cloud 很有帮助。

  • 《基于Mesos的当当作业云Elastic Job Cloud》

  • 《如何从0到1搭建弹性作业云Elastic-Job-Cloud》

道友,赶紧上车,分享一波朋友圈!


欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

已在知识星球更新源码解析如下:

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存