如何快速从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;
    }

如:

发布上线,完成!!!

如(一把导入):

相关推荐
潘多编程18 分钟前
Spring Boot微服务架构设计与实战
spring boot·后端·微服务
2402_8575893624 分钟前
新闻推荐系统:Spring Boot框架详解
java·spring boot·后端
2401_8576226625 分钟前
新闻推荐系统:Spring Boot的可扩展性
java·spring boot·后端
Amagi.2 小时前
Spring中Bean的作用域
java·后端·spring
2402_857589362 小时前
Spring Boot新闻推荐系统设计与实现
java·spring boot·后端
J老熊2 小时前
Spring Cloud Netflix Eureka 注册中心讲解和案例示范
java·后端·spring·spring cloud·面试·eureka·系统架构
Benaso2 小时前
Rust 快速入门(一)
开发语言·后端·rust
sco52822 小时前
SpringBoot 集成 Ehcache 实现本地缓存
java·spring boot·后端
原机小子3 小时前
在线教育的未来:SpringBoot技术实现
java·spring boot·后端
吾日三省吾码3 小时前
详解JVM类加载机制
后端