Elastic-Job的使用
Elastic-Job是一个分布式调度解决方案,由两个相互独立的子项目Elastic-Job-Lite和Elastic-Job-Cloud组成。
Elastic-Job-Lite定位为轻量级无中心化解决方案,使用jar包的形式提供分布式任务的协调服务; --摘自官网
具体的详细介绍,大家可以去官网查阅
这篇文章主要是整合springboot 的简单例子。通过一步一步实现,来逐步的熟悉elastic-job 这个组件,首要条件就是需要你有个运行的zookeeper
搭建springboot项目
这里不在赘述如果搭建,这里我只贴出我的pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kevin</groupId>
<artifactId>es-job</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>es-job</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<elastic-job.version>2.1.4</elastic-job.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- elastic-job dependency -->
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-core</artifactId>
<version>${elastic-job.version}</version>
</dependency>
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-spring</artifactId>
<version>${elastic-job.version}</version>
</dependency>
<!-- spring boot dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
主要的就是加载这2个依赖
<!-- elastic-job dependency -->
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-core</artifactId>
<version>${elastic-job.version}</version>
</dependency>
<dependency>
<groupId>com.dangdang</groupId>
<artifactId>elastic-job-lite-spring</artifactId>
<version>${elastic-job.version}</version>
</dependency>
整合elasticjob
- 编写配置application.yml
zookeeper:
address: 192.168.247.7:2181
namespace: elastic-job
connectionTimeout: 10000
sessionTimeout: 10000
maxRetries: 3
# simplejob配置
simpleJob:
cron: 0/5 * * * * ?
shardingTotalCount: 5
shardingItemParameters: 0=java,1=php,2=erlang,3=angular,4=vue
jobParameter: source1=public,source2=private
failover: true
monitorExecution: true
monitorPort: 8889
maxTimeDiffSeconds: -1
jobShardingStrategyClass: com.dangdang.ddframe.job.lite.api.strategy.impl.AverageAllocationJobShardingStrategy
dataflowJob:
cron: 0/10 * * * * ?
shardingTotalCount: 2
shardingItemParameters: 0=jinan,1=qingdao
############################################################
#
# 配置数据源信息
#
############################################################
spring:
datasource: # 数据源的相关配置
type: com.zaxxer.hikari.HikariDataSource # 数据源类型:HikariCP
driver-class-name: com.mysql.jdbc.Driver # mysql驱动
url: jdbc:mysql://localhost:3306/elasticjob?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false
username: root
password: root
hikari:
connection-timeout: 30000 # 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 默认:30秒
minimum-idle: 5 # 最小连接数
maximum-pool-size: 20 # 最大连接数
auto-commit: true # 自动提交
idle-timeout: 600000 # 连接超时的最大时长(毫秒),超时则被释放(retired),默认:10分钟
pool-name: DateSourceHikariCP # 连接池名字
max-lifetime: 1800000 # 连接的生命时长(毫秒),超时而且没被使用则被释放(retired),默认:30分钟 1800000ms
connection-test-query: SELECT 1
- 注册中心加载到spring 容器中
/**
* @author: kevin
* @Date: 2020/1/22
*/
@Configuration
@ConditionalOnExpression("'${zookeeper.address}'.length() > 0")
public class RegistryCenterConfig {
/**
* 把注册中心加载到spring 容器中
* @return
*/
@Bean(initMethod = "init")
public ZookeeperRegistryCenter registryCenter(@Value("${zookeeper.address}") final String serverLists,
@Value("${zookeeper.namespace}") final String namespace,
@Value("${zookeeper.connectionTimeout}") final int connectionTimeout,
@Value("${zookeeper.sessionTimeout}") final int sessionTimeout,
@Value("${zookeeper.maxRetries}") final int maxRetries) {
ZookeeperConfiguration zookeeperConfiguration = new ZookeeperConfiguration(serverLists, namespace);
zookeeperConfiguration.setConnectionTimeoutMilliseconds(connectionTimeout);
zookeeperConfiguration.setSessionTimeoutMilliseconds(sessionTimeout);
zookeeperConfiguration.setMaxRetries(maxRetries);
return new ZookeeperRegistryCenter(zookeeperConfiguration);
}
}
- 配置JobEventConfig事件追踪
/**
* @author: kevin
* @Date: 2020/1/22
*/
@Configuration
public class JobEventConfig {
@Autowired
private DataSource dataSource;
@Bean
public JobEventConfiguration jobEventConfiguration() {
return new JobEventRdbConfiguration(dataSource);
}
}
-
编写自己的job并完成配置
4.1 定义自己的job
/**
* @author: kevin
* @Date: 2020/1/22
*/
@Component
public class MySimpleJob implements SimpleJob {
@Override
public void execute(ShardingContext shardingContext) {
System.out.println(shardingContext.getShardingParameter());
}
}
4.2 编写作业配置
@Configuration
public class MySimpleJobConfig {
@Autowired
private ZookeeperRegistryCenter registryCenter;
@Autowired
private JobEventConfiguration jobEventConfiguration;
/**
* 具体真正的定时任务执行逻辑
* @return
*/
@Bean
public SimpleJob simpleJob() {
return new MySimpleJob();
}
/**
* @param simpleJob
* @return
*/
@Bean(initMethod = "init")
public JobScheduler simpleJobScheduler(final SimpleJob simpleJob,
@Value("${simpleJob.cron}") final String cron,
@Value("${simpleJob.shardingTotalCount}") final int shardingTotalCount,
@Value("${simpleJob.shardingItemParameters}") final String shardingItemParameters,
@Value("${simpleJob.jobParameter}") final String jobParameter,
@Value("${simpleJob.failover}") final boolean failover,
@Value("${simpleJob.monitorExecution}") final boolean monitorExecution,
@Value("${simpleJob.monitorPort}") final int monitorPort,
@Value("${simpleJob.maxTimeDiffSeconds}") final int maxTimeDiffSeconds,
@Value("${simpleJob.jobShardingStrategyClass}") final String jobShardingStrategyClass) {
return new SpringJobScheduler(simpleJob,
registryCenter,
getLiteJobConfiguration(simpleJob.getClass(),
cron,
shardingTotalCount,
shardingItemParameters,
jobParameter,
failover,
monitorExecution,
monitorPort,
maxTimeDiffSeconds,
jobShardingStrategyClass),
jobEventConfiguration,
new SimpleJobListener());
}
private LiteJobConfiguration getLiteJobConfiguration(Class<? extends SimpleJob> jobClass, String cron,
int shardingTotalCount, String shardingItemParameters, String jobParameter, boolean failover,
boolean monitorExecution, int monitorPort, int maxTimeDiffSeconds, String jobShardingStrategyClass) {
//定义作业核心配置
JobCoreConfiguration jobCoreConfiguration = JobCoreConfiguration
.newBuilder(jobClass.getName(), cron, shardingTotalCount)
.misfire(true)
.failover(failover)
.jobParameter(jobParameter)
.shardingItemParameters(shardingItemParameters)
.build();
//定义SIMPLE类型配置
SimpleJobConfiguration simpleJobConfiguration = new SimpleJobConfiguration(jobCoreConfiguration, jobClass.getCanonicalName());
//定义Lite作业根配置
LiteJobConfiguration liteJobConfiguration = LiteJobConfiguration.newBuilder(simpleJobConfiguration)
.jobShardingStrategyClass(jobShardingStrategyClass)
.monitorExecution(monitorExecution)
.monitorPort(monitorPort)
.maxTimeDiffSeconds(maxTimeDiffSeconds)
.overwrite(true)
.build();
return liteJobConfiguration;
}
5.使用自定义注解完成job的注册(适合多job,无需配置多个config类,第四步与第五步均可实现,选择一个即可)
5.1 编写注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ElasticSimpleJob {
String jobName() default "";
String cron() default "";
String shardingTotalCount() default "";
String shardingItemParameters() default "";
boolean overwrite() default false;
Class<? extends JobShardingStrategy> jobStrategy() default AverageAllocationJobShardingStrategy.class;
boolean jobEvent() default false;
Class<? extends ElasticJobListener>[] jobListener() default {};
}
5.2 编写job,并添加刚刚编写的注解
@ElasticSimpleJob(
jobName = "demoJob.jobName",
cron = "demoJob.cron",
overwrite = true,
shardingTotalCount = "demoJob.shardingTotalCount",
jobListener = {DemoJob.DemoJobListener.class}
)
@Slf4j
public class DemoJob implements SimpleJob {
@Override
public void execute(final ShardingContext shardingContext) {
System.out.println("sssssssssssss");
}
protected static class DemoJobListener implements ElasticJobListener {
@Override
public void beforeJobExecuted(final ShardingContexts shardingContexts) {
log.info("demoJob开始了");
}
@Override
public void afterJobExecuted(final ShardingContexts shardingContexts) {
log.info("demoJob结束了");
}
}
}
5.3 编写Job的实体类
@Data
public class SimpleJobEntity {
String jobName;
String cron;
int shardingTotalCount;
String shardingItemParameters;
boolean overwrite;
Class<?> jobStrategy;
boolean isJobEvent;
Class<? extends ElasticJobListener>[] listeners;
ElasticJobListener[] listenerInstances;
}
5.4编写自动配置类(根据注解拿到job的信息完成注册)
@Component
@Slf4j
public class SimpleJobAutoConfig {
@Autowired
private CoordinatorRegistryCenter zkCenter;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private Environment environment;
@Value("${regCenter.namespace}")
private String namespace;
@PostConstruct
public void initSimpleJob() {
log.info("初始化JOB任务中...");
Map<String, Object> beans = applicationContext.getBeansWithAnnotation(ElasticSimpleJob.class);
log.info("发现JOB任务:{}", beans.size());
for (Map.Entry<String, Object> entry : beans.entrySet()) {
Object instance = entry.getValue();
Class<?>[] interfaces = instance.getClass().getInterfaces();
for (Class<?> superInterface : interfaces) {
if (superInterface == SimpleJob.class) {
ElasticSimpleJob annotation = instance.getClass().getAnnotation(ElasticSimpleJob.class);
SimpleJobEntity simpleJobEntity = getSimpleJobEntity(annotation);
if (simpleJobEntity == null) {
log.error("#SimpleJobAutoConfig.initSimpleJob 未发现job配置信息:{}", instance.getClass().getName());
continue;
}
log.info("正在注册JOB任务:{}", simpleJobEntity.getJobName());
// job核心配置
final JobCoreConfiguration jcc;
final JobCoreConfiguration.Builder builder = JobCoreConfiguration.newBuilder(namespace + "." + simpleJobEntity.getJobName(), simpleJobEntity.getCron(), simpleJobEntity.getShardingTotalCount());
if (StringUtils.isNotBlank(simpleJobEntity.getShardingItemParameters())) {
builder.shardingItemParameters(simpleJobEntity.getShardingItemParameters());
}
jcc = builder.build();
// job类型配置
SimpleJobConfiguration simpleJobConfiguration = new SimpleJobConfiguration(jcc,
instance.getClass().getCanonicalName());
// job根的配置
LiteJobConfiguration ljc;
LiteJobConfiguration.Builder liteBuilder = LiteJobConfiguration.newBuilder(simpleJobConfiguration);
if (simpleJobEntity.getJobStrategy() != null) {
liteBuilder.jobShardingStrategyClass(simpleJobEntity.getJobStrategy().getCanonicalName());
}
ljc = liteBuilder.build();
if (simpleJobEntity.isJobEvent()) {
} else {
new SpringJobScheduler((ElasticJob) instance, zkCenter, ljc, simpleJobEntity.getListenerInstances()).init();
log.info("注册JOB任务成功:{}", simpleJobEntity.getJobName());
}
}
}
}
log.info("初始化JOB任务成功");
}
private SimpleJobEntity getSimpleJobEntity(ElasticSimpleJob annotation) {
try {
if (StringUtils.isBlank(annotation.jobName())) {
return null;
}
if (StringUtils.isBlank(annotation.cron())) {
return null;
}
SimpleJobEntity simpleJobEntity = new SimpleJobEntity();
simpleJobEntity.setJobName(environment.getProperty(annotation.jobName()));
simpleJobEntity.setCron(environment.getProperty(annotation.cron()));
simpleJobEntity.setOverwrite(annotation.overwrite());
simpleJobEntity.setJobStrategy(annotation.jobStrategy());
simpleJobEntity.setJobEvent(annotation.jobEvent());
if (annotation.shardingTotalCount() != null) {
simpleJobEntity.setShardingTotalCount(Integer.valueOf(environment.getProperty(annotation.shardingTotalCount())));
} else {
simpleJobEntity.setShardingTotalCount(1);
}
if (StringUtils.isNotBlank(annotation.shardingItemParameters())) {
simpleJobEntity.setShardingItemParameters(environment.getProperty(annotation.shardingItemParameters()));
}
simpleJobEntity.setListenerInstances(getElasticJobListener(annotation));
return simpleJobEntity;
} catch (Exception e) {
log.error("",e);
}
return null;
}
private ElasticJobListener[] getElasticJobListener(ElasticSimpleJob annotation) {
Class<? extends ElasticJobListener>[] listeners = annotation.jobListener();
//监听器
ElasticJobListener[] listenerInstances = null;
if (listeners.length > 0) {
listenerInstances = new ElasticJobListener[listeners.length];
List<ElasticJobListener> listenerInstanceList = new ArrayList<>();
Arrays.asList(listeners).forEach(item -> {
ElasticJobListener listenerInstance = null;
try {
listenerInstance = item.getDeclaredConstructor().newInstance();
listenerInstanceList.add(listenerInstance);
} catch (InstantiationException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
log.error("", e);
}
});
listenerInstanceList.toArray(listenerInstances);
} else {
listenerInstances = new ElasticJobListener[0];
}
return listenerInstances;
}
}
完成后,如有新job加入通过@ElasticSimpleJob添加job的信息(如5.2)即可完成job的注册
运行
这里直接启动后,可以看到控制台打印
java
php
erlang
angular
vue
搭建运维的平台
将elastic-job-lite-console-2.1.5.tar.gz
解压,修改conf文件中的zookeeper地址
文件地址:elastic-job-lite-console-2.1.5.tar.gz
linux启动start.sh
window启动start.bat