原文链接:https://www.2cto.com/kf/201612/577514.html
elastic-job部署以及简单例子:elastic-job是当当开发的基于qutarz以及zookeeper封装的作业调度工具,主要有两个大框架,一个是elastic-job lite另外一个是elastic-job cloud,其中qutarz是一个开源的作业调度工具,zookeeper是分布式调度工具,这两者结合搭建了elastic-job-lite,这是一个无中心节点的调度,而elastic-job-cloud是一个有中心节点的分布式调度开源工具,只需要设置好机器以及分片,就可以自动的调度到对应的机器上运行。
与lite的不同时cloud采用了mesos来进行分布式资源管理,简单的来说两者的不同是:同一个作业在两台机器上跑,lite需要手动在两台机器上跑,但是cloud只需要上传作业包,就可以自动的在两台机器上跑,因为lite不支持作业的调度,为无中心的。
二、环境的搭建
由于elastic-job-cloud的环境暂时未搭建出来,因此在此简单介绍lite的搭建
(1)jdk的安装
jdk需要1.7以上,因为里面有spring相关的代码,具体的安装请自行百度,或参考链接https://blog.csdn.net/molong1208/article/details/50537898
(2)zookeeper的安装
具体的安装过程见链接https://blog.csdn.net/molong1208/article/details/53675063
(3)maven的安装
官网maven要求3.0.4以及以上,具体的安装过程与jdk类似,请自行百度
三、elastic-job-lite的优势及特点
(1)简单的概念及适用场景
1. 分片概念
任务的分布式执行,需要将一个任务拆分为n个独立的任务项,然后由分布式的服务器分别执行某一个或几个分片项。
例如:有一个遍历数据库某张表的作业,现有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=广州,那么代码中直接使用北京,上海,广州的枚举值即可完成分片项和业务逻辑的对应关系。
(2)elastic-job-lite优势及特点
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。在不丢失分片项的情况下,最大限度的利用现有资源提高吞吐量。
三、简单的例子
elastic-job的作业类型分为三种,一种是简单的simple的形式,一种是基于流式数据的处理,一种是基于脚本的调度,因为本人所使用的情况是基于流式的处理,那么就简单搭了一个基于流式的demo,其他类型的类似
流式作业的方式适合于不间断的数据处理的类型,例如需要拉取订单数据,因为订单是连续不间断的,因此需要一直拉取。
按照elastic-job官网上介绍,搭建一个基于dataflow(流式处理)的demo,这个demo的功能就是,从一个数据中心里面取数据,按照数据中心的数据id%分片个数==分片参数进行拉取数据,拉取完成后将对应的数据id置为完成的状态,具体代码如下所示:
(1)入口函数main函数以及作业的配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
package
ElasticJobExample.ElasticJobExample;
import
com.dangdang.ddframe.job.config.JobCoreConfiguration;
import
com.dangdang.ddframe.job.config.dataflow.DataflowJobConfiguration;
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;
/**
* Hello world!
*
*/
public
class
App
{
public
static
void
main(String[] args) {
new
JobScheduler(createRegistryCenter(), createJobConfiguration()).init();
}
private
static
CoordinatorRegistryCenter createRegistryCenter() {
CoordinatorRegistryCenter regCenter =
new
ZookeeperRegistryCenter(
new
ZookeeperConfiguration(
"ip:2181"
,
"elastic-job-demo"
));
regCenter.init();
return
regCenter;
}
private
static
LiteJobConfiguration createJobConfiguration() {
// 创建作业配置
JobCoreConfiguration coreConfig = JobCoreConfiguration.newBuilder(
"myDataFlowTest"
,
"0/10 * * * * ?"
,
3
).shardingItemParameters(
"0=0,1=1,2=2"
).build();
DataflowJobConfiguration dataflowJobConfig =
new
DataflowJobConfiguration(coreConfig, JavaDataflowJob.
class
.getCanonicalName(),
true
);
LiteJobConfiguration result = LiteJobConfiguration.newBuilder(dataflowJobConfig).build();
return
result;
}
}
|
(2)作业的逻辑处理部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package
ElasticJobExample.ElasticJobExample;
import
java.util.ArrayList;
import
java.util.Date;
import
java.util.List;
import
com.dangdang.ddframe.job.api.ShardingContext;
import
com.dangdang.ddframe.job.api.dataflow.DataflowJob;
import
dataflowjob.entity.Foo;
import
dataflowjob.process.DataProcess;
import
dataflowjob.process.DataProcessFactory;
public
class
JavaDataflowJob
implements
DataflowJob
private
DataProcess dataProcess = DataProcessFactory.getDataProcess();
@Override
public
List
List
new
ArrayList
result = dataProcess.getData(context.getShardingParameter(), context.getShardingTotalCount());
System.out.println(String.format(
"------Thread ID: %s, Date: %s, Sharding Context: %s, Action: %s, Data: %s"
, Thread.currentThread().getId(),
new
Date(), context,
"fetch data"
,result));
return
result;
}
@Override
public
void
processData(ShardingContext shardingContext, List
System.out.println(String.format(
"------Thread ID: %s, Date: %s, Sharding Context: %s, Action: %s, Data: %s"
, Thread.currentThread().getId(),
new
Date(), shardingContext,
"finish data"
,data));
for
(Foo foo:data){
dataProcess.setData(foo.getId());
}
}
}
|
(3)具体的处理类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package
dataflowjob.process;
import
java.util.ArrayList;
import
java.util.List;
import
java.util.Map;
import
java.util.concurrent.ConcurrentHashMap;
import
dataflowjob.entity.Foo;
public
class
DataProcess {
private
Map
public
DataProcess()
{
for
(
int
i=
0
;i<
30
;i++){
data.put(i,
new
Foo(i,Foo.Status.TODO));
}
}
public
List
int
shardNum)
{
int
intId = Integer.parseInt(tailId);
List
new
ArrayList
for
(Map.Entry
Foo foo = each.getValue();
int
key = each.getKey();
if
(key % shardNum == intId && foo.getStatus() == Foo.Status.TODO) {
result.add(foo);
}
}
return
result;
}
public
void
setData(
int
i){
data.get(i).setStatus(Foo.Status.DONE);
}
}
|
(4)entity类Foo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package
dataflowjob.entity;
public
class
Foo {
private
int
id;
private
Status status;
public
Foo(
final
int
id,
final
Status status) {
this
.id = id;
this
.status = status;
}
public
int
getId() {
return
id;
}
public
void
setId(
int
id) {
this
.id = id;
}
public
Status getStatus() {
return
status;
}
public
void
setStatus(Status status) {
this
.status = status;
}
public
enum
Status{
TODO,
DONE
}
}
|
(5)具体处理工厂类
1
2
3
4
5
6
7
8
9
10
11
|
package
dataflowjob.process;
public
class
DataProcessFactory {
private
static
DataProcess dataProcess =
new
DataProcess();
public
static
DataProcess getDataProcess() {
return
dataProcess;
}
}
|