JAVA代码的热修复实现

简介: 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();

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

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

目录
相关文章
|
15天前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
5天前
|
Java
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
java小工具util系列4:基础工具代码(Msg、PageResult、Response、常量、枚举)
17 5
|
7天前
|
Java API 开发者
探索Java中的Lambda表达式:简洁与强大的代码实践
本文深入探讨Java中Lambda表达式的定义、用法及优势,通过实例展示其如何简化代码、提升可读性,并强调在使用中需注意的兼容性和效率问题。Lambda作为Java 8的亮点功能,不仅优化了集合操作,还促进了函数式编程范式的应用,为开发者提供了更灵活的编码方式。
|
3天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码之旅##
【8月更文挑战第62天】 Java 8的发布为开发者带来了诸多新特性,其中最引人注目的无疑是Lambda表达式。这一特性不仅让代码变得更加简洁,还极大地提升了开发的效率。本文将通过实际示例,展示如何利用Lambda表达式来优化我们的代码结构,同时探讨其背后的工作原理和性能考量。 ##
|
6天前
|
Java API 开发者
探索Java中的Lambda表达式:简化代码,提升效率
【9月更文挑战第27天】在Java 8中引入的Lambda表达式为编程带来了革命性的变化。通过简洁的语法和强大的功能,它不仅简化了代码编写过程,还显著提升了程序的执行效率。本文将深入探讨Lambda表达式的本质、用法和优势,并结合实例演示其在实际开发中的应用。无论你是Java新手还是资深开发者,都能从中获得启发,优化你的代码设计。
|
7天前
|
Java Linux Python
Linux环境下 代码java调用python出错
Linux环境下 代码java调用python出错
21 3
|
6天前
|
存储 Java 索引
使用java代码实现左右括号查找
使用java代码实现左右括号查找
|
7天前
|
算法 Java
java 概率抽奖代码实现
java 概率抽奖代码实现
|
15天前
|
Java 程序员 API
Java中的Lambda表达式:简化代码的秘密武器
在Java 8中引入的Lambda表达式是一种强大的编程工具,它可以显著简化代码,提高可读性。本文将介绍Lambda表达式的基本概念、优势以及在实际开发中的应用。通过具体示例,您将了解如何使用Lambda表达式来简化集合操作、线程编程和函数式编程。让我们一起探索这一革命性的特性,看看它是如何改变Java编程方式的。
23 4
|
15天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码
【8月更文挑战第49天】在Java 8的发布中,Lambda表达式无疑是最令人兴奋的新特性之一。它不仅为Java开发者提供了一种更加简洁、灵活的编程方式,而且还极大地提高了代码的可读性和开发效率。本文将通过实际代码示例,展示如何利用Lambda表达式优化和重构Java代码,让你的编程之旅更加轻松愉快。