如何急速从schedulerx2批量切换到XXl-job(并且双调度兼容) 不管是几百个job,如果手动去配置和每个差异化修改代码就非常麻烦耗费时间
整体思路:
- 1 拒绝手动去配置job 思路:在schedulerx2后台导出job配置,解析配置生成xxl-job的配置导入
xxl-job
.xxl_job_info 表 - 2 拒绝动脑改写代码 思路:在原有schedulerx2的代码基础上加入几行固定代码即可,无脑粘贴就行,所有代码一支,操作不累,并且可以同时兼容schedulerx2和xxl-job调度
实现流程
一: 重写XxlJobSpringExecutor类,实现简单扩展,具体代码:
pom: com.xuxueli xxl-job-core 2.2.0
重写XxlJobSpringExecutor:
java
package com.wbcloudhealth.biz.mall.job.config;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import com.xxl.job.core.glue.GlueFactory;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.core.handler.impl.MethodJobHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class XxlJobSpringExecutorInner extends XxlJobSpringExecutor {
// ---------------------- applicationContext ----------------------
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@Override
public void afterSingletonsInstantiated() {
// init JobHandler Repository
/*initJobHandlerRepository(applicationContext);*/
// init JobHandler Repository (for method)
initJobHandlerMethodRepository(applicationContext);
// refresh GlueFactory
GlueFactory.refreshInstance(1);
// super start
try {
super.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private void initJobHandlerMethodRepository(ApplicationContext applicationContext) {
log.info("执行重写方法initJobHandlerMethodRepository");
if (applicationContext == null) {
return;
}
// init job handler from method
String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
for (String beanDefinitionName : beanDefinitionNames) {
Object bean = applicationContext.getBean(beanDefinitionName);
Map<Method, XxlJob> annotatedMethods = null; // referred to :org.springframework.context.event.EventListenerMethodProcessor.processBean
Map<Method, Object> annotatedMethodsBeanMap = new HashMap<>(); // referred to :org.springframework.context.event.EventListenerMethodProcessor.processBean
try {
annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
new MethodIntrospector.MetadataLookup<XxlJob>() {
@Override
public XxlJob inspect(Method method) {
annotatedMethodsBeanMap.put(method, bean);
return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);
}
});
} catch (Throwable ex) {
log.error("xxl-job method-jobhandler resolve error for bean[" + beanDefinitionName + "].", ex);
}
if (annotatedMethods==null || annotatedMethods.isEmpty()) {
continue;
}
for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) {
Method method = methodXxlJobEntry.getKey();
XxlJob xxlJob = methodXxlJobEntry.getValue();
if (xxlJob == null) {
continue;
}
// String name = xxlJob.value();
// String name = methodXxlJobEntry.getKey().getClass().getName();
String name = annotatedMethodsBeanMap.get(methodXxlJobEntry.getKey()).getClass().getName();
if (name.trim().length() == 0) {
throw new RuntimeException("xxl-job method-jobhandler name invalid, for[" + bean.getClass() + "#" + method.getName() + "] .");
}
if (loadJobHandler(name) != null) {
throw new RuntimeException("xxl-job jobhandler[" + name + "] naming conflicts.");
}
// execute method
if (!(method.getParameterTypes().length == 1 && method.getParameterTypes()[0].isAssignableFrom(String.class))) {
throw new RuntimeException("xxl-job method-jobhandler param-classtype invalid, for[" + bean.getClass() + "#" + method.getName() + "] , " +
"The correct method format like \" public ReturnT<String> execute(String param) \" .");
}
if (!method.getReturnType().isAssignableFrom(ReturnT.class)) {
throw new RuntimeException("xxl-job method-jobhandler return-classtype invalid, for[" + bean.getClass() + "#" + method.getName() + "] , " +
"The correct method format like \" public ReturnT<String> execute(String param) \" .");
}
method.setAccessible(true);
// init and destory
Method initMethod = null;
Method destroyMethod = null;
if (xxlJob.init().trim().length() > 0) {
try {
initMethod = bean.getClass().getDeclaredMethod(xxlJob.init());
initMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException("xxl-job method-jobhandler initMethod invalid, for[" + bean.getClass() + "#" + method.getName() + "] .");
}
}
if (xxlJob.destroy().trim().length() > 0) {
try {
destroyMethod = bean.getClass().getDeclaredMethod(xxlJob.destroy());
destroyMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
throw new RuntimeException("xxl-job method-jobhandler destroyMethod invalid, for[" + bean.getClass() + "#" + method.getName() + "] .");
}
}
// registry jobhandler
registJobHandler(name, new MethodJobHandler(bean, method, initMethod, destroyMethod));
}
}
}
}
二:增加config配置
这一步中 XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutorInner(); new的对象一定是 XxlJobSpringExecutorInner (需要创建自定义的这个对象)
kotlin
package com.wbcloudhealth.biz.mall.job.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
/**
* xxl-job config
*/
@Configuration
@Slf4j
public class XxlJobConfig {
@Value("${public.xxl.job.admin:http://xxl-job-svc}")
private String adminAddresses;
@Value("${public.xxl.job.executor.appname:}")
private String appName;
//@Value("${public.xxl.job.accessToken}")
private String accessToken;
@Value("${public.xxl.job.executor.logpath:/root/logs/xxl-job/jobhandler}")
private String logPath;
@Value("${public.xxl.job.executor.logretentiondays:7}")
private int logRetentionDays;
public XxlJobConfig() {
}
@Bean
public XxlJobSpringExecutor xxlJobExecutor(Environment environment) {
String appNameFromEnv = environment.getProperty("spring.application.name");
log.info("启动xxljob");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutorInner();
try {
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(StringUtils.isNoneBlank(appName) ? appName : appNameFromEnv);
//xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
} catch (Exception e) {
log.error("XXLjob启动异常", e);
}
return xxlJobSpringExecutor;
}
}
三: 从schedulerx导出job并且导入xxl-job
首先需要从schedulerx上导出job配置json
生成sql
java
package com.wbcloudhealth.biz.mall.job.config;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
public class SchedulerxConfig2XxlConfigTool {
public static void main(String[] args) {
//全局参数
String author = "system";//创建人
String alarm_email = "";//警告邮件通知人
String job_group = "";//组,自己去 select * from `xxl-job`.xxl_job_group order by id desc ; 查看
File file = new File("C:\\Users\\FosunHealth\\Downloads\\AppJobs-mall-mall.json");
JSONObject schedulerxConfigJson = JSONObject.parseObject(txt2String(file));
if (StringUtils.isBlank(job_group)) {
throw new NullPointerException("job_group 不能为空");
}
JSONArray jsonArray = schedulerxConfigJson.getJSONObject("content").getJSONArray("jobConfigInfo");
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject jsonObject = jsonArray.getJSONObject(i);
if ("java".equals(jsonObject.get("jobType"))) {
String className = jsonObject.getString("className");
String name = jsonObject.getString("name");
JSONObject paramMapJSONObject = jsonObject.getJSONObject("paramMap");
String timeExpression = paramMapJSONObject.getString("timeExpression");
String status = paramMapJSONObject.getString("status");//1 启用,0 禁用
String parameters = paramMapJSONObject.getString("parameters");//job参数
if (null == parameters) {
parameters = "";
}
String sql = "INSERT INTO `xxl-job`.xxl_job_info ( job_group, job_cron, job_desc, add_time, update_time, author, alarm_email,\n" +
" executor_route_strategy, executor_handler, executor_param, executor_block_strategy,\n" +
" executor_timeout, executor_fail_retry_count, glue_type, glue_source, glue_remark,\n" +
" glue_updatetime, child_jobid, trigger_status, trigger_last_time, trigger_next_time)\n" +
"VALUES (\n" +
" " + job_group + ",\n" +
" '" + timeExpression + "',\n" +
" '" + name + "',\n" +
" now(), now(),\n" +
" 'sys',\n" +
" '', 'FIRST',\n" +
" '" + className + "',\n" +
" '" + parameters + "', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '',\n" +
" 'GLUE代码初始化', now(), '', \n" +
" " + status + ",\n" +
" 0, 0);";
System.out.println(sql);
}
}
System.out.println("完成");
}
public static String txt2String(File file) {
StringBuilder result = new StringBuilder();
try {
// 构造一个BufferedReader类来读取文件
BufferedReader br = new BufferedReader(new FileReader(file));
String s = null;
// 使用readLine方法,一次读一行
while ((s = br.readLine()) != null) {
result.append(System.lineSeparator() + s);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return result.toString();
}
}
把生成的sql执行如:
四: 原有schedulerx的JavaProcessor所有实现类改下,在原有类中加入如下代码,复制粘贴即可(无脑粘贴)
scss
@XxlJob("使用XxlJobSpringExecutorInner,name后续再改")
public ReturnT<String> demoJobHandler(String param) throws Exception {
process(JobContext.newBuilder().setJobParameters(param).setInstanceParameters(param).build());
return ReturnT.SUCCESS;
}
如:
发布上线,完成!!!
如(一把导入):