欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识

我们使用了asm做字节码 劳务派遣管理系统 注入来做线上性能的监测

点击: 次  来源:宝鼎软件 时间:2017-09-05

原文出处: 飒然Hang

之前在看agentzh的此篇博文动态追踪技能漫谈时,了解到了动态追踪技能的强大之处,也一直由于无法在不重启线上处事器的环境下排查线上问题在寻找Java中的动态追踪东西。在公司内部的JavaEE机能检测框架中,我们利用了asm做字节码注入来做线上机能的监测,沿着这个思路,假如要做到动态追踪应该是需要做字节码注入的,可是特另外一点是需要动态加载字节码替换掉原有的类的。另外,机能监测框架是需要耦合到业务应用中的,无法做到一个监测东西的机动性。

厥后听同事提到了BTrace这个东西,于是去实验了一下。BTrace是SUN Kenai云计较开拓平台下的一个开源项目,旨在为java提供安详靠得住的动态跟踪阐明东西。江南白衣的这篇文章http://calvin1978.blogcn.com/articles/btrace1.html做了较量具体的描写。

那么,BTrace这么神奇的成果是如何实现的呢?既然这是个开源的代码,那么直接从代码找道理。BTrace代码开源在https://github.com/btraceio/btrace。

总体来说,BTrace是基于动态字节码修改技能(Hotswap)来实现运行时java措施的跟踪和替换。概略的道理可以用下面的公式描写:

Client(Java compile api + attach api) + Agent(剧本理会引擎 + ASM + JDK6 Instumentation) + Socket

BTrace的进口类在https://github.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/client/Main.java中。在其main要领中,可以看到起最终的焦点逻辑是在https://github.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/client/Client.java中。要领挪用如下:

  • client.compile
  • client.attach
  • client.submit
  • Client

    首先是client.compile要领,利用的是Java compile api,将我们通报的java源文件编译为.class文件,虽然你假如利用btracec提前编译了源代码,那么这里就不会有这一步。

    针对官方剧本的一个例子:

    import com.sun.btrace.annotations.*;
    import static com.sun.btrace.BTraceUtils.*;
    @BTrace
    public class HelloWorld {
        @OnMethod(
            clazz="java.lang.Thread",
            method="start"
        )
        public static void func() {
            println("about to start a thread!");
        }
    }

    @OnMethod汇报Btrace理会引擎需要署理的类和要领。 这个例子的浸染是当java.lang.Thread类的任意一个工具挪用 start 要领后,会挪用func要领。

    client端在编译完剧本之后,举办了一次字节码修改,可是仅仅是做了一些兼容性,譬喻域会见节制器、简写等。

    接着client.attach中利用java的attach api将agent动态attach到方针jvm历程中(ava agent,凡是有两种方法添加到jvm历程中:动态attach;在方针jvm启动之前添加agent参数)。

    VirtualMachine vm  = VirtualMachine.attach(pid);
    ...
    vm.loadAgent(agentPath, agentArgs);

    最后client的submit要领,会向agent发送监控呼吁以及通报对应code的字节码。

    Agent

    BTrace的agent实现类就在https://github.com/btraceio/btrace/blob/master/src/share/classes/com/sun/btrace/agent/Main.java中,详细的实现可以看其main要领,此agent的premain和agentmain要领都是挪用了这个要领。这里需要留意的一点:必需要上jdk6,因为jdk5固然已经有了instrument api,软件开发,可是其仅仅支持premain要领,也就是仅仅支持在main要领运行之前执行一些行动,而jdk6后插手了agentmain要领和VirtualMachine,是可以在main要领运行后执行的(假如是通过呼吁行启动的,那么agentmain要领不会被挪用)。另外,在jdk6之前,措施启动之后是无法再配置boot class加载路径和system class加载路径的。而jdk6之后,instrument新增的appendToBootstrapClassLoaderSearch和appendToSystemClassLoaderSearch是可以动态添加classpath的。

    agent被提交到方针jvm历程后,首先会添加boot classpath.

    ...
    inst.appendToBootstrapClassLoaderSearch(jf);
    ...
    inst.appendToSystemClassLoaderSearch(jf);

    接着开启一个serversocket期待client的毗连。之后client和agent之间的数据通讯,好比生成.class发送到agent,agent将线上措施打印的数据回传给 client都是通过socket来举办的。当agent吸收到监控呼吁后,主要有以下两部门事情:

  • 重写类:遍历当前所有的class,按照正则找到匹配的类,用asm重写
  • 替换类:替换掉本来的class
  • agent接管到client发来的监控指令以及对应的参数后,会load所有的class,按照正则去匹配指定的类和要领,并利用剧本理会引擎去处理惩罚发送过来的字节码然后利用ASM将剧本里标注的类java.lang.Thread的字节码重写,植入跟踪代码或新的逻辑。在上面谁人例子中,Java.lang.Thread这个类的字节码被重写并在start要领体尾部植入了func要领的挪用。