elastic-job使用及過程中遇到的問題
以前的公司專案分散式排程用的兩種方式,一是通過配置檔案進行分割槽,一是用阿里的一個dtss控制元件;配置檔案的方式就不說了,比較挫;而阿里的dtss控制元件存在問題,而且該控制元件也已經停止維護更新(別的控制元件又要收錢)。
所以找到了這個elastic-job,目前只用到了其中的SimpleJob,後續的再更新。。。
官方文件
使用
簡單介紹:
這個框架的任務分派,簡單來說就是,通過zookeeper知道線上總共有多少臺執行機器,然後把分片平均分到線上的機器上,而每次機器的上線、下線都會觸發分片的重新分配。
而分片其實就是數字,數字將平均分配到幾臺機器上,例如:
設定總分片數為4片(0,1,2,3總共4個分片),線上2臺機器,機器A可能被分配到0、1兩個數字,機器B被分配到2、3兩個數字,然後就可以根據這個被分配到的數字進行任務劃分。
依賴新增:
<!-- 引入elastic-job-lite核心模組 -->
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-core</artifactId>
<version>2.1.5</version>
</dependency>
<!-- 使用springframework自定義名稱空間時引入 -->
<dependency>
<groupId >com.dangdang</groupId>
<artifactId>elastic-job-lite-spring</artifactId>
<version>2.1.5</version>
</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();
}
配置檔案啟動:
當載入該檔案的時候,就啟動其中任務排程
<?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="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>
<!-- 配置關聯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>
注意項
程式碼啟動中,實現類的execute方法中不能使用spring注入的物件:
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);
}
}
需要注意的配置:
overwrite:
預設為false,如果為false的話,第一次啟動的時候,會在zookeeper中儲存了一份作業資訊(排程時間、引數等),後面即使修改了作業資訊,無論重新啟動服務或者zookeeper,還是會使用第一次啟動時候的作業資訊(根據作業名字)。
因此需要設為true,這樣每次啟動,作業資訊都會覆蓋zookeeper中的儲存的配置資訊,這樣可以保證修改了配置資訊可以馬上使用。
分片策略:
根據文件介紹,OdevitySortByNameJobShardingStrategy這個比預設的策略好
啟動的時候可能連不上zookeeper
服務原來就有用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.<init>(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(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框架也用到了,然後版本不一樣導致了依賴衝突。
相關文件
運維平臺安裝部署
1.編譯jar包
就是編譯的時候用的是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 <goals> -rf :elastic-job-lite-lifecycle
看pom檔案中,該專案原來用的是1.7版本的jdk,後面用了1.7的版本但是還是報這個錯,應該小版本不對,後面直接把pom中的javadoc相關的配置注掉後,就可以正常編譯了(1.8也可以):
/* 把下面程式碼注掉
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<aggregate>true</aggregate>
<charset>${project.build.sourceEncoding}</charset>
<encoding>${project.build.sourceEncoding}</encoding>
<docencoding>${project.build.sourceEncoding}</docencoding>
</configuration>
</plugin>
*/
2.啟動
在伺服器上啟動的,需要長期存活,這樣啟動不會退出伺服器就關掉
nohup sh ${PROJECT_NAME}/elastic-job-lite-console-3.0.0.M1-SNAPSHOT/bin/start.sh -p 30001 &