一、背景
在我们的项目当中,使用定时任务是避免不了的,我们在部署定时任务时,通常只部署一台机器。当部署多台机器时,同一个任务会执行多次。比如下面这个场景,“短信提醒”,每天定时的给用户下发短信,每个用户只会收到一条短信。如果部署了多台服务,每个服务都会执行这个短信提醒任务,那么每个用户就会收到多条短信,这明显是不可以的;如果只部署一台机器,那么定时任务的高可用是无法满足的,如果这个唯一的服务宕机,那么整个定时任务就挂掉了,这也是不行的。那么今天向大家介绍的这款开源产品就是解决这个问题的,分布式定时任务解决方案——elastic-job。
二、简介
Elastic-Job是一个分布式调度解决方案,由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。在这里我们着重介绍Elastic-Job-Lite。Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供最轻量级的分布式任务的协调服务,外部依赖仅Zookeeper。
- 分片概念
任务的分布式执行,需要将一个任务拆分为多个独立的任务项,然后由分布式的服务器分别执行某一个或几个分片项。
例如:有一个遍历数据库某张表的作业,现有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结尾的数据。
Elastic-Job并不直接提供数据处理的功能,框架只会将分片项分配至各个运行中的作业服务器,开发者需要自行处理分片项与真实数据的对应关系。 - 作业高可用
如果在上述的作业中,如果有一个应用挂掉(总共有2个应用),分片项将会重新分片,剩下的唯一应用将获得分片项0-9。
三、实际应用
这里我们采用大家都比较熟悉的基于spring配置文件的配置。
- 引入jar包
在项目的pom.xml中添加如下配置:
<!-- 引入elastic-job-lite核心模块 -->
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-core</artifactId>
<version>${latest.release.version}</version>
</dependency>
<!-- 使用springframework自定义命名空间时引入 -->
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-spring</artifactId>
<version>${latest.release.version}</version>
</dependency>
- 作业程序
在这里我们只做一个Simple类型的定时任务,代码如下:
@Slf4j
public class MySimpleJob implements SimpleJob {
@Override
public void execute(ShardingContext shardingContext) {
log.info("我是分片项:"+shardingContext.getShardingItem());
log.info("分片总数:"+shardingContext.getShardingTotalCount());
}
}
我们的定时任务要实现SimpleJob接口,并实现execute方法。并使用context.getShardingItem() 可以获得当前的分片项,使用context.getShardingTotalCount()获得总分片数。我们把当前分片项,总分片数传入到sql中,按照某个字段取模,检索出该分片处理的数据,再进行处理。
- spring配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:reg="http://www.dangdang.com/schema/ddframe/reg"
xmlns:job="http://www.dangdang.com/schema/ddframe/job"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.dangdang.com/schema/ddframe/reg
http://www.dangdang.com/schema/ddframe/reg/reg.xsd
http://www.dangdang.com/schema/ddframe/job
http://www.dangdang.com/schema/ddframe/job/job.xsd
">
<!--配置作业注册中心 -->
<reg:zookeeper id="regCenter" server-lists="yourhost:2181" namespace="my-job" base-sleep-time-milliseconds="1000" max-sleep-time-milliseconds="3000" max-retries="3" />
<!-- 配置作业-->
<job:simple id="mySimpleJob" overwrite="true" class="xxx.MySimpleJob" registry-center-ref="regCenter" cron="0/10 * * * * ?" sharding-total-count="10" />
</beans>
注册中心我们采用zookeeper,使用最小的zookeeper集群,3台。在注册中心配置中,server-lists填写3台zk地址,用“,”隔开,格式为:zk1:port1,zk2:port2,zk3:port3。
配置作业中就是我们任务的具体规则,class对应具体的实现类、registry-center-ref对应注册中心zookeeper的id(regCenter)、cron定时任务规则、sharding-total-count总分片数。overwrite=”true”这个配置很重要,因为这些配置都要上传到zk中,当你改变了配置之后,更改后的配置并不会上传到zookeeper中,执行的任务还是旧的。加上overwrite=”true”以后,每次任务重启时,服务中的配置就会覆盖掉zookeeper中的配置,所以以后任务执行时,都会从zookeeper中得到最新的配置。
到这里,我们的分布式定时任务就配置好了,剩下的就是部署了,上面的例子中,我们的总分片数是10,如果我们部署2台机器,每台机器将获得5个分片;如果部署5台机器,每台机器获得2个分片。如果出现宕机情况,分片将重新分配,从而做到高可用。
四、总结
分布式定时任务框架Elastic-Job,不仅解决了服务高可用的问题,而且还支持分布式,每个服务只处理一部分数据,大大减轻了系统的压力。总而言之,这是一款非常不错的分布式定时任务框架。