+++
date = "2017-11-05"
title = "elastic-job分享"
author:wg
+++
# 概述
Elastic-Job是一个分布式调度解决方案,由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。
Elastic-Job-Cloud使用Mesos + Docker的解决方案,额外提供资源治理、应用分发以及进程隔离等服务。
Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供最轻量级的分布式任务的协调服务,外部依赖仅Zookeeper。
# 功能列表
* 分布式调度协调
* 弹性扩容缩容
* 失效转移
* 错过执行作业重触发
* 作业分片一致性,保证同一分片在分布式环境中仅一个执行实例
* 自诊断并修复分布式不稳定造成的问题
* 支持并行调度
* 支持作业生命周期操作
* 丰富的作业类型
* Spring整合以及命名空间提供
* 运维平台
#整体架构
http://elasticjob.io/docs/elastic-job-lite/img/architecture/elastic_job_lite.png
# 基本概念
## 1. 分片概念
任务的分布式执行,需要将一个任务拆分为多个独立的任务项,然后由分布式的服务器分别执行某一个或几个分片项。
例如:有一个遍历数据库某张表的作业,现有2台服务器。为了快速的执行作业,那么每台服务器应执行作业的50%。
为满足此需求,可将作业分成2片,每台服务器执行1片。作业遍历数据的逻辑应为:服务器A遍历ID以奇数结尾的数据;服务器B遍历ID以偶数结尾的数据。
如果分成10片,则作业遍历数据的逻辑应为:每片分到的分片项应为ID%10,而服务器A被分配到分片项0,1,2,3,4;服务器B被分配到分片项5,6,7,8,9,直接的结果就是服务器A遍历ID以0-4结尾的数据;服务器B遍历ID以5-9结尾的数据。
## 2. 分片项与业务处理解耦
Elastic-Job并不直接提供数据处理的功能,框架只会将分片项分配至各个运行中的作业服务器,开发者需要自行处理分片项与真实数据的对应关系。
## 3. 个性化参数的适用场景
个性化参数即shardingItemParameter,可以和分片项匹配对应关系,用于将分片项的数字转换为更加可读的业务代码。
例如:按照地区水平拆分数据库,数据库A是北京的数据;数据库B是上海的数据;数据库C是广州的数据。
如果仅按照分片项配置,开发者需要了解0表示北京;1表示上海;2表示广州。
合理使用个性化参数可以让代码更可读,如果配置为0=北京,1=上海,2=广州,那么代码中直接使用北京,上海,广州的枚举值即可完成分片项和业务逻辑的对应关系。
# 核心理念
## 1. 分布式调度
Elastic-Job-Lite并无作业调度中心节点,而是基于部署作业框架的程序在到达相应时间点时各自触发调度。
## 2. 作业高可用
Elastic-Job-Lite提供最安全的方式执行作业。将分片总数设置为1,并使用多于1台的服务器执行作业,作业将会以1主n从的方式执行。
一旦执行作业的服务器崩溃,等待执行的服务器将会在下次作业启动时替补执行。开启失效转移功能效果更好,可以保证在本次作业执行时崩溃,备机立即启动替补执行。
## 3. 最大限度利用资源
Elastic-Job-Lite也提供最灵活的方式,最大限度的提高执行作业的吞吐量。将分片项设置为大于服务器的数量,最好是大于服务器倍数的数量,作业将会合理的利用分布式资源,动态的分配分片项。
例如:3台服务器,分成10片,则分片项分配结果为服务器A=0,1,2;服务器B=3,4,5;服务器C=6,7,8,9。
如果服务器C崩溃,则分片项分配结果为服务器A=0,1,2,3,4;服务器B=5,6,7,8,9。在不丢失分片项的情况下,最大限度的利用现有资源提高吞吐量。
# Java Code配置
## a. 注册中心配置
用于注册和协调作业分布式行为的组件,目前仅支持Zookeeper。
### ZookeeperConfiguration属性详细说明
| 属性名 | 类型 | 构造器注入 | 缺省值 | 描述 |
| ------------------------------|:--------|:---------|:-------|:----|
| serverLists | String | 是 | | 连接Zookeeper服务器的列表 包括IP地址和端口号 多个地址用逗号分隔 如: host1:2181,host2:2181 |
| namespace | String | 是 | | Zookeeper的命名空间 |
| baseSleepTimeMilliseconds | int | 否 | 1000 | 等待重试的间隔时间的初始值 单位:毫秒 |
| maxSleepTimeMilliseconds | String | 否 | 3000 | 等待重试的间隔时间的最大值 单位:毫秒 |
| maxRetries | String | 否 | 3 | 最大重试次数 |
| sessionTimeoutMilliseconds | boolean | 否 | 60000 | 会话超时时间 单位:毫秒 |
| connectionTimeoutMilliseconds | boolean | 否 | 15000 | 连接超时时间 单位:毫秒 |
| digest | String | 否 | | 连接Zookeeper的权限令牌 缺省为不需要权限验证 |
## b. 作业配置
作业配置分为3级,分别是JobCoreConfiguration,JobTypeConfiguration和LiteJobConfiguration。LiteJobConfiguration使用JobTypeConfiguration,JobTypeConfiguration使用JobCoreConfiguration,层层嵌套。
JobTypeConfiguration根据不同实现类型分为SimpleJobConfiguration,DataflowJobConfiguration和ScriptJobConfiguration。
### JobCoreConfiguration属性详细说明
| 属性名 | 类型 | 构造器注入 | 缺省值 | 描述 |
| -----------------------|:--------|:---------|:-------|:--------|
| jobName | String | 是 | | 作业名称 |
| cron | String | 是 | | cron表达式,用于控制作业触发时间 |
| shardingTotalCount | int | 是 | | 作业分片总数 |
| shardingItemParameters | String | 否 | | 分片序列号和参数用等号分隔,多个键值对用逗号分隔 分片序列号从0开始,不可大于或等于作业分片总数如:0=a,1=b,2=c |
| jobParameter | String | 否 | | 作业自定义参数 作业自定义参数,可通过传递该参数为作业调度的业务方法传参,用于实现带参数的作业 例:每次获取的数据量、作业实例从数据库读取的主键等 |
| failover | boolean | 否 | false | 是否开启任务执行失效转移,开启表示如果作业在一次任务执行中途宕机,允许将该次未完成的任务在另一作业节点上补偿执行 |
| misfire | boolean | 否 | true | 是否开启错过任务重新执行 |
| description | String | 否 | | 作业描述信息 |
| jobProperties | Enum | 否 | | 配置jobProperties定义的枚举控制Elastic-Job的实现细节 JOB_EXCEPTION_HANDLER用于扩展异常处理类 EXECUTOR_SERVICE_HANDLER用于扩展作业处理线程池类|
### SimpleJobConfiguration属性详细说明
| 属性名 | 类型 | 构造器注入 | 缺省值 | 描述 |
| -----------|:---------------------|:---------|:-------|:--------------------------------|
| coreConfig | JobCoreConfiguration | 是 | | |
| jobClass | String | 是 | | 作业实现类,需实现ElasticJob接口 |
### LiteJobConfiguration属性详细说明
| 属性名 | 类型 | 构造器注入 | 缺省值 | 描述 |
| -------------------------|:---------------------|:---------|:----------------|:-------------------------------------------------------|
| jobConfig | JobTypeConfiguration | 是 | | |
| monitorExecution | boolean | 否 |true | 监控作业运行时状态 每次作业执行时间和间隔时间均非常短的情况,建议不监控作业运行时状态以提升效率。因为是瞬时状态,所以无必要监控。请用户自行增加数据堆积监控。并且不能保证数据重复选取,应在作业中实现幂等性。 每次作业执行时间和间隔时间均较长的情况,建议监控作业运行时状态,可保证数据不会重复选取。 |
| monitorPort | int | 否 |-1 | 作业监控端口 建议配置作业监控端口, 方便开发者dump作业信息。 使用方法: echo "dump" | nc 127.0.0.1 9888 |
| maxTimeDiffSeconds | int | 否 |-1 | 最大允许的本机与注册中心的时间误差秒数 如果时间误差超过配置秒数则作业启动时将抛异常 配置为-1表示不校验时间误差 |
| jobShardingStrategyClass | String | 否 |-1 | 作业分片策略实现类全路径 默认使用平均分配策略 详情参见:[作业分片策略](/02-guide/job-sharding-strategy) |
| reconcileIntervalMinutes | int | 否 |10 | 修复作业服务器不一致状态服务调度间隔时间,配置为小于1的任意值表示不执行修复 单位:分钟 |
| eventTraceRdbDataSource | String | 否 | | 作业事件追踪的数据源Bean引用 |
## 2. Spring命名空间配置
Spring命名空间与Java Code方式配置类似,大部分属性只是将命名方式由驼峰式改为以减号间隔。使用Spring命名空间需在pom.xml文件中添加elastic-job-lite-spring模块的依赖。
```xml
com.dangdang
elastic-job-lite-spring
${latest.release.version}
```
### a. 注册中心配置
#### reg:zookeeper命名空间属性详细说明
| 属性名 | 类型 | 是否必填 | 缺省值 | 描述 |
| ------------------------------- |:-------|:-------|:------|:-----------------------------------------------------------------------|
| id | String | 是 | | 注册中心在Spring容器中的主键 |
| server-lists | String | 是 | | 连接Zookeeper服务器的列表 包括IP地址和端口号 多个地址用逗号分隔 如: host1:2181,host2:2181 |
| namespace | String | 是 | | Zookeeper的命名空间 |
| base-sleep-time-milliseconds | int | 否 | 1000 | 等待重试的间隔时间的初始值 单位:毫秒 |
| max-sleep-time-milliseconds | int | 否 | 3000 | 等待重试的间隔时间的最大值 单位:毫秒|
| max-retries | int | 否 | 3 |最大重试次数 |
| session-timeout-milliseconds | int | 否 | 60000 | 会话超时时间 单位:毫秒 |
| connectionTimeoutMilliseconds | boolean | 否 | 15000 | 连接超时时间 单位:毫秒 |
| digest | String | 否 | | 连接Zookeeper的权限令牌 缺省为不需要权限验证 |
### b. 作业配置
#### job:simple命名空间属性详细说明
| 属性名 | 类型 | 是否必填 | 缺省值 | 描述 |
| ------------------------------------|:--------|:--------|:----------------|:---------------------------------------------------------------------------|
| id | String | 是 | | 作业名称 |
| class | String | 否 | | 作业实现类,需实现ElasticJob接口 |
| job-ref | String | 否 | | 作业关联的beanId,该配置优先级大于class属性配置 |
| registry-center-ref | String | 是 | | 注册中心Bean的引用,需引用reg:zookeeper的声明 |
| cron | String | 是 | | cron表达式,用于控制作业触发时间 |
| sharding-total-count | int | 是 | | 作业分片总数 |
| sharding-item-parameters | String | 否 | | 分片序列号和参数用等号分隔,多个键值对用逗号分隔 分片序列号从0开始,不可大于或等于作业分片总数 如:
0=a,1=b,2=c|
| job-instance-id | String | 否 | defaultInstance | 作业实例主键,同IP可运行实例主键不同, 但名称相同的多个作业实例 |
| job-parameter | String | 否 | | 作业自定义参数 作业自定义参数,可通过传递该参数为作业调度的业务方法传参,用于实现带参数的作业 例:每次获取的数据量、作业实例从数据库读取的主键等 |
| monitor-execution | boolean | 否 | true | 监控作业运行时状态 每次作业执行时间和间隔时间均非常短的情况,建议不监控作业运行时状态以提升效率。因为是瞬时状态,所以无必要监控。请用户自行增加数据堆积监控。并且不能保证数据重复选取,应在作业中实现幂等性。 每次作业执行时间和间隔时间均较长的情况,建议监控作业运行时状态,可保证数据不会重复选取。|
| monitor-port | int | 否 | -1 | 作业监控端口 建议配置作业监控端口, 方便开发者dump作业信息。 使用方法: echo "dump" | nc 127.0.0.1 9888|
| max-time-diff-seconds | int | 否 | -1 | 最大允许的本机与注册中心的时间误差秒数 如果时间误差超过配置秒数则作业启动时将抛异常 配置为-1表示不校验时间误差|
| failover | boolean | 否 | false | 是否开启失效转移
| misfire | boolean | 否 | true | 是否开启错过任务重新执行 |
| job-sharding-strategy-class | String | 否 | | 作业分片策略实现类全路径 默认使用平均分配策略 详情参见:[作业分片策略](/02-guide/job-sharding-strategy)|
| description | String | 否 | | 作业描述信息 |
| disabled | boolean | 否 | false | 作业是否禁止启动 可用于部署作业时,先禁止启动,部署结束后统一启动 |
| overwrite | boolean | 否 | false | 本地配置是否可覆盖注册中心配置 如果可覆盖,每次启动作业都以本地配置为准 |
| job-exception-handler | String | 否 | | 扩展异常处理类 |
| executor-service-handler | String | 否 | | 扩展作业处理线程池类 |
| reconcile-interval-minutes | int | 否 | 10 | 修复作业服务器不一致状态服务调度间隔时间,配置为小于1的任意值表示不执行修复 单位:分钟 |
| event-trace-rdb-data-source | String | 否 | | 作业事件追踪的数据源Bean引用 |
# 通过代码配置开启事件追踪
Elastic-Job-Lite在配置中提供了JobEventConfiguration,目前支持数据库方式配置。
```java
// 初始化数据源
DataSource dataSource = ...;
// 定义日志数据库事件溯源配置
JobEventConfiguration jobEventRdbConfig = new JobEventRdbConfiguration(dataSource);
// 初始化注册中心
CoordinatorRegistryCenter regCenter = ...;
// 初始化作业配置
LiteJobConfiguration liteJobConfig = ...;
new JobScheduler(regCenter, liteJobConfig, jobEventRdbConfig).init();
```
事件追踪的event_trace_rdb_url属性对应库自动创建JOB_EXECUTION_LOG和JOB_STATUS_TRACE_LOG两张表以及若干索引。
JOB_EXECUTION_LOG字段含义
| 字段名称 | 字段类型 | 是否必填 | 描述 |
| ----------------- |:------------|:--------|:------------------------------------------------------------------------------------------------|
|id |VARCHAR(40) |是 | 主键 |
|job_name |VARCHAR(100) |是 | 作业名称 |
|task_id |VARCHAR(1000)|是 | 任务名称,每次作业运行生成新任务 |
|hostname |VARCHAR(255) |是 | 主机名称 |
|ip |VARCHAR(50) |是 | 主机IP |
|sharding_item |INT |是 | 分片项 |
|execution_source |VARCHAR(20) |是 | 作业执行来源。可选值为NORMAL_TRIGGER, MISFIRE, FAILOVER |
|failure_cause |VARCHAR(2000)|否 | 执行失败原因 |
|is_success |BIT |是 | 是否执行成功 |
|start_time |TIMESTAMP |是 | 作业开始执行时间 |
|complete_time |TIMESTAMP |否 | 作业结束执行时间 |
JOB_EXECUTION_LOG记录每次作业的执行历史。分为两个步骤:
1. 作业开始执行时向数据库插入数据,除failure_cause和complete_time外的其他字段均不为空。
2. 作业完成执行时向数据库更新数据,更新is_success, complete_time和failure_cause(如果作业执行失败)。
***
JOB_STATUS_TRACE_LOG字段含义
| 字段名称 | 字段类型 | 是否必填 | 描述 |
| ----------------- |:------------|:--------|:---------------------------------------------------------------------------- |
|id |VARCHAR(40) |是 | 主键 |
|job_name |VARCHAR(100) |是 | 作业名称 |
|original_task_id |VARCHAR(1000)|是 | 原任务名称 |
|task_id |VARCHAR(1000)|是 | 任务名称 |
|slave_id |VARCHAR(1000)|是 | 执行作业服务器的名称,Lite版本为服务器的IP地址,Cloud版本为Mesos执行机主键 |
|source |VARCHAR(50) |是 | 任务执行源,可选值为CLOUD_SCHEDULER, CLOUD_EXECUTOR, LITE_EXECUTOR |
|execution_type |VARCHAR(20) |是 | 任务执行类型,可选值为NORMAL_TRIGGER, MISFIRE, FAILOVER |
|sharding_item |VARCHAR(255) |是 | 分片项集合,多个分片项以逗号分隔 |
|state |VARCHAR(20) |是 | 任务执行状态,可选值为TASK_STAGING, TASK_RUNNING, TASK_FINISHED, TASK_KILLED, TASK_LOST, TASK_FAILED, TASK_ERROR|
|message |VARCHAR(2000)|是 | 相关信息 |
|creation_time |TIMESTAMP |是 | 记录创建时间 |
JOB_STATUS_TRACE_LOG记录作业状态变更痕迹表。可通过每次作业运行的task_id查询作业状态变化的生命周期和运行轨迹。
# 接入elastic-job 需要注意的问题
##1、jar包冲突
com.google.guava
guava
23.0
暴力解决,直接干掉这个依赖,可能还有更好的解决办法
##2、elastic-job除了依赖zookeeper,还依赖连接zookeeper的客户端curator-client,4.0.0版本足够使用
##3、接入elastic-job建议使用xml方式接入
##4、命名空间与jobClass问题:同一命名空间下jobclass一样会冲突,建议按照项目区别
##5、elastic-Job-console 版本问题,如果需要本地运行运维平台,现在最新的版本是2.1.5 但是并没有同步上传中央仓库,最新版本只有1.1.1 如果运行会出现问题