如何快速从schedulerx2批量切换到XXl-job(并且双调度兼容)

如何急速从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

微信图片编辑_20231114095121.jpg

生成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;
    }

如:

发布上线,完成!!!

如(一把导入):

相关推荐
程序员鱼皮4 分钟前
学 Java 还是 Go 语言?这事儿很简单!
java·后端·计算机·程序员·开发·编程经验·自学编程
天蓝的那一角15 分钟前
你想要的Lambda第二弹
后端
JohnYan17 分钟前
Bun技术评估 - 06 Redis
redis·后端·bun
写bug写bug19 分钟前
Dubbo中SPI机制的实现原理和优势
java·后端·dubbo
刘白Live22 分钟前
【Java】Git的一些常用命令
git·后端
Barcke24 分钟前
AI赋能开发者工具:智能提示词编写与项目管理实践
前端·后端
攒了一袋星辰33 分钟前
Spring类型转换融入IOC生命周期
java·后端·spring
考虑考虑38 分钟前
Springboot3.4.x中的RestClient 和 RestTemplate
spring boot·后端·spring
Cxzzzzzzzzzz42 分钟前
Go语言堆内存管理
开发语言·后端·golang
Java永无止境1 小时前
Web后端基础:Maven基础
java·后端·maven