聊聊PowerJob的ProcessorLoader

本文主要研究一下PowerJob的ProcessorLoader

ProcessorLoader

tech/powerjob/worker/processor/ProcessorLoader.java

public interface ProcessorLoader {

    ProcessorBean load(ProcessorDefinition definition);
}

ProcessorLoader定义了load方法,用于根据ProcessorDefinition加载ProcessorBean

ProcessorDefinition

tech/powerjob/worker/extension/processor/ProcessorDefinition.java

@Getter
@Setter
@ToString
@Accessors(chain = true)
public class ProcessorDefinition implements Serializable {

    /**
     * 后台配置的处理器类型
     */
    private String processorType;
    /**
     * 后台配置的处理器信息
     */
    private String processorInfo;

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        ProcessorDefinition that = (ProcessorDefinition) o;
        return Objects.equals(processorType, that.processorType) && Objects.equals(processorInfo, that.processorInfo);
    }

    @Override
    public int hashCode() {
        return Objects.hash(processorType, processorInfo);
    }
}

ProcessorDefinition定义了processorType及processorInfo两个属性

ProcessorBean

tech/powerjob/worker/extension/processor/ProcessorBean.java

@Getter
@Setter
@Accessors(chain = true)
public class ProcessorBean {

    /**
     * 真正用来执行逻辑的处理器对象
     */
    private transient BasicProcessor processor;
    /**
     * 加载该处理器对象的 classLoader,可空,空则使用 {@link Object#getClass()#getClassLoader() 代替}
     */
    private transient ClassLoader classLoader;

}

ProcessorBean定义了BasicProcessor及ClassLoader两个属性

PowerJobProcessorLoader

tech/powerjob/worker/processor/PowerJobProcessorLoader.java

@Slf4j
public class PowerJobProcessorLoader implements ProcessorLoader {

    private final List<ProcessorFactory> processorFactoryList;
    private final Map<ProcessorDefinition, ProcessorBean> def2Bean = new ConcurrentHashMap<>(128);

    public PowerJobProcessorLoader(List<ProcessorFactory> processorFactoryList) {
        this.processorFactoryList = processorFactoryList;
    }

    @Override
    public ProcessorBean load(ProcessorDefinition definition) {
        return def2Bean.computeIfAbsent(definition, ignore -> {
            final String processorType = definition.getProcessorType();
            log.info("[ProcessorFactory] start to load Processor: {}", definition);
            for (ProcessorFactory pf : processorFactoryList) {
                final String pfName = pf.getClass().getSimpleName();
                if (!Optional.ofNullable(pf.supportTypes()).orElse(Collections.emptySet()).contains(processorType)) {
                    log.info("[ProcessorFactory] [{}] can't load type={}, skip!", pfName, processorType);
                    continue;
                }
                log.info("[ProcessorFactory] [{}] try to load processor: {}", pfName, definition);
                try {
                    ProcessorBean processorBean = pf.build(definition);
                    if (processorBean != null) {
                        log.info("[ProcessorFactory] [{}] load processor successfully: {}", pfName, definition);
                        return processorBean;
                    }
                } catch (Throwable t) {
                    log.error("[ProcessorFactory] [{}] load processor failed: {}", pfName, definition, t);
                }
            }
            throw new PowerJobException("fetch Processor failed, please check your processorType and processorInfo config");
        });
    }
}

PowerJobProcessorLoader实现了ProcessorLoader接口,其构造器要求传入processorFactoryList,它还定义了def2Bean,用于维护ProcessorDefinition与ProcessorBean的关系;其load方法使用ConcurrentHashMap的computeIfAbsent,将加载好的ProcessorBean放入到def2Bean;其加载过程为遍历processorFactoryList,找到支持该processorType的ProcessorFactory,然后执行其build方法进行构造

ProcessorFactory

tech/powerjob/worker/extension/processor/ProcessorFactory.java

public interface ProcessorFactory {

    /**
     * 支持的处理器类型,类型不匹配则跳过该 ProcessorFactory 的加载逻辑
     * 对应的是控制台的'处理器类型' TAB,不做任何定制的情况下,取值范围为 {@link ProcessorType#name()}
     * @return 支持的处理器类型
     */
    Set<String> supportTypes();

    /**
     * 根据处理器定义构建处理器对象
     * 注意:Processor 为单例对象,即 PowerJob 对每一个 ProcessorBean 只调用一次 build 方法
     * @param processorDefinition 处理器定义
     * @return null or ProcessorBean
     */
    ProcessorBean build(ProcessorDefinition processorDefinition);
}

ProcessorFactory接口定义了supportTypes、build方法;它有四个实现类,其中BuiltInSpringProcessorFactory及BuildInSpringMethodProcessorFactory继承自AbstractBuildInSpringProcessorFactory,另外两个为BuiltInDefaultProcessorFactory、JarContainerProcessorFactory

BuiltInDefaultProcessorFactory

tech/powerjob/worker/processor/impl/BuiltInDefaultProcessorFactory.java

@Slf4j
public class BuiltInDefaultProcessorFactory implements ProcessorFactory {

    @Override
    public Set<String> supportTypes() {
        return Sets.newHashSet(ProcessorType.BUILT_IN.name());
    }

    @Override
    public ProcessorBean build(ProcessorDefinition processorDefinition) {

        String className = processorDefinition.getProcessorInfo();

        try {
            Class<?> clz = Class.forName(className);
            BasicProcessor basicProcessor = (BasicProcessor) clz.getDeclaredConstructor().newInstance();
            return new ProcessorBean()
                    .setProcessor(basicProcessor)
                    .setClassLoader(basicProcessor.getClass().getClassLoader());
        }catch (Exception e) {
            log.warn("[ProcessorFactory] load local Processor(className = {}) failed.", className, e);
        }
        return null;
    }
}

BuiltInDefaultProcessorFactory是默认的处理器工厂,通过全限定类名加载处理器,但没有IOC功能

JarContainerProcessorFactory

tech/powerjob/worker/processor/impl/JarContainerProcessorFactory.java

@Slf4j
public class JarContainerProcessorFactory implements ProcessorFactory {

    private final WorkerRuntime workerRuntime;

    public JarContainerProcessorFactory(WorkerRuntime workerRuntime) {
        this.workerRuntime = workerRuntime;
    }

    @Override
    public Set<String> supportTypes() {
        return Sets.newHashSet(ProcessorType.EXTERNAL.name());
    }

    @Override
    public ProcessorBean build(ProcessorDefinition processorDefinition) {

        String processorInfo = processorDefinition.getProcessorInfo();
        String[] split = processorInfo.split("#");
        String containerName = split[0];
        String className = split[1];

        log.info("[ProcessorFactory] try to load processor({}) in container({})", className, containerName);

        OmsContainer omsContainer = OmsContainerFactory.fetchContainer(Long.valueOf(containerName), workerRuntime);
        if (omsContainer != null) {
            return new ProcessorBean()
                    .setProcessor(omsContainer.getProcessor(className))
                    .setClassLoader(omsContainer.getContainerClassLoader());
        } else {
            log.warn("[ProcessorFactory] load container failed. processor info : {}", processorInfo);
        }
        return null;
    }
}

JarContainerProcessorFactory主要是通过OmsContainer来加载ProcessorBean

AbstractBuildInSpringProcessorFactory

tech/powerjob/worker/processor/impl/AbstractBuildInSpringProcessorFactory.java

@Slf4j
public abstract class AbstractBuildInSpringProcessorFactory implements ProcessorFactory {

    protected final ApplicationContext applicationContext;

    protected AbstractBuildInSpringProcessorFactory(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public Set<String> supportTypes() {
        return Sets.newHashSet(ProcessorType.BUILT_IN.name());
    }

    protected boolean checkCanLoad() {
        try {
            ApplicationContext.class.getClassLoader();
            return applicationContext != null;
        } catch (Throwable ignore) {
        }
        return false;
    }


    @SuppressWarnings("unchecked")
    protected static <T> T getBean(String className, ApplicationContext ctx) throws Exception {

        // 0. 尝试直接用 Bean 名称加载
        try {
            final Object bean = ctx.getBean(className);
            if (bean != null) {
                return (T) bean;
            }
        } catch (Exception ignore) {
        }

        // 1. ClassLoader 存在,则直接使用 clz 加载
        ClassLoader classLoader = ctx.getClassLoader();
        if (classLoader != null) {
            return (T) ctx.getBean(classLoader.loadClass(className));
        }
        // 2. ClassLoader 不存在(系统类加载器不可见),尝试用类名称小写加载
        String[] split = className.split("\\.");
        String beanName = split[split.length - 1];
        // 小写转大写
        char[] cs = beanName.toCharArray();
        cs[0] += 32;
        String beanName0 = String.valueOf(cs);
        log.warn("[SpringUtils] can't get ClassLoader from context[{}], try to load by beanName:{}", ctx, beanName0);
        return (T) ctx.getBean(beanName0);
    }
}

AbstractBuildInSpringProcessorFactory是两个spring相关ProcessorFactory的抽象类,它使用spring的ApplicationContext来加载

BuiltInSpringProcessorFactory

tech/powerjob/worker/processor/impl/BuiltInSpringProcessorFactory.java

@Slf4j
public class BuiltInSpringProcessorFactory extends AbstractBuildInSpringProcessorFactory {


    public BuiltInSpringProcessorFactory(ApplicationContext applicationContext) {
        super(applicationContext);
    }

    @Override
    public ProcessorBean build(ProcessorDefinition processorDefinition) {

        try {
            boolean canLoad = checkCanLoad();
            if (!canLoad) {
                log.info("[ProcessorFactory] can't find Spring env, this processor can't load by 'BuiltInSpringProcessorFactory'");
                return null;
            }
            String processorInfo = processorDefinition.getProcessorInfo();
            //用于区分方法级别的参数
            if (processorInfo.contains("#")) {
                return null;
            }
            BasicProcessor basicProcessor = getBean(processorInfo, applicationContext);
            return new ProcessorBean()
                    .setProcessor(basicProcessor)
                    .setClassLoader(basicProcessor.getClass().getClassLoader());
        } catch (NoSuchBeanDefinitionException ignore) {
            log.warn("[ProcessorFactory] can't find the processor in SPRING");
        } catch (Throwable t) {
            log.warn("[ProcessorFactory] load by BuiltInSpringProcessorFactory failed. If you are using Spring, make sure this bean was managed by Spring", t);
        }

        return null;
    }
}

BuiltInSpringProcessorFactory通过ApplicationContext加载spring相关的Bean,但它不处理processorInfo包含#的processorDefinition

BuildInSpringMethodProcessorFactory

tech/powerjob/worker/processor/impl/BuildInSpringMethodProcessorFactory.java

@Slf4j
public class BuildInSpringMethodProcessorFactory extends AbstractBuildInSpringProcessorFactory {

    private static final List<String> jobHandlerRepository = new LinkedList<>();

    private final static String DELIMITER = "#";


    public BuildInSpringMethodProcessorFactory(ApplicationContext applicationContext) {
        super(applicationContext);
    }


    @Override
    public ProcessorBean build(ProcessorDefinition processorDefinition) {
        try {
            boolean canLoad = checkCanLoad();
            if (!canLoad) {
                log.info("[ProcessorFactory] can't find Spring env, this processor can't load by 'BuildInSpringMethodProcessorFactory'");
                return null;
            }
            String processorInfo = processorDefinition.getProcessorInfo();
            if (!processorInfo.contains(DELIMITER)) {
                log.info("[ProcessorFactory] can't parse processorDefinition, this processor can't load by 'BuildInSpringMethodProcessorFactory'");
                return null;
            }
            String[] split = processorInfo.split(DELIMITER);
            String methodName = split[1];
            String className = split[0];
            Object bean = getBean(className,applicationContext);
            Method[] methods = bean.getClass().getDeclaredMethods();
            for (Method method : methods) {
                PowerJobHandler powerJob = method.getAnnotation(PowerJobHandler.class);
                if (powerJob == null) {
                    continue;
                }
                String name = powerJob.name();
                //匹配到和页面定义相同的methodName
                if (!name.equals(methodName)) {
                    continue;
                }
                if (name.trim().length() == 0) {
                    throw new RuntimeException("method-jobhandler name invalid, for[" + bean.getClass() + "#" + method.getName() + "] .");
                }
                if (containsJobHandler(name)) {
                    throw new RuntimeException("jobhandler[" + name + "] naming conflicts.");
                }
                method.setAccessible(true);
                registerJobHandler(methodName);

                MethodBasicProcessor processor = new MethodBasicProcessor(bean, method);
                return new ProcessorBean()
                        .setProcessor(processor)
                        .setClassLoader(processor.getClass().getClassLoader());
            }
        } catch (NoSuchBeanDefinitionException ignore) {
            log.warn("[ProcessorFactory] can't find the processor in SPRING");
        } catch (Throwable t) {
            log.warn("[ProcessorFactory] load by BuiltInSpringProcessorFactory failed. If you are using Spring, make sure this bean was managed by Spring", t);
        }
        return null;
    }


    public static void registerJobHandler(String name) {
        jobHandlerRepository.add(name);
    }


    private boolean containsJobHandler(String name) {
        return jobHandlerRepository.contains(name);
    }
}

BuildInSpringMethodProcessorFactory专门用于processorInfo包含#的processorDefinition,它会遍历指定class的methods,找到方法上标注有@PowerJobHandler注解且方法名一致的method,注册到jobHandlerRepository,其创建的是MethodBasicProcessor

小结

PowerJob的ProcessorLoader定义了load方法,用于根据ProcessorDefinition加载ProcessorBean;PowerJobProcessorLoader实现了ProcessorLoader接口,它会遍历processorFactoryList,找到支持该processorType的ProcessorFactory,然后执行其build方法进行构造;ProcessorFactory接口定义了supportTypes、build方法;它有四个实现类,其中BuiltInSpringProcessorFactory及BuildInSpringMethodProcessorFactory继承自AbstractBuildInSpringProcessorFactory,另外两个为BuiltInDefaultProcessorFactory、JarContainerProcessorFactory。

PowerJob是全新一代分布式调度与计算框架,支持CRON、API、固定频率、固定延迟等调度策略,提供工作流来编排任务解决依赖关系,使用简单,功能强大,文档齐全,能让您轻松完成作业的调度与繁杂任务的分布式计算。 PowerJob特点: 使用简单:提供前端Web界面,允许开发者可视化地完成调度任务的管理(增、删、改、查)、任务运行状态监控和运行日志查看等功能。 定时策略完善:支持CRON表达式、固定频率、固定延迟和API四种定时调度策略。 执行模式丰富:支持单机、广播、Map、MapReduce四种执行模式,其中Map/MapReduce处理器能使开发者寥寥数行代码便获得集群分布式计算的能力。 DAG工作流支持:支持在线配置任务依赖关系,可视化得对任务进行编排,同时还支持上下游任务间的数据传递 执行器支持广泛:支持Spring Bean、内置/外置Java类、Shell、Python等处理器,应用范围广。 运维便捷:支持在线日志功能,执行器产生的日志可以在前端控制台页面实时显示,降低debug成本,极大地提高开发效率。 依赖精简:最小仅依赖关系型数据库(MySQL/Oracle/MS SQLServer...),扩展依赖为MongoDB(用于存储庞大的在线日志)。 高可用&高性能:调度服务器经过精心设计,一改其他调度框架基于数据库锁的策略,实现了无锁化调度。部署多个调度服务器可以同时实现高可用和性能的提升(支持无限的水平扩展)。 故障转移与恢复:任务执行失败后,可根据配置的重试策略完成重试,只要执行器集群有足够的计算节点,任务就能顺利完成。 PowerJob适用场景: 有定时执行需求的业务场景:如每天凌晨全量同步数据、生成业务报表等。 有需要全部机器一同执行的业务场景:如使用广播执行模式清理集群日志。 有需要分布式处理的业务场景:比如需要更新一大批数据,单机执行耗时非常长,可以使用Map/MapReduce处理器完成任务的分发,调动整个集群加速计算。 有需要延迟执行某些任务的业务场景:比如订单过期处理等。     PowerJob 更新日志: v4.0.1 Features 支持 PostgreSQL 强化前端控制台,新增 tag、上次在线时间等 worker 信息,便于排查无法连接的问题。 BugFix 修复 server 集群选主问题 修复当没有 worker 连接到 server 时出现的 NPE 问题 修复前端控制台错误显示 worker 列表的问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值