JAVA代码的热修复实现

用于处理线上的一些逻辑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();

只要规避掉这样的调用,就可以让含有内部类的类热加载。

同事还是很努力的!点个赞!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值