JAVA代码热部署,在线不停服动态更新

发表于:2015-6-05 10:02

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:CM4J    来源:51Testing软件测试网采编

  本地debug的时候,可以实时编译并更新代码,线上也可以不停服来动态更新类,即所说的java热部署。
  JDK代理的两种方式:
  1.premain方式是Java SE5开始就提供的代理方式,但其必须在命令行指定代理jar,并且代理类必须在main方法前启动,它要求开发者在应用启动前就必须确认代理的处理逻辑和参数内容等等
  2.agentmain方式是JavaSE6开始提供,它可以在应用程序的VM启动后再动态添加代理的方式
  agentmain应用场景:
  比如正常的生产环境下,一般不会开启代理功能,但是在发生问题时,我们不希望停止应用就能够动态的去修改一些类的行为,以帮助排查问题,这在应用启动前是无法确定的
  与Permain类似,agent方式同样需要提供一个agent jar,并且这个jar需要满足[可查看附件的jar文件]:
  1.在manifest中指定Agent-Class属性,值为代理类全路径
  2.代理类需要提供public static void agentmain(String args, Instrumentation inst)或public static void agentmain(String args)方法。并且再二者同时存在时以前者优先。args和inst和premain中的一致。
  那如何在不停服的情况下动态修改类呢??
  实现代码如下:
  Java Code 代码动态更改类
public class JavaAgent {
public static final Logger logger = LoggerFactory.getLogger(JavaAgent.class);
private static String classesPath;
private static String jarPath;
private static VirtualMachine vm;
private static String pid;
static {
classesPath = JavaAgent.class.getClassLoader().getResource("").getPath();
logger.error("java agent:classpath:{}", classesPath);
jarPath = getJarPath();
logger.error("java agent:jarPath:{}", jarPath);
// 当前进程pid
String name = ManagementFactory.getRuntimeMXBean().getName();
pid = name.split("@")[0];
logger.error("当前进程pid:{}", pid);
}
/**
* 获取jar包路径
* @return
*/
public static String getJarPath() {
// StringUtils是jar文件内容
URL url = StringUtils.class.getProtectionDomain().getCodeSource().getLocation();
String filePath = null;
try {
filePath = URLDecoder.decode(url.getPath(), "utf-8");// 转化为utf-8编码
} catch (Exception e) {
e.printStackTrace();
}
if (filePath.endsWith(".jar")) {// 可执行jar包运行的结果里包含".jar"
// 截取路径中的jar包名
filePath = filePath.substring(0, filePath.lastIndexOf("/") + 1);
}
File file = new File(filePath);
filePath = file.getAbsolutePath();//得到windows下的正确路径
return filePath;
}
private static void init() throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
// 虚拟机加载
vm = VirtualMachine.attach(pid);
vm.loadAgent(jarPath + "/javaagent.jar");
Instrumentation instrumentation = JavaDynAgent.getInstrumentation();
Preconditions.checkNotNull(instrumentation, "initInstrumentation must not be null");
}
private static void destroy() throws IOException {
if (vm != null) {
vm.detach();
}
}
/**
* 重新加载类
*
* @param classArr
* @throws Exception
*/
public static void javaAgent(String root, String[] classArr) throws ClassNotFoundException, IOException, UnmodifiableClassException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
init();
try {
// 1.整理需要重定义的类
List<ClassDefinition> classDefList = new ArrayList<ClassDefinition>();
for (String className : classArr) {
Class<?> c = Class.forName(className);
String classPath = (StringUtils.isNotBlank(root) ? root : classesPath) + className.replace(".", "/") + ".class";
logger.error("class redefined:" + classPath);
byte[] bytesFromFile = Files.toByteArray(new File(classPath));
ClassDefinition classDefinition = new ClassDefinition(c, bytesFromFile);
classDefList.add(classDefinition);
}
// 2.redefine
JavaDynAgent.getInstrumentation().redefineClasses(classDefList.toArray(new ClassDefinition[classDefList.size()]));
} finally {
destroy();
}
}
public static void main(String[] args) throws Exception {
PortUtil.test();
javaAgent(null, new String[] {"com.agileeagle.webgame.framework.util.PortUtil"});
PortUtil.test();
}
}
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号