游戏上线后难免会有功能性bug,这些bug很多只做一些小的改动即可修复。设想假如每次有bug修复之后,都要重启服务器,势必会导致部分玩家流失,对游戏产生不好的影响。在这个背景下,代码热更新还是很有必要的。这里,我们来讨论一种java代码热更新的实现。
代码热更-开源工具的实现
1.Apache Tomcat 实现:
Tomcat 动态 JSP 编译运行,这是一种阉割的热加载。Tomcat 采用ecj (eclise-jdt)组件动态编译jsp生成servlet类,servlet是有固定模板。相对于只改变方法,使用自定义JspLoader 重新创建一个实例的方式运行。普通类和Servlet 本身不行,如修改需要触发context reload (自动重启)
2.Eclipse实现
Eclipse 调试模式下的代码热更,由IDE 内部实现,实际使用的是java agent技术。
代码热更-javaAgent实现
JavaAgent 是JDK 1.5 以后引入的。java.lang.instrument
Agent技术,解决以下两个问题:
1. Java程序启动之前修改类,即main()方法执行前
通过指令java -javaagent:<jarpath>[=<选项>] 触发以下接口
public static void premain(String agentArgs, Instrumentation inst)
2. Java程序运行时修改类
通过新启一个程序获取目标
VirtualMachine virtualmachine = VirtualMachine.attach(jvm_pid);
virtualmachine.loadAgent(agentJarPath, agentParam); 触发下面接口
public static void agentmain (String agentArgs, Instrumentation inst)
作为agent jar 文件需要定义 MANIFEST.MF 属性
Manifest-Version: 1.0
Agent-Class: com.cyou.xyj.agent.ReloadAccountAgent
Premain-Class: com.cyou.xyj.agent.ReloadAccountAgent
Can-Redine-Classes: true
Can-Retransform-Classes: true
Instrumentation 类
void addTransformer(ClassFileTransformer transformer, boolean canRetransform)
boolean removeTransformer(ClassFileTransformer transformer)
void redefineClasses(ClassDefinition... definitions) // 重定义类
boolean isModifiableClass (Class<?> theClass);
void appendToBootstrapClassLoaderSearch(JarFile jarfile)
// 应用。运行时远程加载类库,核心类库不公开
// 运行时扩展功能 etc..
…
ClassFileTransformer 类,唯一接口
// loader 类加载器
// classBeingRedefined 是否允许重定义
// classfileBuffer 原类的字节码
byte[]transform(ClassLoader loader,String className,Class<?> classBeingRedefined,ProtectionDomain protectionDomain,byte[] classfileBuffer)
类转化的实际逻辑就是创建一个ClassFileTransformer实例,并覆写此方法
Account类中修改后的方法被修改。代码热更新完成
通过 –XX:+TraceClassLoading 输出如下