用于处理线上的一些逻辑bug的利器,这样,就不会为了一些几行代码的错误导致的bug,需要重新发版本重启服务器,而严重影响在线活跃,收入了。
1.实现一个代理类:
package com.lingyu.common.hotcode; import java.lang.instrument.ClassDefinition; import java.lang.instrument.Instrumentation; /** * java 代码热更新 */ public class HotCodeAgent { private static final Logger logger = LogManager.getLogger(HotCodeAgent.class); public static Instrumentation INST; /** JVM 首先尝试在代理类上调用以下方法 */ public static void premain(String agentArgs, Instrumentation inst) { INST = inst; } /** * 重新定义可能会改变方法体,常量池和属性。重定义不能添加,删除或重命名字段或方法,更改方法的签名或更改继承。这些限制可能在将来的版本中解除。 * 类文件字节不会被检查,验证和安装,直到应用转换为止,如果结果字节错误,则此方法将抛出异常。 如果此方法抛出异常,则不会重新定义任何类。 */ public static void reload(Class<?> clazz, byte[] data) { try { ClassDefinition classDefinition = new ClassDefinition(clazz, data); INST.redefineClasses(classDefinition); } catch (Throwable e) { logger.error(e.getMessage(), e); } } }
2.在game-common的build.xml里增加节点:
<manifest file="MANIFEST.MF"> <attribute name="Built-By" value="${user.name}" /> <attribute name="Built-Date" value="${buildTime}" /> <attribute name="Implementation-Version" value="${branch}-${svn}-${version}" /> <attribute name="Premain-Class" value="com.lingyu.common.hotcode.HotCodeAgent" /> <attribute name="Can-Redefine-Classes" value="true" /> </manifest>
3.在启动参数中加入参数 java -javaagent:lib/game-common.jar -server -Xms1024m -Xmx1024m -jar game.jar
游戏服务器启动的时候 会根据game-common.jar里的manifest.mf文件查找premain-class调用premain方法 项目保留Instrumentation的引用。
4. 通过后台上传class文件 更新到线上 建议 先在内网环境测试,一般只热更新逻辑代码。
class文件-->通过后台上传-->把class字节数据分发给线上,解析,重新加载类定义。(该画个图)
public void handleHotCode(String message) { // TODO Auto-generated method stub HotCodeInfo info = JSON.parseObject(message,HotCodeInfo.class); Class<?> clazz = null; try { //完整的包名类路径直接获取到已经加载的类 if(StringUtils.isNotEmpty(info.getReplaceName())){ clazz = Class.forName(info.getReplaceName()); } else{ //直接根据spring的接口获取对应的类 clazz = GameServerContext.getBean(info.getName()).getClass(); } HotCodeAgent.reload(clazz,info.getData()); } catch (Exception e) { logger.error(e.getMessage(),e); } }
最近遇到个问题,有没有遇到过热加载时有内部类或者匿名内部类的类 ,加载的时候没法成功。抛出的异常为:
2018-12-19 10:36:16.000 ERROR [Message SubScribe Monitor][HotCodeAgent] - class redefinition failed: attempted to add a method java.lang.UnsupportedOperationException: class redefinition failed: attempted to add a method at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method) at sun.instrument.InstrumentationImpl.redefineClasses(InstrumentationImpl.java:170) at com.lingyu.common.hotcode.HotCodeAgent.reload(HotCodeAgent.java:53)
或者:
java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
同样的一份class文件,为什么会提示新增或者删除了方法呢?
然后我们开始对比 javap -p ooxx.class ,发现在底部多了这些方法,这些都是内部类访问外部类的变量或者方法时调用导致的。
static java.util.Map access$0(com.lingyu.game.service.role.RoleManager); static org.apache.logging.log4j.Logger access$1();
只要规避掉这样的调用,就可以让含有内部类的类热加载。
同事还是很努力的!点个赞!