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任务调度相关推荐

  1. xxl子任务_阿里面试官:聊一下分布式任务调度有那些解决方案?

    作者:黄兆平 来源:http://blog.freshfood.cn/article/39 # 简介 随着系统规模的发展,定时任务数量日益增多,任务也变得越来越复杂,尤其是在分布式环境下,存在多个业务 ...

  2. xxl子任务_XXL-JOB v2.0.2,分布式任务调度平台 | 多项特性优化更新

    v2.0.2 Release Notes 1.底层通讯方案优化:升级较新版本xxl-rpc,由"JETTY"方案调整为"NETTY_HTTP"方案,执行器内嵌n ...

  3. xxl子任务_XXL-JOB v2.1.2 发布,分布式任务调度平台

    v2.1.2 Release Notes 1.方法任务支持:由原来基于JobHandler类任务开发方式,优化为支持基于方法的任务开发方式:因此,可以支持单个类中开发多个任务方法,进行类复用 @Xxl ...

  4. 分布式定时任务调度系统技术选型--转

    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% ...

  5. python建站部署_SpringBoot入门建站全系列(三十二)接入xxl-job分布式任务调度平台...

    SpringBoot入门建站全系列(三十二)接入xxl-job分布式任务调度平台 一.概述 XXL-JOB是一个轻量级分布式任务调度平台,其核心设计目标是开发迅速.学习简单.轻量级.易扩展.现已开放源 ...

  6. python任务调度平台 界面_分布式任务调度平台XXL-JOB

    以前带我的人说过,最好的学习就是看官方文档,个人也有4个T的学习视频,但是会发现讲的都是入门,有的也比较浅. 官方文档比较官方,也比较权威,打开xxl-job的官网,写的贼详细,有些人喜欢收博客,不喜 ...

  7. 轻量级分布式任务调度平台 XXL-JOB

    From:https://www.cnblogs.com/xuxueli/p/5021979.html github 地址 及 中文文档地址:https://github.com/xuxueli/xx ...

  8. 3千字带你搞懂XXL-JOB任务调度平台

    思维导图 文章已收录Github精选,欢迎Star:https://github.com/yehongzhi/learningSummary 一.概述 在平时的业务场景中,经常有一些场景需要使用定时任 ...

  9. 分布式任务调度平台一站式讲解

    文章目录 一.传统的定时任务 1. 传统的定时任务存在那些缺点 2. 定时任务集群幂等性问题 二.传统定时任务的实现方案 2.1. 多线程 2.2. TimeTask 2.3. 线程池 2.4. Sp ...

  10. SpringBoot2 集成 xxl-job任务调度中心_路由策略

    文章目录 一.简述 二.故障转移演示 2.1. 启动2个执行器 2.2. 添加执行器ip 2.3. 故障转移策略 2.4. 启动任务 2.5. 模拟8081执行器宕机 2.6. 结论 三.轮训策略演示 ...

最新文章

  1. Linux I2C工具查看配置I2C设备【转】
  2. 模拟电视频率可用于超级Wi-Fi
  3. Set 的合集 并集 差集
  4. 更新代码到gitlab上
  5. 2 TileMapObject的使用
  6. iptables基础篇
  7. bootcmd 和bootargs
  8. Android之提示android.content.res.Resources$NotFoundException: Resource ID #0x7f08010a
  9. JUnit测试方法订购
  10. jdbc获取mysql第二行表信息_【奇技淫巧】MySQL另类方法获取元数据信息
  11. 云主机用linux还是winows,云服务器一般使用什么系统?Linux还是Windows?
  12. 不能定义声明dllimport_不允许 dllimport 函数 的定义 高手帮帮忙
  13. 学python买什么书-19年学习Python有什么好的书籍推荐吗?
  14. 再见Navicat! IDEA的这个兄弟真的很香!我粉了...
  15. 股票交易软件接口编程语言
  16. LinuxC网络编程
  17. 微信订阅号利用订阅号助手发布信息
  18. 8.NLTK之分析句子结构
  19. 学习编程悟出8个字《精辟》!
  20. python 下载 M3U8 视频

热门文章

  1. 上海行:陈正翔与 Mind+,最简易图形化编程工具
  2. 运行edX Devstack
  3. 百度「联邦学习」战略全布局
  4. gentoo linux u盘安装,Gentoo系统安装步骤详解
  5. 服务器 备案 文档,备案需要备案服务器
  6. ethtool如何让接口闪灯_如何使用ethtool命令操作以太网卡
  7. 7分钟学会HTML网页制作
  8. 类和结构体的内存空间占有问题
  9. GitLab 设置为中文版
  10. 【华为od机试】统计射击比赛成绩-Python3