XXL-Job详解(五):动态添加、启动任务

目录

    • 前言
    • XXL-Job API接口
    • 添加任务API
    • 动态添加任务
    • 动态启动任务

前言

看该文章之前,最好看一下之前的文章,比较方便我们理解
XXL-Job详解(一):组件架构
XXL-Job详解(二):安装部署
XXL-Job详解(三):任务开发
XXL-Job详解(四):任务注册原理

XXL-Job API接口

我们之前xxl-job添加任务,需要去管理后台进行添加,在一些任务量大或者需要实时添加任务的情况,通过手动添加任务是不现实的,那有什么方法呢?

我们需要知道,我们在页面添加任务,其实调用的就是调度平台的API接口,调度平台的API接口就在xxl-job-admin的com.xxl.job.admin.controller目录下
在这里插入图片描述
那我们怎么知道添加任务是哪个接口呢,很简单,我们在页面添加一个任务,看页面请求了哪个接口就行,我新增了一个任务,可以看到,调度平台是请求了add接口,很明显,add就是我们调度平台添加任务的接口

在这里插入图片描述

添加任务API

我们去调度平台项目查找add接口,add接口就在JobInfoController下,add接口如下

	@RequestMapping("/add")@ResponseBodypublic ReturnT<String> add(XxlJobInfo jobInfo) {return xxlJobService.add(jobInfo);}

下面我们看一下service的add方法

@Overridepublic ReturnT<String> add(XxlJobInfo jobInfo) {// valid baseXxlJobGroup group = xxlJobGroupDao.load(jobInfo.getJobGroup());if (group == null) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_choose")+I18nUtil.getString("jobinfo_field_jobgroup")) );}if (jobInfo.getJobDesc()==null || jobInfo.getJobDesc().trim().length()==0) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_jobdesc")) );}if (jobInfo.getAuthor()==null || jobInfo.getAuthor().trim().length()==0) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+I18nUtil.getString("jobinfo_field_author")) );}// valid triggerScheduleTypeEnum scheduleTypeEnum = ScheduleTypeEnum.match(jobInfo.getScheduleType(), null);if (scheduleTypeEnum == null) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );}if (scheduleTypeEnum == ScheduleTypeEnum.CRON) {if (jobInfo.getScheduleConf()==null || !CronExpression.isValidExpression(jobInfo.getScheduleConf())) {return new ReturnT<String>(ReturnT.FAIL_CODE, "Cron"+I18nUtil.getString("system_unvalid"));}} else if (scheduleTypeEnum == ScheduleTypeEnum.FIX_RATE/* || scheduleTypeEnum == ScheduleTypeEnum.FIX_DELAY*/) {if (jobInfo.getScheduleConf() == null) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")) );}try {int fixSecond = Integer.valueOf(jobInfo.getScheduleConf());if (fixSecond < 1) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );}} catch (Exception e) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("schedule_type")+I18nUtil.getString("system_unvalid")) );}}// valid jobif (GlueTypeEnum.match(jobInfo.getGlueType()) == null) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_gluetype")+I18nUtil.getString("system_unvalid")) );}if (GlueTypeEnum.BEAN==GlueTypeEnum.match(jobInfo.getGlueType()) && (jobInfo.getExecutorHandler()==null || jobInfo.getExecutorHandler().trim().length()==0) ) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("system_please_input")+"JobHandler") );}// 》fix "\r" in shellif (GlueTypeEnum.GLUE_SHELL==GlueTypeEnum.match(jobInfo.getGlueType()) && jobInfo.getGlueSource()!=null) {jobInfo.setGlueSource(jobInfo.getGlueSource().replaceAll("\r", ""));}// valid advancedif (ExecutorRouteStrategyEnum.match(jobInfo.getExecutorRouteStrategy(), null) == null) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorRouteStrategy")+I18nUtil.getString("system_unvalid")) );}if (MisfireStrategyEnum.match(jobInfo.getMisfireStrategy(), null) == null) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("misfire_strategy")+I18nUtil.getString("system_unvalid")) );}if (ExecutorBlockStrategyEnum.match(jobInfo.getExecutorBlockStrategy(), null) == null) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_executorBlockStrategy")+I18nUtil.getString("system_unvalid")) );}// 》ChildJobId validif (jobInfo.getChildJobId()!=null && jobInfo.getChildJobId().trim().length()>0) {String[] childJobIds = jobInfo.getChildJobId().split(",");for (String childJobIdItem: childJobIds) {if (childJobIdItem!=null && childJobIdItem.trim().length()>0 && isNumeric(childJobIdItem)) {XxlJobInfo childJobInfo = xxlJobInfoDao.loadById(Integer.parseInt(childJobIdItem));if (childJobInfo==null) {return new ReturnT<String>(ReturnT.FAIL_CODE,MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_not_found")), childJobIdItem));}} else {return new ReturnT<String>(ReturnT.FAIL_CODE,MessageFormat.format((I18nUtil.getString("jobinfo_field_childJobId")+"({0})"+I18nUtil.getString("system_unvalid")), childJobIdItem));}}// join , avoid "xxx,,"String temp = "";for (String item:childJobIds) {temp += item + ",";}temp = temp.substring(0, temp.length()-1);jobInfo.setChildJobId(temp);}// add in dbjobInfo.setAddTime(new Date());jobInfo.setUpdateTime(new Date());jobInfo.setGlueUpdatetime(new Date());xxlJobInfoDao.save(jobInfo);if (jobInfo.getId() < 1) {return new ReturnT<String>(ReturnT.FAIL_CODE, (I18nUtil.getString("jobinfo_field_add")+I18nUtil.getString("system_fail")) );}return new ReturnT<String>(String.valueOf(jobInfo.getId()));}

这个方法很简单,最重要就是两步

1、校验jobinfo参数

2、插入jobinfo到表

好了,到这里我们就基本了解了xxl-job添加任务的流程了,那么我们想动态添加任务就很简单了,主要流程是以下两步

1、构造jobinfo对象

2、以jobinfo对象为入参请求add接口,请求add接口我们可以使用RestTemplate或者openFeign等,大家按自己项目来决定,下面我使用的是RestTemplate

动态添加任务

1、首先把调度平台的jobinfo对象拷贝到我们的执行器项目,jobinfo对象在com.xxl.job.admin.core.model目录下,我们按需拷贝对象

public class XxlJobInfo {private int id;				// 主键IDprivate int jobGroup;		// 执行器主键IDprivate String jobDesc;private Date addTime;private Date updateTime;private String author;		// 负责人private String alarmEmail;	// 报警邮件private String scheduleType;			// 调度类型private String scheduleConf;			// 调度配置,值含义取决于调度类型private String misfireStrategy;			// 调度过期策略private String executorRouteStrategy;	// 执行器路由策略private String executorHandler;		    // 执行器,任务Handler名称private String executorParam;		    // 执行器,任务参数private String executorBlockStrategy;	// 阻塞处理策略private int executorTimeout;     		// 任务执行超时时间,单位秒private int executorFailRetryCount;		// 失败重试次数private String glueType;		// GLUE类型	#com.xxl.job.core.glue.GlueTypeEnumprivate String glueSource;		// GLUE源代码private String glueRemark;		// GLUE备注private Date glueUpdatetime;	// GLUE更新时间private String childJobId;		// 子任务ID,多个逗号分隔private int triggerStatus;		// 调度状态:0-停止,1-运行private long triggerLastTime;	// 上次调度时间private long triggerNextTime;	// 下次调度时间}

2、放开登录校验

因为调度平台的api接口都是在后台,要请求接口,都需要通过登录校验,要解决这个问题,有两个方法

1、模拟登录(请求登录接口),获取登录cookie

2、在我们需要请求的接口上添加@PermissionLimit(limit = false)注解 ,默认是true,也就是需要做登录验证

/*** 权限限制* @author xuxueli 2015-12-12 18:29:02*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionLimit {/*** 登录拦截 (默认拦截)*/boolean limit() default true;/*** 要求管理员权限** @return*/boolean adminuser() default false;}

我为了方便,就直接使用PermissionLimit注解放开校验,大家按自己实际情况选择

为了不影响原有的接口,新增一个addJob接口,在这个接口上进行修改

在add接口添加PermissionLimit注解,如下所示

	@RequestMapping("/addJob")@ResponseBody@PermissionLimit(limit = false)public ReturnT<String> addJob(@RequestBody XxlJobInfo jobInfo) {return xxlJobService.add(jobInfo);}

因为我们在设置XxlJobInfo 信息的时候,还需要获取XxlJobGroup的信息,所以我们还需要在JobGroupController添加以下接口

	@RequestMapping("/loadByAppName")@ResponseBody@PermissionLimit(limit = false)public ReturnT<XxlJobGroup> loadByAppName(@RequestBody XxlJobGroup xxlJobGroup){XxlJobGroup jobGroup = xxlJobGroupDao.loadByAppName(xxlJobGroup);return jobGroup!=null?new ReturnT<XxlJobGroup>(jobGroup):new ReturnT<XxlJobGroup>(ReturnT.FAIL_CODE, null);}

loadByAppName也是我新增的,如下所示,使用appname查找XxlJobGroup信息

	<select id="loadByAppName" resultType="com.xxl.job.admin.core.model.XxlJobGroup">SELECT <include refid="Base_Column_List" />FROM xxl_job_group AS tWHERE t.app_name = #{appname}</select>

3、添加请求工具类

@Component
public class XxlJobUtil {/*** xxl-job地址*/@Value("${xxl.job.admin.addresses}")private String adminAddresses;/*** xxl-job标识*/@Value("${xxl.job.executor.appname}")private String appname;private RestTemplate restTemplate = new RestTemplate();/*** 调度平台的api*/private static final String ADD_URL = "/jobinfo/addJob";private static final String GET_GROUP_INFO = "/jobgroup/loadByAppName";/*** 添加任务* @param jobInfo* @return*/public String add(XxlJobInfo jobInfo){// 查询appname对应groupId:Map<String,Object> param = new HashMap<>();param.put("appname", appname);String json = JSON.toJSONString(param);String result = doPost(adminAddresses + GET_GROUP_INFO, json);JSONObject jsonObject = JSON.parseObject(result);String content = jsonObject.getString("content");if (content != null){XxlJobGroup jobGroup = JSONObject.parseObject(content,XxlJobGroup.class);int groupId = jobGroup.getId();jobInfo.setJobGroup(groupId);}//发送请求String json2 = JSON.toJSONString(jobInfo);return doPost(adminAddresses + ADD_URL, json2);}/*** 发送请求* @param url* @param json* @return*/public String doPost(String url, String json){HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);HttpEntity<String> entity = new HttpEntity<>(json ,headers);ResponseEntity<String> stringResponseEntity = restTemplate.postForEntity(url, entity, String.class);return stringResponseEntity.getBody().toString();}}

4、测试

我们在执行器项目,添加一个接口,来测试下,是否可以动态添加任务

@Controller
@RequestMapping("/job")
public class JobInfoController {@Autowiredprivate XxlJobUtil xxlJobUtil;@RequestMapping("/add")@ResponseBodypublic String add(XxlJobInfo jobInfo) {return xxlJobUtil.add(jobInfo);}}

使用postman请求,可以看到,已经成功返回,content就是我们的任务ID,去调度中心的任务管理也可以看到我们的任务
在这里插入图片描述
在这里插入图片描述

动态启动任务

有了上面动态添加任务的案例,动态启动任务就简单了,下面我示范下,其他像暂停、删除等操作也类似

1、在调度平台项目,新增一个启动任务的接口

	@RequestMapping("/startJob")@ResponseBody@PermissionLimit(limit = false)public ReturnT<String> startJob(@RequestBody XxlJobInfo jobInfo) {return xxlJobService.start(jobInfo.getId());}

2、在执行器项目,添加动态启动项目的方法

/*** 启动任务* @param jobId* @return*/public String start(int jobId) {JSONObject json = new JSONObject();json.put("id",jobId);String jsonString = JSON.toJSONString(json);String result = doPost(adminAddresses + START_JOB_URL, jsonString);return result;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/197037.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

沐风老师3DMAX随机变换工具RandomTransform插件使用方法详解

3DMAX随机变换工具RandomTransform插件使用方法 3dMax随机变换工具RandomTransform&#xff0c;是一款用MAXScript脚本语言开发的3dsMax小工具&#xff0c;可以随机变换选中的单个或多个对象的位置、角度及大小。 在3dMax中“变换”工具是最常用的工具&#xff08;移动、旋转和…

vue3+ts项目中导入组件时报错has no default export

下面这句会报错has no default export import Button from "./components/Button.vue";使用vetur这个插件&#xff08;我目前的版本是0.37.3&#xff0c;应该是这个版本之前的都不支持&#xff09;。但是依旧报错&#xff0c;所以我选择禁用了&#xff0c;就不报错了…

Redis的基本数据类型及常用命令

Redis通用命令 KEYS命令用于查找所有匹配给定模式 pattern 的 key 。生产环境下不建议使用KEYS命令&#xff0c;会影响效率。 匹配规则&#xff1a; h?llo 匹配 hello, hallo 和 hxlloh*llo 匹配 hllo 和 heeeelloh[ae]llo 匹配 hello and hallo, 不匹配 hilloh[^e]llo 匹配 …

selenium自动化测试实战案例

Chrome DevTools 简介 Chrome DevTools 是一组直接内置在基于 Chromium 的浏览器&#xff08;如 Chrome、Opera 和 Microsoft Edge&#xff09;中的工具&#xff0c;用于帮助开发人员调试和研究网站。 借助 Chrome DevTools&#xff0c;开发人员可以更深入地访问网站&#xf…

8.4 Windows驱动开发:文件微过滤驱动入门

MiniFilter 微过滤驱动是相对于SFilter传统过滤驱动而言的&#xff0c;传统文件过滤驱动相对来说较为复杂&#xff0c;且接口不清晰并不符合快速开发的需求&#xff0c;为了解决复杂的开发问题&#xff0c;微过滤驱动就此诞生&#xff0c;微过滤驱动在编写时更简单&#xff0c;…

全网最牛最“刑”的Fiddler移动端抓包

本篇文章&#xff0c;博主想使用通俗易懂的话语&#xff0c;让大家明白以下内容&#xff1a; 什么是抓包哪些场景需要用到抓包Fiddler抓包的原理怎样使用Fiddler进行移动端抓包 抓包 包 (Packet) 是TCP/IP协议通信传输中的数据单位&#xff0c;一般也称“数据包”。 我们平常…

[54] 螺旋矩阵 js

题目描述&#xff1a;给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] * 输出&#xff1a;[1,2,3,6,9,8,7,4,5] 解题思路&#xff1a; 按照顺时针一行一列&#xff…

源码安装git

系统&#xff1a; Centos 由于系统自带的yum源仓库的git版本较低&#xff0c;所以在官网下载源码进行编译安装 官网地址&#xff1a;https://git-scm.com/ 源码下载地址&#xff1a;https://github.com/git/git 安装旧版本的git拉去git源码&#xff08;如果是上传到服务器就不用…

无人机智慧工地:助力工地管理的未来之选

在现代工地管理中&#xff0c;无人机凭借其小巧、轻便和多角度拍摄等特点得到广泛应用&#xff0c;尤其在智慧工地的现场管理中发挥着重要作用。 一、无人机代替人工巡检省时省力 以往&#xff0c;施工现场检查主要依赖人工巡检方式&#xff0c;需要较长时间。而现在&#xff…

链表【2】

文章目录 &#x1f95d;24. 两两交换链表中的节点&#x1f951;题目&#x1f33d;算法原理&#x1f96c;代码实现 &#x1f34e;143. 重排链表&#x1f352;题目&#x1f345;算法原理&#x1f353;代码实现 &#x1f95d;24. 两两交换链表中的节点 &#x1f951;题目 题目链接…

【LeetCode热题100】【哈希】最长连续序列

据说是字节跳动二面的原题&#xff0c;这题面试要是让我当场手撕&#xff0c;我直接当场去世T_T 给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 请你设计并实现时间复杂度为 O(n) 的算法解…

KMP字符串

试题传送门&#xff1a;831. KMP字符串 给定一个字符串 S&#xff0c;以及一个模式串 P&#xff0c;所有字符串中只包含大小写英文字母以及阿拉伯数字。 模式串 P 在字符串 S 中多次作为子串出现。 求出模式串 P 在字符串 S 中所有出现的位置的起始下标。 输入格式 第一行输入…

Azure Machine Learning - Azure AI 搜索中的索引器

在 Azure AI 搜索中&#xff0c;搜索索引是可搜索的内容&#xff0c;可供搜索引擎用于索引编制、全文搜索和筛选后查询。 索引由架构定义并保存到搜索服务中&#xff0c;第二步是数据导入。 除了在主数据存储中&#xff0c;此内容也存在于搜索服务中&#xff0c;这是在新式应用…

由11月27日滴滴崩溃到近两个月国内互联网产品接二连三崩溃引发的感想

文章目录 知乎文分析微信聊天截图微信公众号 滴滴技术 发文k8s 官方文档滴滴官方微博账号 近两个月国内互联网产品“崩溃”事件2023-10-23 语雀崩溃2023-11-12 阿里云崩溃2023-11-27 滴滴崩溃2023-12-03 腾讯视频崩溃总结 我的感想 知乎文分析 最近连续加班&#xff0c;打车较…

黑猫带你学eMMC协议第31篇:什么是eMMC的驱动强度(Drive Strength)

本文依据eMMC JEDEC5.1及个人工作经验整理而成,如有错误请留言。 文章为个人辛苦整理,付费内容,已加入原创侵权保护,禁止私自转载。 文章所在专栏:《黑猫带你学:eMMC协议详解》 1 简介 首先要清楚: 内阻越大,驱动强度越小;内阻越小,驱动强度越大。 ECSD[185]可调节…

【ARM CoreLink 系列 8.2 -- SMMU 详细介绍-STE Entry 详细介绍 2】

请阅读【ARM CoreLink 文章专栏导读】 上篇文章:【ARM CoreLink 系列 8.1 – SMMU 详细介绍-STE Entry 详细介绍 1】 文章目录 ARM SMMU STE ENTRY1.1 STE Entry WORD[3]1.1.1 MemAttr1.1.2 MTCFG1.1.3 ALLOCCFG1.1.4 SHCFG1.1.5 NSCFG1.1.6 PRIVCFG

todesk连接ubuntu显示当前系统并无桌面环境,或无显示器,无法显示远程桌面,您需要自行安装X11桌面环境,或者使用终端文件功能

ToDesk远程遇到的问题如上图&#xff0c;换向日葵直接黑屏&#xff1b; 问题原因 截止发文时间&#xff0c;Todesk只支持X11协议&#xff0c;没有适配最新的Wayland协议&#xff0c;所以我们需要把窗口系统调整为X11才可以。 解决方法 修改配置文件&#xff0c;关闭wayland su…

【模电】基本共射放大电路的工作原理及波形分析

基本共射放大电路的工作原理及波形分析 在上图所示的基本放大电路中&#xff0c;静态时的 I B Q I\tiny BQ IBQ、 I C Q I\tiny CQ ICQ、 U C E Q U\tiny CEQ UCEQ如下图( b )、( c )中虚线所标注。 &#xff08; a &#xff09; u i 的波形&#xff08; b &#xff09; i B …

LeetCode:1038. 从二叉搜索树到更大和树(反向中序遍历 C++、Java)

目录 1038. 从二叉搜索树到更大和树 题目描述&#xff1a; 实现代码与解析&#xff1a; dfs 原理思路&#xff1a; 1038. 从二叉搜索树到更大和树 题目描述&#xff1a; 给定一个二叉搜索树 root (BST)&#xff0c;请将它的每个节点的值替换成树中大于或者等于该节点值的所…

JDK8新特性——Stream流

文章目录 一、Stream流体验二、Stream流的创建三、Stream流中间方法四、Stream流终究方法 Stream流&#xff08;也叫Stream API&#xff09;。它是从JDK8以后才有的一个新特性&#xff0c;是专业用于对集合或者数组进行便捷操作的 一、Stream流体验 需求&#xff1a;有一个Lis…