原理篇-- 定时任务xxl-job-执行器项目启动过程(1)

本文详细介绍了XXL-JOB执行器的作用,如任务执行、结果反馈、日志记录和监控,涉及源码中的bean注入机制,特别是使用SpringFramework的MethodIntrospector.selectMethods方法。文章重点展示了执行器bean的初始化过程和如何扫描带有特定注解的方法。
摘要由CSDN通过智能技术生成


前言

本篇文章对xxl-job 执行器端启动过程进行介绍;


一、xxl-job 执行器端作用:

xxl-job执行器端是xxl-job分布式任务调度平台中的一个组件,主要负责接收并执行调度中心下发的任务。xxl-job执行器端的作用包括:

  1. 任务执行:执行器端接收调度中心下发的任务信息,根据任务配置执行具体的业务逻辑,完成任务的执行操作。

  2. 执行结果反馈:执行器端在完成任务执行后,将执行结果(成功、失败、异常等)反馈给调度中心,用于调度中心监控任务执行状态。

  3. 日志记录:执行器端负责记录任务执行的日志信息,包括执行结果、执行时间、执行耗时等,方便后续排查问题和统计分析。

  4. 监控任务执行:执行器端会定时向调度中心发送心跳信息,保持与调度中心的连接,同时接收调度中心的任务下发和监控指令。

  5. 弹性扩缩容:可以根据任务调度的负载情况自动扩展和缩减执行器端的数量,以达到资源的合理利用。

总的来说,xxl-job执行器端是xxl-job平台的关键组件之一,负责执行任务、记录日志、反馈执行结果等操作,保证任务能够按时正确执行,并与调度中心实现有效的通信与协作。

二、源码内容:

执行器bean的注入主要完成对application.properties 内容的读取,并初始化XxlJobSpringExecutor 的bean;

2.1 执行器bean的注入:

@Configuration
public class XxlJobConfig {
    private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
	// 服务端的地址
    @Value("${xxl.job.admin.addresses}")
    private String adminAddresses;
	// 服务端的访问令牌
    @Value("${xxl.job.accessToken}")
    private String accessToken;
	// 执行器的名称
    @Value("${xxl.job.executor.appname}")
    private String appname;
	// 执行器的地址
    @Value("${xxl.job.executor.address}")
    private String address;
	// 执行器的地址ip
    @Value("${xxl.job.executor.ip}")
    private String ip;
	// 执行器的地址端口
    @Value("${xxl.job.executor.port}")
    private int port;
	// 日志文件保存路径
    @Value("${xxl.job.executor.logpath}")
    private String logPath;
	// 日志文件保存天数
    @Value("${xxl.job.executor.logretentiondays}")
    private int logRetentionDays;

	// 实例化 XxlJobSpringExecutor  对象
    @Bean
    public XxlJobSpringExecutor xxlJobExecutor() {
        logger.info(">>>>>>>>>>> xxl-job config init.");
        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
        xxlJobSpringExecutor.setAppname(appname);
        xxlJobSpringExecutor.setAddress(address);
        xxlJobSpringExecutor.setIp(ip);
        xxlJobSpringExecutor.setPort(port);
        xxlJobSpringExecutor.setAccessToken(accessToken);
        xxlJobSpringExecutor.setLogPath(logPath);
        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);

        return xxlJobSpringExecutor;
    }

    /**
     * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP;
     *
     *      1、引入依赖:
     *          <dependency>
     *             <groupId>org.springframework.cloud</groupId>
     *             <artifactId>spring-cloud-commons</artifactId>
     *             <version>${version}</version>
     *         </dependency>
     *
     *      2、配置文件,或者容器启动变量
     *          spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.'
     *
     *      3、获取IP
     *          String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress();
     */


}

2.2 XxlJobSpringExecutor 执行器bean:

XxlJobSpringExecutor 对象内部继续进行初始化的工作;

/**
* 实现ApplicationContextAware 在容器创建后可以调用 setApplicationContext 对容器上下文赋值
* 实现 SmartInitializingSingleton SmartInitializingSingleton 是 Spring Framework 提供的一个接口,用于在所有单例 bean 实例化后提供一个回调方法 afterSingletonsInstantiated,以便在容器初始化阶段执行特定的初始化逻辑
**/
public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationContextAware, SmartInitializingSingleton, DisposableBean {
    private static final Logger logger = LoggerFactory.getLogger(XxlJobSpringExecutor.class);


    // start
    @Override
    public void afterSingletonsInstantiated() {

        // init JobHandler Repository
        /*initJobHandlerRepository(applicationContext);*/

        // init JobHandler Repository (for method)
        //  获取xxl job 的注解类及其方法 并放入到map 集合中
        initJobHandlerMethodRepository(applicationContext);

        // refresh GlueFactory 
        // 管理任务的执行代码脚本的工厂类 实例化
        GlueFactory.refreshInstance(1);

        // super start
        try {
        	// 调用父类  XxlJobExecutor 继续完成初始化工作
            super.start();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // destroy
    @Override
    public void destroy() {
        super.destroy();
    }


    /*private void initJobHandlerRepository(ApplicationContext applicationContext) {
        if (applicationContext == null) {
            return;
        }

        // init job handler action
        Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(JobHandler.class);

        if (serviceBeanMap != null && serviceBeanMap.size() > 0) {
            for (Object serviceBean : serviceBeanMap.values()) {
                if (serviceBean instanceof IJobHandler) {
                    String name = serviceBean.getClass().getAnnotation(JobHandler.class).value();
                    IJobHandler handler = (IJobHandler) serviceBean;
                    if (loadJobHandler(name) != null) {
                        throw new RuntimeException("xxl-job jobhandler[" + name + "] naming conflicts.");
                    }
                    registJobHandler(name, handler);
                }
            }
        }
    }*/
	//  获取xxl job 的注解类及其方法 并放入到map 集合中
    private void initJobHandlerMethodRepository(ApplicationContext applicationContext) {
        if (applicationContext == null) {
        	// 容器为空,则直接返回
            return;
        }
        // init job handler from method
        // 获取容器中所有的bean 对象
        String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
        for (String beanDefinitionName : beanDefinitionNames) {
        	// 遍历bean 对象 获取到bean 的定义
            Object bean = applicationContext.getBean(beanDefinitionName);

            Map<Method, XxlJob> annotatedMethods = null;   // referred to :org.springframework.context.event.EventListenerMethodProcessor.processBean
            try {
            	// 根据bean 定义 筛选出标识有 @XxlJob 注解的所有方法 
                annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
                        new MethodIntrospector.MetadataLookup<XxlJob>() {
                            @Override
                            public XxlJob inspect(Method method) {
                                return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);
                            }
                        });
            } catch (Throwable ex) {
                logger.error("xxl-job method-jobhandler resolve error for bean[" + beanDefinitionName + "].", ex);
            }
           
            if (annotatedMethods==null || annotatedMethods.isEmpty()) {
             // 如果没有可以执行的任务 则继续遍历下一个bean
                continue;
            }

            for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) {
            	// 遍历改bean 下的所有被  @XxlJob 注解标注的方法
            	// key :方法的信息
                Method executeMethod = methodXxlJobEntry.getKey();
                // 获取主机的信息
                XxlJob xxlJob = methodXxlJobEntry.getValue();
                // regist 进行注册 注册内容放到后续 XxlJobExecutor 内容进行介绍 
                // 这里进行下简单介绍 最终会将方法信息放入到 ConcurrentMap<String, IJobHandler> jobHandlerRepository 中
                // key 为定义的   @XxlJob("demoJobHandler")  “demoJobHandler”,value:为封装方法信息的 IJobHandler 对象
                // 在后续 执行器项目在执行任务时 会通过 key 获取到方法的信息,然后通过反射进行方法的调用从而完成任务的执行
                registJobHandler(xxlJob, bean, executeMethod);
            }
        }
    }

    // ---------------------- applicationContext ----------------------
    private static ApplicationContext applicationContext;
	// 容器上下文注册
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        XxlJobSpringExecutor.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

}

三、扩展:

3.1 MethodIntrospector.selectMethods :

MethodIntrospector.selectMethods 是 Spring Framework 中的一个工具方法,用于根据指定的条件选择类中符合条件的方法。该方法的作用是根据一些规则或条件,从指定的类中选择出符合条件的方法,返回一个被选中方法所组成的 Map。

具体来说,MethodIntrospector.selectMethods 方法接受一个目标类的 Class 对象、一个筛选条件以及一些其他参数,通过这些参数指定要筛选的方法条件。方法会遍历目标类中的所有方法,根据筛选条件判断方法是否符合要求,将符合条件的方法保存到一个 Map 中,并返回给调用者。

一般情况下,开发者可以使用MethodIntrospector.selectMethods方法来实现一些自定义功能,例如扫描类中的方法,根据一些条件选择出特定的方法进行后续处理,或者进行方法的过滤等操作。这样的方式可以帮助开发者更方便地处理类中的方法,根据条件来选择符合要求的方法进行进一步处理。

Demo: 获取带有注解标识的类中所有的方法:


import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

public class MethodSelectorDemo {

    public static void main(String[] args) {
        // 定义一个目标类
        Class<MyClass> clazz = MyClass.class;

        // 使用MethodIntrospector.selectMethods方法,选择出不带参数的方法
        Set<Method> selectedMethods = MethodIntrospector.selectMethods(clazz,
                new ReflectionUtils.MethodFilter() {
                    @Override
                    public boolean matches(Method method) {
                        return AnnotationUtils.findAnnotation(method, MyAnnotation.class) != null;
                    }
                });
        // 遍历选择出的方法
        for (Method selectedMethod : selectedMethods) {
            System.out.println("Method with annotation: " + selectedMethod.getName());
        }

        Map<Method, MyAnnotation> annotatedMethods = MethodIntrospector.selectMethods(clazz,
                new MethodIntrospector.MetadataLookup<MyAnnotation>() {
                    @Override
                    public MyAnnotation inspect(Method method) {
                        return AnnotatedElementUtils.findMergedAnnotation(method, MyAnnotation.class);
                    }
                });

        for (Map.Entry<Method, MyAnnotation> methodMyAnnotationEntry : annotatedMethods.entrySet()) {
            System.out.println("Method with annotation: " +  methodMyAnnotationEntry.getKey().getName());
        }


    }

    static class MyClass {
        @MyAnnotation("test")
        public void doSomething() {
            // do something
        }

        public void doAnotherThing(String param) {
            // do another thing
        }
    }
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    public @interface MyAnnotation {

        /**
         * jobhandler name
         */
        String value();

        /**
         * init handler, invoked when JobThread init
         */
        String init() default "";

        /**
         * destroy handler, invoked when JobThread destroy
         */
        String destroy() default "";
    }
}


总结

本文对xl-job-执行器项目启动过程 进行介绍,其它初始化的工作在后续文章中进行介绍。

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值