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;就不报错了…

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;一般也称“数据包”。 我们平常…

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

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

链表【2】

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

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;打车较…

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…

linux远程桌面管理工具(xrdp)、向日葵

Windows远程桌面 linux远程桌面 使用向日葵远程桌面&#xff08;手机端同理&#xff09; Windows远程桌面 微软自带Remote Desktop Connection Manager &#xff08;RDCMan&#xff09;远程控制管理软件介绍 远程桌面连接管理器 v2.93 linux远程桌面 Windows远程桌面Ubunt…

JVM 运行时内存(三)

Java 堆从 GC 的角度还可以细分为: 新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代。 1. 新生代 是用来存放新生的对象。一般占据堆的 1/3 空间。由于频繁创建对象&#xff0c;所以新生代会频繁触发MinorGC 进行垃圾回收。新生代又分为 Eden 区、ServivorFrom、…

Android Framework 电池提醒相关Dialog熄屏消失的问题

记录一下花了三四天干一天就能完成的需求的傻事。 说在前头&#xff0c;这篇文章记录了电池提醒dialog相关&#xff0c;弹出dialog且熄屏再亮屏dialog不会消失的代码&#xff0c;这篇废话比较多&#xff0c;看正常代码直接跳到代码3。 故事背景 需求要求添加非法电池的弹窗&a…

[多线程]阻塞队列和生产者消费者模型

目录 1.阻塞队列 1.1引言 1.2Java标准库中的阻塞队列 1.3自主通过Java代码实现一个阻塞队列(泛型实现) 2.生产者消费者模型 1.阻塞队列 1.1引言 阻塞队列是多线程部分一个重要的概念,它相比于一般队列,有两个特点: 1.线程是安全的 2.带有阻塞功能 1) 队列为空,出队列就会阻…

语义分割 DeepLab V1网络学习笔记 (附代码)

论文地址&#xff1a;https://arxiv.org/abs/1412.7062 代码地址&#xff1a;GitHub - TheLegendAli/DeepLab-Context 1.是什么&#xff1f; DeepLab V1是一种基于VGG模型的语义分割模型&#xff0c;它使用了空洞卷积和全连接条件随机&#xff08;CRF&#xff09;来提高分割…

JAVA-作业7-画一个笑脸

要求如题 代码如下&#xff1a; SmileFace01: import java.awt.Color; import java.awt.Graphics;import javax.swing.JPanel;public class SmileFace01 extends JPanel {Overrideprotected void paintComponent(Graphics g) {super.paintComponent(g);int width getWidth(…