以前的公司项目分布式调度用的两种方式,一是通过配置文件进行分区,一是用阿里的一个dtss控件;配置文件的方式就不说了,比较挫;而阿里的dtss控件存在问题,而且该控件也已经停止维护更新(别的控件又要收钱)。
所以找到了这个elastic-job,目前只用到了其中的SimpleJob,后续的再更新。。。
官网地址:http://elasticjob.io/index_zh.html
官网文档:http://elasticjob.io/docs/elastic-job-lite/00-overview/
这个框架的任务分派,简单来说就是,通过zookeeper知道线上总共有多少台执行机器,然后把分片平均分到线上的机器上,而每次机器的上线、下线都会触发分片的重新分配。
而分片其实就是数字,数字将平均分配到几台机器上,例如:
设置总分片数为4片(0,1,2,3总共4个分片),线上2台机器,机器A可能被分配到0、1两个数字,机器B被分配到2、3两个数字,然后就可以根据这个被分配到的数字进行任务划分。
<dependency>
<groupId>com.dangdanggroupId>
<artifactId>elastic-job-lite-coreartifactId>
<version>2.1.5version>
dependency>
<dependency>
<groupId>com.dangdanggroupId>
<artifactId>elastic-job-lite-springartifactId>
<version>2.1.5version>
dependency>
public class TestJob implements SimpleJob{
private Logger logger = LoggerFactory.getLogger(TestJob.class);
@Override
public void execute(ShardingContext shardingContext) {
// 分片数字以及作业的一些信息都在ShardingContext这个类中
logger.info(...);
}
}
有两种启动方式:代码启动和配置文件加载启动,
// 启动方法
public void init(){
new JobScheduler(getRegistryCenter(), getJobConfiguration()).init();
}
// 配置zookeeper连接信息,以及进行连接
public CoordinatorRegistryCenter getRegistryCenter() {
// 连接Zookeeper服务器的列表,包括IP地址和端口号,多个地址用逗号分隔,如: host1:2181,host2:2181
// Zookeeper的命名空间
CoordinatorRegistryCenter regCenter = new ZookeeperRegistryCenter(new ZookeeperConfiguration(zookeeperIps, zookeeperNamespace));
regCenter.init();
return regCenter;
}
// 配置job的信息
public LiteJobConfiguration getJobConfiguration(){
// 定义作业核心配置
JobCoreConfiguration simpleCoreConfig = getJobCoreConfiguration();
// 定义SIMPLE类型配置
SimpleJobConfiguration simpleJobConfig = getSimpleJobConfiguration(simpleCoreConfig);
// 定义Lite作业根配置
LiteJobConfiguration simpleJobRootConfig = getLiteJobConfiguration(simpleJobConfig);
return simpleJobRootConfig;
}
// 定义作业核心配置
public JobCoreConfiguration getJobCoreConfiguration(){
// 分片总数
int shardingTotalCount = 10;
return JobCoreConfiguration.newBuilder("任务名称", "cron表达式", shardingTotalCount ).jobParameter("作业参数").shardingItemParameters("分片参数").build();
}
// 定义SIMPLE类型配置
public SimpleJobConfiguration getSimpleJobConfiguration(JobCoreConfiguration simpleCoreConfig){
// job实现类名字需要写全名
return new SimpleJobConfiguration(simpleCoreConfig, SimpleJob.getClass().getCanonicalName());
}
// 定义Lite作业根配置
public LiteJobConfiguration getLiteJobConfiguration(SimpleJobConfiguration simpleJobConfig){
// overwrite:本地配置是否可覆盖注册中心配置
// jobShardingStrategyClass:分片策略
return LiteJobConfiguration.newBuilder(simpleJobConfig).overwrite(isOverwrite).jobShardingStrategyClass(OdevitySortByNameJobShardingStrategy.class.getCanonicalName()).build();
}
当加载该文件的时候,就启动其中任务调度
<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="1.1.1.1:2181,2.2.2.2:2181"
namespace="Zookeeper的命名空间"
base-sleep-time-milliseconds="1000"
max-sleep-time-milliseconds="3000"
max-retries="3"
/>
// 实现类,需要实现指定接口
<bean id="simpleJob" class="xxx.SimpleJob">
bean>
<job:simple id="simpleRefElasticJob"
overwrite="true"
job-parameter="2"
sharding-item-parameters="0=A,1=B"
job-ref="simpleJob"
registry-center-ref="regCenter"
cron="0/59 * * * * ?"
sharding-total-count="64"
job-sharding-strategy-class="com.dangdang.ddframe.job.lite.api.strategy.impl.OdevitySortByNameJobShardingStrategy"
/>
beans>
elastic-job是封装的quartz框架,这个特性也存留下来,execute方法中只能用static对象
@Service
public class TestJob implements SimpleJob{
private Logger logger = LoggerFactory.getLogger(TestJob.class);
@Autowired
private TestService testService;
private static TestService staticTestService;
// 启动前,需要调用该方法初始化static对象
public void init(){
staticTestService = testService;
}
@Override
public void execute(ShardingContext shardingContext) {
// 分片数字以及作业的一些信息都在ShardingContext这个类中
logger.info(...);
// 日志输出为:true
logger.info(testService == null);
// 日志输出为:false
logger.info(staticTestService == null);
}
}
详细的配置信息可以看官方文档:http://elasticjob.io/docs/elastic-job-lite/02-guide/config-manual/
默认为false,如果为false的话,第一次启动的时候,会在zookeeper中保存了一份作业信息(调度时间、参数等),后面即使修改了作业信息,无论重新启动服务或者zookeeper,还是会使用第一次启动时候的作业信息(根据作业名字)。
因此需要设为true,这样每次启动,作业信息都会覆盖zookeeper中的保存的配置信息,这样可以保证修改了配置信息可以马上使用。
根据文档介绍,OdevitySortByNameJobShardingStrategy这个比默认的策略好
服务原来就有用dubbo,dubbo这边每次都可以连上zookeeper,但是elastic-job经常出现启动的时候连不上zookeeper的,需要启多几次,也可以把zookeeper的最大重连次数(max-retries)设置大点
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.dangdang.ddframe.job.lite.spring.api.SpringJobScheduler#0': Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.apache.curator.framework.api.CreateBuilder.creatingParentsIfNeeded()Lorg/apache/curator/framework/api/ProtectACLCreateModePathAndBytesable;
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:93)
at crm.app.logistics.LogisticsAppMain.main(LogisticsAppMain.java:44)
Caused by: java.lang.NoSuchMethodError: org.apache.curator.framework.api.CreateBuilder.creatingParentsIfNeeded()Lorg/apache/curator/framework/api/ProtectACLCreateModePathAndBytesable;
at com.dangdang.ddframe.job.reg.zookeeper.ZookeeperRegistryCenter.persist(ZookeeperRegistryCenter.java:218)
at com.dangdang.ddframe.job.lite.internal.storage.JobNodeStorage.replaceJobNode(JobNodeStorage.java:160)
at com.dangdang.ddframe.job.lite.internal.config.ConfigurationService.persist(ConfigurationService.java:72)
at com.dangdang.ddframe.job.lite.internal.schedule.SchedulerFacade.updateJobConfiguration(SchedulerFacade.java:103)
at com.dangdang.ddframe.job.lite.api.JobScheduler.init(JobScheduler.java:105)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1758)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1695)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 12 more
后面经过排除,发现是依赖冲突的问题,原来项目中有用到阿里的dtss控件,里面有依赖到一个jar包:org.apache.curator,elastic-job框架也用到了,然后版本不一样导致了依赖冲突。
官方文档对于很多机制写得并不详细,下面列出些别的大牛的文章来补充:
源码分析ElasticJob任务错过机制(misfire)与幂等性:https://blog.csdn.net/prestigeding/article/details/80140777
官方文档中关于运维平台的介绍:http://elasticjob.io/docs/elastic-job-lite/02-guide/web-console/
就是编译的时候用的是1.8的jdk编译的时候,报了异常:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-javadoc-plugin:2.10.3:jar (attach-javadocs) on project elastic-job-lite-lifecycle: MavenReportException: Error while generating Javadoc:
[ERROR] Exit code: 1 - ????: ???E:\maven-repository\org\codehaus\jettison\jettison\1.1\jettison-1.1.jar?????; error in opening zip file
[ERROR]
[ERROR] Command line was: D:\java\jdk1.8.0_181\jre\..\bin\javadoc.exe @options @packages
[ERROR]
[ERROR] Refer to the generated Javadoc files in 'E:\zwh\elastic-job-lite-dev\elastic-job-lite-lifecycle\target\apidocs' dir.
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
[ERROR]
[ERROR] After correcting the problems, you can resume the build with the command
[ERROR] mvn -rf :elastic-job-lite-lifecycle
看pom文件中,该项目原来用的是1.7版本的jdk,后面用了1.7的版本但是还是报这个错,应该小版本不对,后面直接把pom中的javadoc相关的配置注掉后,就可以正常编译了(1.8也可以):
/* 把下面代码注掉
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-javadoc-pluginartifactId>
<executions>
<execution>
<id>attach-javadocsid>
<goals>
<goal>jargoal>
goals>
execution>
executions>
<configuration>
<aggregate>trueaggregate>
<charset>${project.build.sourceEncoding}charset>
<encoding>${project.build.sourceEncoding}encoding>
<docencoding>${project.build.sourceEncoding}docencoding>
configuration>
plugin>
*/
在服务器上启动的,需要长期存活,这样启动不会退出服务器就关掉
nohup sh ${PROJECT_NAME}/elastic-job-lite-console-3.0.0.M1-SNAPSHOT/bin/start.sh -p 30001 &