文章目录
前言
本篇文章对xxl-job 执行器端启动过程进行介绍;
一、xxl-job 执行器端作用:
xxl-job执行器端是xxl-job分布式任务调度平台中的一个组件,主要负责接收并执行调度中心下发的任务。xxl-job执行器端的作用包括:
-
任务执行:执行器端接收调度中心下发的任务信息,根据任务配置执行具体的业务逻辑,完成任务的执行操作。
-
执行结果反馈:执行器端在完成任务执行后,将执行结果(成功、失败、异常等)反馈给调度中心,用于调度中心监控任务执行状态。
-
日志记录:执行器端负责记录任务执行的日志信息,包括执行结果、执行时间、执行耗时等,方便后续排查问题和统计分析。
-
监控任务执行:执行器端会定时向调度中心发送心跳信息,保持与调度中心的连接,同时接收调度中心的任务下发和监控指令。
-
弹性扩缩容:可以根据任务调度的负载情况自动扩展和缩减执行器端的数量,以达到资源的合理利用。
总的来说,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-执行器项目启动过程 进行介绍,其它初始化的工作在后续文章中进行介绍。