我是靠谱客的博主 犹豫眼睛,最近开发中收集的这篇文章主要介绍JAVA代码热部署,在线不停服动态更新,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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

本地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 代码动态更改类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
 
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();
    }
}
 
 Java Code  Instrumentation 如何获取?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 
public  class JavaDynAgent {
     private  static Instrumentation instrumentation;
     private  static Object lockObject =  new Object();

     public JavaDynAgent() {
    }

     public  static  void agentmain( String args, Instrumentation inst) {
        Object var2 = lockObject;
         synchronized(lockObject) {
             if(instrumentation == null) {
                instrumentation = inst;
                System.out.println( "0->" + inst);
            }  else {
                System.out.println( "1->" + inst);
            }

        }
    }

     public  static Instrumentation getInstrumentation() {
         return instrumentation;
    }
}
 
实现原理是:
1.绑定pid获得虚拟机对象,然后通过虚拟机加载代理jar包,这样就调用到 agentmain,获取得到Instrumentation
2.基于 Instrumentation接口可以实现JDK的代理机制,从而实现对类进行动态重新定义。
 
注意:com.sun.tools.attach. VirtualMachine的jar包是 jdk下lib中的tools.jar,所以项目中要引用到这个jar包,而且因为涉及到底层虚拟机,windows和linux机器这个jar不同
 
因此, 整个流程就是:
1.项目中引用 jdk/lib/tools.jar,否则无法使用VirtualMachine类
2.项目中引用 javaagent.jar ,它提供了agentmain接口
3.代码实现动态增加JDK代理
 
posted on 2015-05-13 16:18 CM4J 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/cm4j/p/hot_deploy.html

最后

以上就是犹豫眼睛为你收集整理的JAVA代码热部署,在线不停服动态更新的全部内容,希望文章能够帮你解决JAVA代码热部署,在线不停服动态更新所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(55)

评论列表共有 0 条评论

立即
投稿
返回
顶部