saturn java 热加载(一)

背景:

每次启用都要重启executor,在此之前还要确保该executor下没有任务在执行,很麻烦

 

方案:

将不常更换的公共包放在executor classpath下,仍然由saturn加载

将常变动的包放在executor类加载器可见范围外,使其不能加载

 

public class ProxyJdsJavaJob extends AbstractSaturnJavaJob {

    private static final Logger LOGGER = LoggerFactory.getLogger(ProxyJdsJavaJob.class);

    @Override
    public SaturnJobReturn handleJavaJob(final String jobName, final Integer shardItem, final String shardParam, final SaturnJobExecutionContext context) {

        LOGGER.info("saturn 传入 java job 文件路径及参数:{}", shardParam);
        AbstractJdsJavaJob abstractJdsJavaJob = null;
        try {
            String [] temp = shardParam.split(";");
            String dir = "file:"+temp[0];
            LOGGER.info("saturn 传入 java job 文件路径:{}", temp[0]);
            URL url = new URL(dir);
            URL[] urls2 = {url};

            MyUrlClassLoader myUrlClassLoader = new MyUrlClassLoader(urls2);
            LOGGER.info("saturn 传入 java job 类名:{}", temp[1]);
         //   Class CA = myUrlClassLoader.loadClass(temp[1]);
            Class CA = myUrlClassLoader.findClass(temp[1]);
            abstractJdsJavaJob = (AbstractJdsJavaJob)CA.newInstance();
            abstractJdsJavaJob.runJdsJob(jobName, shardItem, shardParam);
            LOGGER.info("success:{},{},{}", jobName, shardItem, shardParam);
            return new SaturnJobReturn(Common.createJobResString("", jobName, shardItem, shardParam, true));

        } catch (Exception e) {
            LOGGER.info("error:{},{},{}", jobName, shardItem, shardParam);
            LOGGER.error(ExceptionUtils.getStackTrace(e));
            new SendMailThread(new Date(), e, jobName, shardItem, shardParam).execute();
            throw new JdsFastJobException(e);
        } catch (Throwable throwable) {
            LOGGER.error(throwable.getMessage(), throwable);
            throw new JdsFastJobException("类加载器失败");
        } finally {
            if(abstractJdsJavaJob != null)
                abstractJdsJavaJob.release();
        }
    }


}

 

public class MyUrlClassLoader extends URLClassLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyUrlClassLoader.class);

    public MyUrlClassLoader(URL[] urls) {

        super(urls, Thread.currentThread().getContextClassLoader());
        // 父加载器不一定是系统类加载器,可能是saturn自定义加载器
        ClassLoader parent = Thread.currentThread().getContextClassLoader();
        LOGGER.info("parent class loader {}", parent);
    }

    @Override
    protected Class<?> findClass(final String name) throws ClassNotFoundException {
        return super.findClass(name);
    }
}

 

1. 如果待热加载jar包(后称为A)在executor类加载器可见范围内,必须使用findclass避开缓存

如果在可见范围外,loadclass与findclass都可以,但请注意:

findclass(A),A中引用B,那么jvm使用loadclass加载B,这就意味着,如果B在父加载器缓存中,则不会热加载

所以如果B也要热加载,则1)将B也放到主程序类加载可见范围外,2)在A中使用findclass避开主类加载器缓存,当然同时也打破双亲委派

 

2. 这里有个坑,executor的类加载器并不是AppClassLoader,日志显示

(MyUrlClassLoader.java:21) -> parent class loader com.vip.saturn.job.executor.JobClassLoader@5fd764ee

所以,自定义类加载器必须指定parent class loader为saturn的类加载器,而不是使用默认null作为参数,这将导致AppClassLoader作为父加载器,否则

AbstractJdsJavaJob是由saturn类加载器加载

而具体的被热加载的类继承AbstractJdsJavaJob,由自定义类加载器加载,缺省情况下,其parent为系统类加载器,导致自定义加载器和saturn类加载器变为平行关系,saturn加载的类,对我们要热加载的类不可见,从而抛出error

 

3. 永远要确保finally中的语句是安全的,因为如果它抛异常,将覆盖catch中所有代码

详细见:https://www.cnblogs.com/silyvin/p/9993458.html

 

4. 由第2点抛出的异常,catch Exception是catch不到的,详细见:https://www.cnblogs.com/silyvin/p/10274898.html

故使用了catch Throwable,成功抓取

 

 

[INFO] 2019-01-15 17:26:43 985 [jds.fast.job.ProxyJdsJavaJob] [Saturn-test_ProxyJdsJavaJob-24-thread-1] (ProxyJdsJavaJob.java:30) -> saturn 传入 java job 类名:saturn.DemoJob
[ERROR] 2019-01-15 17:26:43 987 [jds.fast.job.ProxyJdsJavaJob] [Saturn-test_ProxyJdsJavaJob-24-thread-1] (ProxyJdsJavaJob.java:45) -> jds/fast/job/AbstractJdsJavaJob
java.lang.NoClassDefFoundError: jds/fast/job/AbstractJdsJavaJob
        at java.lang.ClassLoader.defineClass1(Native Method)
        at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
        at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
        at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
        at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
        at jds.fast.job.MyUrlClassLoader.findClass(MyUrlClassLoader.java:17)
        at jds.fast.job.ProxyJdsJavaJob.handleJavaJob(ProxyJdsJavaJob.java:32)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.vip.saturn.job.java.SaturnJavaJob$1.internalCall(SaturnJavaJob.java:218)
        at com.vip.saturn.job.basic.AbstractSaturnJob$JobBusinessClassMethodCaller.call(AbstractSaturnJob.java:205)
        at com.vip.saturn.job.java.SaturnJavaJob.handleJavaJob(SaturnJavaJob.java:214)
        at com.vip.saturn.job.java.SaturnJavaJob.doExecution(SaturnJavaJob.java:204)
        at com.vip.saturn.job.basic.JavaShardingItemCallable.call(JavaShardingItemCallable.java:158)
        at com.vip.saturn.job.basic.ShardingItemFutureTask.call(ShardingItemFutureTask.java:88)
        at com.vip.saturn.job.basic.ShardingItemFutureTask.call(ShardingItemFutureTask.java:17)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.ClassNotFoundException: jds.fast.job.AbstractJdsJavaJob
        at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
        at jds.fast.job.MyUrlClassLoader.findClass(MyUrlClassLoader.java:17)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        ... 26 common frames omitted

 

 

5 7.13补充:

public class MyUrlClassLoader extends URLClassLoader {

    private static final Logger LOGGER = LoggerFactory.getLogger(MyUrlClassLoader.class);

    public MyUrlClassLoader(URL[] urls) {

        super(urls, Thread.currentThread().getContextClassLoader());
        // 父加载器不一定是系统类加载器,可能是saturn自定义加载器
        ClassLoader parent = Thread.currentThread().getContextClassLoader();
        LOGGER.info("parent class loader {}", parent);
        ClassLoader grandParent = parent.getParent();
        LOGGER.info("grand class loader {}", grandParent);
    }

    @Override
    protected Class<?> findClass(final String name) throws ClassNotFoundException {
        return super.findClass(name);
    }

    @Override
    protected void finalize() throws Throwable {
        LOGGER.info("回收MyUrlClassLoader");
        super.finalize();
    }
}

 

grandParent 居然是null,这也导致了与以premain方式注入监控的jmx_exporter所在的系统类加载器相互不可见:https://www.cnblogs.com/silyvin/articles/10600530.html

 

2019.12.26

这篇文章能干啥

1)任务隔离

2)热加载

3)加解密

4)任务状态统一记录

posted on 2019-01-15 23:49  silyvin  阅读(1072)  评论(0编辑  收藏  举报