Elastic-Job:动态添加任务,支持动态分片
多情只有春庭月,犹为离人照落花。
概述
因项目中使用到定时任务,且服务部署多实例,因此需要解决定时任务重复执行的问题。即在同一时间点,每一个定时任务只在一个节点上执行。常见的开源方案,如 elastic-job
、 xxl-job
、quartz
、 saturn
、 opencron
、 antares
等。最终决定使用elastic-job
。elastic-job
的亮点主要如下:
- 基于quartz 定时任务框架为基础的,因此具备quartz的大部分功能
- 使用zookeeper做协调,调度中心,更加轻量级
- 支持任务的分片
- 支持弹性扩容 , 可以水平扩展 , 当任务再次运行时,会检查当前的服务器数量,重新分片,分片结束之后才会继续执行任务
- 失效转移,容错处理,当一台调度服务器宕机或者跟zookeeper断开连接之后,会立即停止作业,然后再去寻找其他空闲的调度服务器,来运行剩余的任务
- 提供运维界面,可以管理作业和注册中心
但在实际开发中发现elastic-job
对于动态添加的定时任务不支持分片。即在多实例情况下,在某个实例上动态添加任务,则该任务会一直在这一台节点上运行。如果需要在其它实例上运行,则需要以相同的参数调用其它实例接口。参考:elastic-job:动态进行任务的添加。在多次百度+google
下发现Elastic-Job动态添加任务这里与楼主遇到了相同的问题。但经楼主测试动态添加任务的分片时好时坏,且只要在zookeeper
中注册了任务,重启时任务还是会自动初始化。(关于对动态呢任务的描述,可以参考上面链接的描述,此处不在做过多的解释)。
解决
顺着尹大的思路,将任务的节点都集中管理起来,无论动态任务在哪个节点上进行注册,都需要将这个请求转发到其他的节点上进行初始化操作,这样就可以保证多节点分片的任务正常执行。
代码如下:
/**
* 开启任务监听,当有任务添加时,监听zk中的数据增加,自动在其他节点也初始化该任务
*/
public void monitorJobRegister() {
CuratorFramework client = zookeeperRegistryCenter.getClient();
@SuppressWarnings("resource")
PathChildrenCache childrenCache = new PathChildrenCache(client, "/", true);
PathChildrenCacheListener childrenCacheListener = new PathChildrenCacheListener() {
public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
ChildData data = event.getData();
switch (event.getType()) {
case CHILD_ADDED:
String config = new String(client.getData().forPath(data.getPath() + "/config"));
Job job = JsonUtils.toBean(Job.class, config);
Object bean = null;
// 获取bean失败则添加任务
try {
bean = ctx.getBean("SpringJobScheduler" + job.getJobName());
} catch (BeansException e) {
logger.error("ERROR NO BEAN,CREATE BEAN SpringJobScheduler" + job.getJobName());
}
if (Objects.isNull(bean)) {
addJob(job);
}
break;
default:
break;
}
}
};
childrenCache.getListenable().addListener(childrenCacheListener);
try {
// https://blog.csdn.net/u010402202/article/details/79581575
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
} catch (Exception e) {
e.printStackTrace();
}
}
测试
测试动态添加定时任务,支持分片失效转移。
- 下载elastic-job-spring-boot-starter 使用
maven
命令install
到本地 - 创建
demo-elastic-job
项目
目录结构如下:
demo-elastic-job
├── mvnw
├── mvnw.cmd
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── demo
│ │ │ ├── job
│ │ │ │ ├── DynamicJob.java
│ │ │ │ └── TestJob.java
│ │ │ └── DemoApplication.java
│ │ └── resources
│ │ ├── application.yml
│ │ └── application-dev.yml
│ └── test
│ └── java
│ └── com
│ └── example
│ └── demo
│ └── DemoApplicationTests.java
├── pom.xml
└── demo.iml
pom.xml
<?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 http://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.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>2.10.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.cxytiandi</groupId>
<artifactId>elastic-job-spring-boot-starter</artifactId>
<version>1.0.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
DemoApplication.java
package com.example.demo;
import com.cxytiandi.elasticjob.annotation.EnableElasticJob;
import com.cxytiandi.elasticjob.dynamic.service.JobService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableElasticJob
@ComponentScan(basePackages = {"com.cxytiandi", "com.example.demo"})
public class DemoApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Autowired
private JobService jobService;
@Override
public void run(String... args) throws Exception {
// 模拟初始化读取数据库 添加任务
// Job job1 = new Job();
// job1.setJobName("job1");
// job1.setCron("0/10 * * * * ? ");
// job1.setJobType("SIMPLE");
// job1.setJobClass("com.example.demo.job.DynamicJob");
// job1.setShardingItemParameters("");
// job1.setShardingTotalCount(2);
// jobService.addJob(job1);
// Job job2 = new Job();
// job2.setJobName("job2");
// job2.setCron("0/10 * * * * ? ");
// job2.setJobType("SIMPLE");
// job2.setJobClass("com.example.demo.job.DynamicJob");
// job2.setShardingItemParameters("0=A,1=B");
// job2.setShardingTotalCount(2);
// jobService.addJob(job2);
}
}
TestJob.java
package com.example.demo.job;
import com.cxytiandi.elasticjob.annotation.ElasticJobConf;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
/**
* Created by zhenglongfei on 2019/7/22
*
* @VERSION 1.0
*/
@Component
@Slf4j
@ElasticJobConf(name = "dayJob", cron = "0/10 * * * * ?", shardingTotalCount = 2,
shardingItemParameters = "0=AAAA,1=BBBB", description = "简单任务", failover = true)
public class TestJob implements SimpleJob {
@Override
public void execute(ShardingContext shardingContext) {
log.info("TestJob任务名:【{}】, 片数:【{}】, param=【{}】", shardingContext.getJobName(), shardingContext.getShardingTotalCount(),
shardingContext.getShardingParameter());
}
}
DynamicJob.java
package com.example.demo.job;
import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* Created by zhenglongfei on 2019/7/24
*
* @VERSION 1.0
*/
@Component
@Slf4j
public class DynamicJob implements SimpleJob {
@Override
public void execute(ShardingContext shardingContext) {
switch (shardingContext.getShardingItem()) {
case 0:
log.info("【0】 is running");
break;
case 1:
log.info("【1】 is running");
break;
}
}
}
application.yml
elastic:
job:
zk:
serverLists: 172.25.66.137:2181
namespace: demo_test
server:
port: 8082
spring:
redis:
host: 127.0.0.1
port: 6379
测试结果
启动两个项目分别为8081
和8082
端口,使用REST API
来动态的注册任务。
- job
http://localhost:8081/job post
参数如下:
{
"jobName": "DynamicJob01",
"cron": "0/3 * * * * ?",
"jobType": "SIMPLE",
"jobClass": "com.example.demo.job.DynamicJob",
"jobParameter": "test",
"shardingTotalCount": 2,
"shardingItemParameters": "0=AAAA,1=BBBB"
}
代码下载
- github:demo-elastic-job
- github:elastic-job-spring-boot-starter
Elastic-Job:动态添加任务,支持动态分片的更多相关文章
- redis怎么动态添加内存,动态配置,无需重启。
在redis的使用过程中,有时候需要急需修改redis的配置,比如在业务运行的情况下,内存不够怎么办,这时要么赶紧删除无用的内存,要么扩展内存.如果有无用的内容可删除那么所有问题都已经解决.如果内容都 ...
- 动态添加布局、动态添加View、LinearLayout动态添加View;
LinearLayout提供了几个方法,用作动态添加View特别好用: 可以添加View.删除View.删除指定位置View.删除全部View: 看代码: public class MainActiv ...
- redis动态添加内存,动态配置,无需重启
在redis的使用过程中,有时候需要急需修改redis的配置,比如在业务运行的情况下,内存不够怎么办,这时要么赶紧删除无用的内存,要么扩展内存.如果有无用的内容可删除那么所有问题都已经解决.如果内容都 ...
- C# 动态添加类、动态添加类型、代码添加类型
引用控件: DLL下载地址:http://pan.baidu.com/s/1nv2GUWL public class TypeCreator { public static Type Creator( ...
- Spring实现动态数据源,支持动态加入、删除和设置权重及读写分离
当项目慢慢变大,訪问量也慢慢变大的时候.就难免的要使用多个数据源和设置读写分离了. 在开题之前先说明下,由于项目多是使用Spring,因此下面说到某些操作可能会依赖于Spring. 在我经历过的项目中 ...
- vue行内动态添加样式或者动态添加类名
还是记录一下吧(๑•ᴗ•๑) <li :style="{backgroundImage:`url(${item.pic})`}" @click="chooseVip ...
- vue动态添加路由,跳转页面时,页面报错路由重复:vue-router.esm.js?8c4f:16 [vue-router] Duplicate named routes definition: { name: "Login", path: "/login" }
之前用了一个vue-element-admin做了一个小项目,里面用到了动态添加路由,动态展示侧边栏, 当我切换页面时,控制台总是警告提示路由重复,连续跳转几次页面后,控制台就被这些警告占满了, 于是 ...
- svg web拓扑更新了,支持动态添加svg组件
版本1.0请点此 预览地址 https://svg.yaolunmao.top 如何使用 # 克隆项目 git clone https://github.com/yaolunmao/vue-webto ...
- Lua中如何实现类似gdb的断点调试—09支持动态添加和删除断点
前面已经支持了几种不同的方式添加断点,但是必须事先在代码中添加断点,在使用上不是那么灵活方便.本文将支持动态增删断点,只需要开一开始引入调试库即可,后续可以在调试过程中动态的添加和删除断点.事不宜迟, ...
随机推荐
- 最小环-Floyd
floyd求最小环 在Floyd的同时,顺便算出最小环. Floyd算法 :k<=n:k++) { :i<k:i++) :j<k:j++) if(d[i][j]+m[i][k]+m[ ...
- socket基于TCP(粘包现象和处理)
目录 6socket套接字 7基于TCP协议的socket简单的网络通信 AF_UNIX AF_INET(应用最广泛的一个) 报错类型 单一 链接+循环通信 远程命令 9.tcp 实例:远程执行命令 ...
- C#使用NLOG System.TypeInitializationException,类型初始值设定项引发异常
C#如何使用NLOG,网上有很多介绍,本次使用时遇到一个问题,使用NLOG写日志时,出现初始化异常,基本异常信息如下: System.AggregateException: 发生一个或多个错误. -- ...
- NDK Cmake
CMake与NDK搭配使用时,可以配置的部分变量: 1. `ANDROID_PLATFORM`:指定Android的目标版本,对应`$NDK/platforms/`目录下的版本.通常情况下是`defa ...
- Java虚拟机详解(八)------虚拟机监控和分析工具(2)——可视化
上篇博客我们介绍了虚拟机监控和分析命令行工具,由于其不够直观,不是很容易排查问题,那么本篇博客我们就来介绍几个可视化工具. 1.JConsole JConsole(Java Monitoring an ...
- Leetcode之二分法专题-240. 搜索二维矩阵 II(Search a 2D Matrix II)
Leetcode之二分法专题-240. 搜索二维矩阵 II(Search a 2D Matrix II) 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target.该矩阵 ...
- 设置VS2015背景图片(转载)
设置方法很简单:安装扩展ClaudiaIDE 1.在这里下载扩展,https://visualstudiogallery.msdn.microsoft.com/9ba50f8d-f30c-4e33-a ...
- 快速掌握SPSS数据分析
SPSS难吗?无非就是数据类型的区别后,就能理解应该用什么样的分析方法,对应着分析方法无非是找一些参考资料进行即可.甚至在线网页SPSS软件直接可以将数据分析结果指标人工智能地分析出来,这有多难呢 ...
- 007 Linux系统优化进阶
一.更改 ssh 服务远程登录的配置 windows:默认远程端口和管理员用户 管理员:administrator port :3389 Linux:远程连接默认端口和超级用户 管理员:root ...
- 分享一个赚钱方法:用趣分类app在家轻松赚钱
什么是趣分类 近期,垃圾分类是社会各界和广大市民关心的一个热门话题,随着垃圾分类工作的推进,各地都掀起学习垃圾分类的热潮.为了我们的美好生活,打响"垃圾分类"这场硬仗刻不容缓.据了 ...