Java 代码热更方案

由于 jvm just in time 和支持解释运行和过热代码编译的特性,所以无需像 c/c++ 这种纯静态语言,需要先编译再执行。jvm 系的语言是支持脚本执行,只要编译成 class 供 jvm 识别,即可执行,执行过热之后(HotSpot)再编译成机器码,从而提高开发和运行效率的。

代码热更方案应用场景

  • 有状态服务的代码替换或 bugFix
    因为应用程序中保存了用户的信息或上下文信息,重启应用会导致内存中的数据丢失,所以代码热更非常适合这种类型的服务。

比如提供与用户对话的服务,内存中保存了一些用户对话的上下文信息(存储在 NoSQL 需要额外的成本),如果做 bugFix, 但是没有实现代码热更,此时不得不停机重启服务,导致服务一段时间不可用,且丢失用户上下文信息。

  • 配置或业务代码热更

ShowCode

  • Java

需要 javac 编译器或其它支持编译 java 的编译器。以下为封装了 Javac 的开源工具及其实现热更代码

JOOR

OpenHFT Java Runtime Compiler

简要的流程图如下

流程图

import net.openhft.compiler.CompilerUtils;
import org.joor.Reflect;

import java.util.concurrent.TimeUnit;

public class TestCompiler {

    // 单例,持有 volatile 修饰的 runnable, 需要其他线程(例如:监听脚本变更的线程)替换引用
    public static TestCompiler testCompiler = new TestCompiler();

    // 假设为业务代码
    public volatile Runnable bizRunnable = () -> System.out.println(this.getClass().getSimpleName());

    static String className = "com.eahau.example.compiler.TestCompilerRunnable";
    static String sourceCode = "package com.eahau.example.compiler;\n" +
            "\n" +
            "public class TestCompilerRunnable implements Runnable {\n" +
            "    @Override\n" +
            "    public void run() {\n" +
            "        TestCompiler.testCompiler.bizRunnable = () -> System.out.println(this.getClass().getSimpleName());\n" +
            "    }\n" +
            "}";
    /**
     * testJOOR.
     */
    static void testJoor() {
        Runnable runnable = Reflect.compile(className, sourceCode).create().get();
        runnable.run();
    }

    /**
     * test JRC: Java Runtime Compiler.
     */
    static void testJRC() throws Exception {
        Class<?> aClass = CompilerUtils.CACHED_COMPILER.loadFromJava(className, sourceCode);
        Object o = aClass.newInstance();
        if (o instanceof Runnable) {
            ((Runnable) o).run();
        }
    }

    static void bizStart() {
        // 模拟业务线程一直在处理业务
        new Thread(() -> {
            for (; ; ) {
                try {
                    testCompiler.bizRunnable.run();
                    TimeUnit.SECONDS.sleep(1L);
                } catch (InterruptedException ignore) {
                }
            }
        }).start();
    }

    public static void main(String[] args) throws Exception {
        bizStart();
        testJoor();
        testJRC();
    }
}
  • Groovy
    groovy 需要 GroovyClassLoader 进行加载,groovy 依赖 maven 仓库
import groovy.lang.GroovyClassLoader;

import java.util.concurrent.TimeUnit;

public class TestGroovy {
    public static TestGroovy testGroovy = new TestGroovy();
    public volatile Runnable bizRunnable = () -> System.out.println(this.getClass().getSimpleName());

    static void bizStart() {
        // 模拟业务线程一直在处理业务
        new Thread(() -> {
            for (; ; ) {
                try {
                    testGroovy.bizRunnable.run();
                    TimeUnit.SECONDS.sleep(1L);
                } catch (InterruptedException ignore) {
                }
            }
        }).start();
    }

    static void testGroovyCompiler() throws Exception {
        GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
        Class<?> parsedClass = groovyClassLoader.parseClass(
                "package com.eahau.example.compiler\n" +
                        "\n" +
                        "class TestGroovyRunnable implements Runnable {\n" +
                        "    @Override\n" +
                        "    void run() {\n" +
                        "        TestGroovy.testGroovy.bizRunnable = { System.out.println(this.getClass().simpleName) }\n" +
                        "    }\n" +
                        "}");
        Object o = parsedClass.newInstance();
        if (o instanceof Runnable) {
            ((Runnable) o).run();
        }
    }

    public static void main(String[] args) throws Exception {
        bizStart();
        testGroovyCompiler();
    }

}

写得比较简单,欢迎评论补充。如有错误,烦请指出。

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页