XXL-JOB任务调度
XXL-JOB
- 前言
- 一、简介
- 设计思想
- 系统组成
- 架构图
- 二、如何使用
- 下载地址
- 简单说说
- 一个栗子
- 三、动态添加定时任务
前言
前段时间工作中用到了XXL-JOB,所以想着写一篇博客记录一下,比较懒,拖了很久。。。
一、简介
官网地址:https://www.xuxueli.com/xxl-job/
设计思想
将"调度"和"任务"进行解耦。
系统组成
调度模块(调度中心):负责管理调度信息,按照调度配置发出调度请求,自身不承担业务代码。调度系统与任务解耦,提高了系统可用性和稳定性,同时调度系统性能不再受限于任务模块;支持可视化、简单且动态的管理调度信息,包括任务新建,更新,删除,GLUE开发和任务报警等,所有上述操作都会实时生效,同时支持监控调度结果以及执行日志,支持执行器Failover。
执行模块(执行器):负责接收调度请求并执行任务逻辑。任务模块专注于任务的执行等操作,开发和维护更加简单和高效;接收“调度中心”的执行请求、终止请求和日志请求等。
架构图
二、如何使用
下载地址
源码地址:https://gitee.com/xuxueli0323/xxl-job/releases
目前最新版本是2.3.0
源码下载下来之后,执行其中的sql文件(xxl-job-master\doc\db\tables_xxl_job.sql),所使用的的数据库是mysql,版本5.7或以上。
简单说说
启动xxl-job-admin模块,在浏览器访问localhost:8080//xxl-job-admin,输入用户密码admin/123456,即可进入页面。
这里一些可视化的功能就不去介绍了。
对照界面中的任务管理和执行器管理以及数据库的表数据,可以看出对应关系。
所以,可以猜到源码中包含可以查询执行器和调度任务的接口,是的,就在这里。
一个栗子
在XXL-JOB上新建一个执行器xxl-job-executor-test,这里的注册方式选择自动注册就好,业务服务在启动时会将自己机器的ip和端口号注册到这个执行器上。
新建一个属于该执行器的执行任务,这里选择“测试执行器”,也就刚才新建的执行器,调度类型选择固定速度,5秒执行一次,运行模式选择bean,执行时会根据注册的执行地址以及JobHandler找到对应的执行方法。
调度任务这里有Cron和固定速度两种,假如使用cron,它会以固定的时间点执行任务,而不是在定时器启动后开始计算时间执行;使用固定速度,那么就会从定时器启动后开始计算下一次执行时间。
新建一个springboot服务,当做是业务服务。
依赖:
<dependency><groupId>com.xuxueli</groupId><artifactId>xxl-job-core</artifactId><version>2.3.0</version></dependency>
配置文件
server:port: 8090tomcat:uri-encoding: UTF-8max-connections: 1024max-threads: 100xxl:job:isConfigure: true #选择性注册,true说明将自己机器的ip和端口号注册进执行器scheduleConf: 60 #定时任务的执行间隔admin:addresses: http://127.0.0.1:8080/xxl-job-admin #xxl-job的地址userName: adminpassword: 123456executor:appname: xxl-job-executor-sample #执行器的名字logpath: /data/applogs/xxl-job/jobhandler/logretentiondays: -1 #过期日志自动清理;限制大于等于7时生效,否则, 如-1,关闭自动清理功能;port: 9999 #任务执行地址的端口
向执行器注册执行地址的配置类
@Configuration
@Slf4j
@ConditionalOnProperty(value = "xxl.job.isConfigure", havingValue = "true", matchIfMissing = false)
public class XxlJobConfig {@Autowiredprivate XxlJobProperties xxlJobProperties;@Beanpublic XxlJobSpringExecutor xxlJobExecutor() {log.info(">>>>>>>>>>> xxl-job config init. xxlJobProperties:{}", JSONObject.toJSONString(xxlJobProperties));XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();xxlJobSpringExecutor.setAdminAddresses(xxlJobProperties.getAdmin().getAddresses());xxlJobSpringExecutor.setAppname(xxlJobProperties.getExecutor().getAppname());xxlJobSpringExecutor.setPort(xxlJobProperties.getExecutor().getPort());xxlJobSpringExecutor.setLogPath(xxlJobProperties.getExecutor().getLogpath());xxlJobSpringExecutor.setLogRetentionDays(xxlJobProperties.getExecutor().getLogretentiondays());return xxlJobSpringExecutor;}
}
yaml文件属性注入的配置类
@Data
@Component
@ConfigurationProperties(prefix = "xxl.job")
public class XxlJobProperties {private Admin admin;private Executor executor;private String scheduleConf;private String orderPoolName;@Datapublic static class Admin {private String addresses;private String userName;private String password;}@Datapublic static class Executor {private String appname;private String logpath;private int logretentiondays;private int port;}
}
任务执行逻辑,xxl-job会根据注解@XxlJob(“JobHandler”)找到对应执行方法
@Component
@Slf4j
public class XxlJobHandler {@XxlJob("JobHandler")public ReturnT<String> jobHandler() {String param = XxlJobHelper.getJobParam();String strDateFormat = "yyyy-MM-dd HH:mm:ss";SimpleDateFormat sdf = new SimpleDateFormat(strDateFormat);log.info("time : {} param: {}", sdf.format(new Date()), param);return ReturnT.SUCCESS;}
}
启动两个业务服务,要注意的是,除了两个服务的启动端口不同之外,还有注册到XXL-JOB的端口也要不同。
如果上述操作正确,那么在XXL-JOB界面上的执行器中会看到两个注册成功的地址,如下:
自此,这个执行器上的定时任务将会根据这两个注册地址去查找对应的JobHandler并执行任务。
然后启动定时任务,查看业务服务的表现
仔细看时间戳会发现,这个执行策略其实是轮训,因为在创建执行器时,将路由策略选择为轮训
三、动态添加定时任务
有时候我们需要在业务服务中心动态的添加定时任务,之前有说过,XXL-JOB的源码中提供了对外的api,支持对执行器和定时任务的查询和新增。
对XXL-JOB的api的封装:
@Component
@Slf4j
public class XxlJobCommon {@Value("${xxl.job.admin.addresses}")private String adminAddresses;@Value("${xxl.job.admin.userName}")private String userName;@Value("${xxl.job.admin.password}")private String password;@Value("${xxl.job.executor.appname}")private String appname;/*** 动态添加订单取消的定时器** @param xxlJobInfo* @return*/public int addJob(XxlJobInfo xxlJobInfo) {log.info("添加定时器入参xxlJobInfo:{}", JSONObject.toJSONString(xxlJobInfo));int jobId = 0;getCookie();//获取cookietry {String path = adminAddresses + "/jobinfo/add";String jobgroupPath = "/jobgroup/pageList";Map<String, Object> paramMap = new HashMap<>();paramMap.put("jobGroup", getJobGroupId(jobgroupPath));paramMap.put("jobDesc", xxlJobInfo.getJobDesc());paramMap.put("executorRouteStrategy", xxlJobInfo.getExecutorRouteStrategy());// 路由策略paramMap.put("glueType", xxlJobInfo.getGlueType());paramMap.put("executorHandler", xxlJobInfo.getExecutorHandler()); // 此处hander需提前在项目中定义paramMap.put("executorBlockStrategy", xxlJobInfo.getExecutorBlockStrategy());paramMap.put("executorTimeout", xxlJobInfo.getExecutorTimeout());paramMap.put("executorFailRetryCount", xxlJobInfo.getExecutorFailRetryCount());//执行失败重试paramMap.put("author", xxlJobInfo.getAuthor());paramMap.put("scheduleType", xxlJobInfo.getScheduleType());paramMap.put("scheduleConf", xxlJobInfo.getScheduleConf());paramMap.put("glueRemark", xxlJobInfo.getGlueRemark());paramMap.put("triggerStatus", xxlJobInfo.getTriggerStatus()); //调度状态:0-停止,1-运行paramMap.put("misfireStrategy", xxlJobInfo.getMisfireStrategy());paramMap.put("executorParam", xxlJobInfo.getExecutorParam());//入参HttpResponse response = HttpRequest.post(path).form(paramMap).execute();if (HttpStatus.HTTP_OK != response.getStatus()) {log.error("订单:{} 定时器创建失败", xxlJobInfo.getExecutorParam());}JSONObject jsonObject = JSON.parseObject(response.body());log.info("--->jsonObject:{}", jsonObject.toJSONString());jobId = jsonObject.getIntValue("content");} catch (Exception e) {log.error("订单:{} 定时器创建失败", xxlJobInfo.getExecutorParam(), e);}return jobId;}/*** 获取jogGroup的id** @param jobgroupPath* @return* @throws Exception*/public int getJobGroupId(String jobgroupPath) throws Exception {Map<String, Object> jobgroupParamMap = new HashMap<>();//获取jobGroup的IdjobgroupParamMap.put("appname", appname);HttpResponse jobgroupResponse = HttpRequest.post(adminAddresses + jobgroupPath).form(jobgroupParamMap).execute();log.info("jobgroupResponse : {}", JSON.toJSONString(jobgroupResponse.body()));Map<String, Object> stringObjectMap = JSON.parseObject(jobgroupResponse.body(), new TypeReference<Map<String, Object>>() {});List<XxlJobGroup> data = JSON.parseObject(JSON.toJSONString(stringObjectMap.get("data")), new TypeReference<List<XxlJobGroup>>() {});return data.get(0).getId();}/*** 获取XxlJobInfo** @param jobDesc* @return*/public XxlJobInfo getXxlJobInfo(String jobDesc) throws Exception {String path = adminAddresses + "/jobinfo/pageList";Map<String, Object> paramMap = new HashMap<>();paramMap.put("jobDesc", jobDesc);String jobgroupPath = "/jobgroup/pageList";paramMap.put("jobGroup", getJobGroupId(jobgroupPath));paramMap.put("triggerStatus", 1);HttpResponse response = HttpRequest.post(path).form(paramMap).execute();log.info("response : {}", JSON.toJSONString(response.body()));Map<String, Object> stringObjectMap = JSON.parseObject(response.body(), new TypeReference<Map<String, Object>>() {});List<XxlJobInfo> data = JSON.parseObject(JSON.toJSONString(stringObjectMap.get("data")), new TypeReference<List<XxlJobInfo>>() {});return data.get(0);}/*** 停止定时器** @param jobId*/public void stop(int jobId) {String pathStop = adminAddresses + "/jobinfo/stop";Map<String, Object> paramMapStop = new HashMap<>();paramMapStop.put("id", jobId);HttpRequest.post(pathStop).form(paramMapStop).execute();}/*** 删除定时器** @param jobDesc*/public void remove(String jobDesc) throws Exception {XxlJobInfo xxlJobInfo = getXxlJobInfo(jobDesc);String pathRemove = adminAddresses + "/jobinfo/remove";Map<String, Object> paramMapRemove = new HashMap<>();paramMapRemove.put("id", xxlJobInfo.getId());HttpRequest.post(pathRemove).form(paramMapRemove).execute();}/*** 获取cookie** @return*/public String getCookie() {String path = adminAddresses + "/login";Map<String, Object> hashMap = new HashMap();hashMap.put("userName", userName);hashMap.put("password", password);HttpResponse response = HttpRequest.post(path).form(hashMap).execute();List<HttpCookie> cookies = response.getCookie();StringBuilder sb = new StringBuilder();for (HttpCookie cookie : cookies) {sb.append(cookie.toString());}String cookie = sb.toString();log.info("获取cookie:{}", cookie);return cookie;}
}
模拟动态添加定时任务
@RestController
@RequestMapping("/demo")
@Slf4j
public class AddJobController {@Autowiredprivate XxlJobCommon xxlJobCommon;@Autowiredprivate XxlJobProperties xxlJobProperties;@GetMapping("/addJob/{jobParam}")@ResponseBodypublic String addJob(@PathVariable("jobParam") String jobParam) {try{XxlJobInfo xxlJobInfo = new XxlJobInfo();xxlJobInfo.setJobDesc(jobParam);xxlJobInfo.setExecutorRouteStrategy("FAILOVER");// 路由策略xxlJobInfo.setGlueType("BEAN");xxlJobInfo.setExecutorHandler("JobHandler");// 此处hander需提前在项目中定义xxlJobInfo.setExecutorBlockStrategy("SERIAL_EXECUTION");xxlJobInfo.setExecutorTimeout(0);xxlJobInfo.setExecutorFailRetryCount(3);//执行失败重试xxlJobInfo.setAuthor("admin");xxlJobInfo.setScheduleType("FIX_RATE");xxlJobInfo.setScheduleConf(xxlJobProperties.getScheduleConf());// 时间间隔xxlJobInfo.setGlueRemark("GLUE代码初始化");xxlJobInfo.setTriggerStatus(1); //调度状态:0-停止,1-运行xxlJobInfo.setMisfireStrategy("DO_NOTHING");xxlJobInfo.setExecutorParam(jobParam); //执行时的入参xxlJobCommon.addJob(xxlJobInfo);}catch (Exception e){log.error("异常:",e);}return "OK";}}
请求接口http://localhost:8091/demo/addJob/addJobParam之后,会发现XXL-JOB中多了一个定时任务,这个定时器的各种属性也符合在业务中编写的属性。
也可以看到这个执行任务的执行
XXL-JOB任务调度相关推荐
- xxl子任务_阿里面试官:聊一下分布式任务调度有那些解决方案?
作者:黄兆平 来源:http://blog.freshfood.cn/article/39 # 简介 随着系统规模的发展,定时任务数量日益增多,任务也变得越来越复杂,尤其是在分布式环境下,存在多个业务 ...
- xxl子任务_XXL-JOB v2.0.2,分布式任务调度平台 | 多项特性优化更新
v2.0.2 Release Notes 1.底层通讯方案优化:升级较新版本xxl-rpc,由"JETTY"方案调整为"NETTY_HTTP"方案,执行器内嵌n ...
- xxl子任务_XXL-JOB v2.1.2 发布,分布式任务调度平台
v2.1.2 Release Notes 1.方法任务支持:由原来基于JobHandler类任务开发方式,优化为支持基于方法的任务开发方式:因此,可以支持单个类中开发多个任务方法,进行类复用 @Xxl ...
- 分布式定时任务调度系统技术选型--转
http://www.expectfly.com/2017/08/15/%E5%88%86%E5%B8%83%E5%BC%8F%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1% ...
- python建站部署_SpringBoot入门建站全系列(三十二)接入xxl-job分布式任务调度平台...
SpringBoot入门建站全系列(三十二)接入xxl-job分布式任务调度平台 一.概述 XXL-JOB是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速.学习简单.轻量级.易扩展.现已开放源 ...
- python任务调度平台 界面_分布式任务调度平台XXL-JOB
以前带我的人说过,最好的学习就是看官方文档,个人也有4个T的学习视频,但是会发现讲的都是入门,有的也比较浅. 官方文档比较官方,也比较权威,打开xxl-job的官网,写的贼详细,有些人喜欢收博客,不喜 ...
- 轻量级分布式任务调度平台 XXL-JOB
From:https://www.cnblogs.com/xuxueli/p/5021979.html github 地址 及 中文文档地址:https://github.com/xuxueli/xx ...
- 3千字带你搞懂XXL-JOB任务调度平台
思维导图 文章已收录Github精选,欢迎Star:https://github.com/yehongzhi/learningSummary 一.概述 在平时的业务场景中,经常有一些场景需要使用定时任 ...
- 分布式任务调度平台一站式讲解
文章目录 一.传统的定时任务 1. 传统的定时任务存在那些缺点 2. 定时任务集群幂等性问题 二.传统定时任务的实现方案 2.1. 多线程 2.2. TimeTask 2.3. 线程池 2.4. Sp ...
- SpringBoot2 集成 xxl-job任务调度中心_路由策略
文章目录 一.简述 二.故障转移演示 2.1. 启动2个执行器 2.2. 添加执行器ip 2.3. 故障转移策略 2.4. 启动任务 2.5. 模拟8081执行器宕机 2.6. 结论 三.轮训策略演示 ...
最新文章
- Linux I2C工具查看配置I2C设备【转】
- 模拟电视频率可用于超级Wi-Fi
- Set 的合集 并集 差集
- 更新代码到gitlab上
- 2 TileMapObject的使用
- iptables基础篇
- bootcmd 和bootargs
- Android之提示android.content.res.Resources$NotFoundException: Resource ID #0x7f08010a
- JUnit测试方法订购
- jdbc获取mysql第二行表信息_【奇技淫巧】MySQL另类方法获取元数据信息
- 云主机用linux还是winows,云服务器一般使用什么系统?Linux还是Windows?
- 不能定义声明dllimport_不允许 dllimport 函数 的定义 高手帮帮忙
- 学python买什么书-19年学习Python有什么好的书籍推荐吗?
- 再见Navicat! IDEA的这个兄弟真的很香!我粉了...
- 股票交易软件接口编程语言
- LinuxC网络编程
- 微信订阅号利用订阅号助手发布信息
- 8.NLTK之分析句子结构
- 学习编程悟出8个字《精辟》!
- python 下载 M3U8 视频