由于 jvm just in time
和支持解释运行和过热代码编译的特性,所以无需像 c/c++
这种纯静态语言,需要先编译再执行。jvm 系的语言是支持脚本执行,只要编译成 class 供 jvm 识别,即可执行,执行过热之后(HotSpot)再编译成机器码,从而提高开发和运行效率的。
代码热更方案应用场景
- 有状态服务的代码替换或 bugFix
因为应用程序中保存了用户的信息或上下文信息,重启应用会导致内存中的数据丢失,所以代码热更非常适合这种类型的服务。
比如提供与用户对话的服务,内存中保存了一些用户对话的上下文信息(存储在 NoSQL 需要额外的成本),如果做 bugFix, 但是没有实现代码热更,此时不得不停机重启服务,导致服务一段时间不可用,且丢失用户上下文信息。
- 配置或业务代码热更
ShowCode
- Java
需要 javac 编译器或其它支持编译 java 的编译器。以下为封装了 Javac 的开源工具及其实现热更代码
简要的流程图如下
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();
}
}
写得比较简单,欢迎评论补充。如有错误,烦请指出。