APM JAVA 探针(-javaagent)涉及问题和实现思路

为什么80%的码农都做不了架构师?>>>   hot3.png

1.APM客户采集典型的三种方案

Pinpoint:基本不用修改源码和配置文件,只要在启动命令里指定javaagent参数即可,对于运维人员来讲最为方便;
Zipkin:需要对Spring、web.xml之类的配置文件做修改,相对麻烦一些;
CAT:因为需要修改源码设置埋点,因此基本不太可能由运维人员单独完成,而必须由开发人员的深度参与了,而很多开发人员是比较抗拒在代码中加入这些东西滴;

2.使用-javaagent 加载的包,多classloader理解很关键。

2.1.javaagent加载器是什么

An agent is just an interceptor in front of your main method, executed in the same JVM and loaded by the same system classloader, and governed by the same security policy and context.

 

4. One java application may have any number of agents by using -javaagent: option any number of times. Agents are invoked in the same order as specified in options.

http://javahowto.blogspot.nl/2006/07/javaagent-option.html

http://stackoverflow.com/questions/26280406/how-to-specify-class-path-for-java-agent

 

2.2.-javaagent 加载jar

-javaagent加载的jar包,在AppClassLoader。

但是代码里可以把一些包加到bootstrap classloader

Instrumentation.appendToBootstrapClassLoaderSearch

添加到AppClassLoader用:

Instrumentation.appendToSystemClassLoaderSearch

 

2.3.classloader 开发调试和容器启动区别

用IDE 启动classloader:

sun.misc.Launcher$AppClassLoader@18b4aac2

->

sun.misc.Launcher$ExtClassLoader@27f674d

->

bootstrap classloader.

web服务器启动

比如jetty启动web应用,应用的classloader是WebAppClassLoader。

WebAppClassLoader=170949260@a307a8c -> startJarLoader@4d1c00d0 -> sun.misc.Launcher$AppClassLoader@18b4aac2 -> sun.misc.Launcher$ExtClassLoader@63947c6b-> bootstrap classloader.

 

3.探针javaagent 方式考虑的主要问题

java agent功能涉及到的第三方包和应用里的包冲突问题。

一般javaagent要实现的功能有:

  1. 在框架的采集点进行字节码修改
  2. 采集到数据进行上报

 

4.一般的实现方法

4.1.把第三方包重命名来避免冲突

gradle:jarjar

apply plugin: 'org.anarres.jarjar'
 compile jarjar.repackage {

        from "org.slf4j:slf4j-api:${vLogSlf4j}"

        //classDelete "org.slf4j.**"

        classRename "org.slf4j.**","com.xxx.org.slf4j.@1"

        setDestinationName "slf4j-api-${vLogSlf4j}-jarjar.jar"
    }

maven shade重命名:


    org.apache.maven.plugins
    maven-shade-plugin
    2.4.3
    
        
            package
            
                shade
            
        
    
    
        false
        false
        true
        false
        true
        
            
                org.javassist:javassist
                
            
        
        
            
                javassist
                com.xxx.gtrace.javassist
            
        
    

 

4.2 通过自定义classloder加载,实现区分。

比如:采集涉及到的上报部分,一般会有很多公共包,上报的相关包就可以通过自定classloader加载。

目前我考虑的一个agent的目录结构及加载方式:

gtrace

+all    ------打包和release打包复制(包括agent,core,plugins)

+agent------做javaagent启动和类加载相关

+core ------核心实现(比如opentracing的api,reporter的接口)

+api  ------ core功能API,可以通过反射实现,应用如果有定制开发可以用,发布到仓库

+reporter ---采集数据上报具体实现(有可能是kafka,http等)

+plugins ----各个框架的探针相关代码(依赖的包一般是provided,不会出依赖包)

  +--plugin-dubbo

 +--plugin-hessian

类加载策略:

agent+core及依赖包,把第三方包重命名打包(可以合并打包),加载到systemClassloader

plugins的相关包,加载到跟应用加载一样的classloader,可以通过反射调用UrlClassloader.addUrl动态添加

reporter包,可以通过自定定义classloader加载,然后通过serviceloader加载奥实现,给agent调用。

 

5.其他一些问题

5.1. manifest文件设置

premain-class 一般不会忘,但是下面两个属性可能会忘,报错

java.lang.UnsupportedOperationException: adding retransformable transformers is not supported in this environment

打包的时候,设置这个2个属性

manifest.attributes["Can-Retransform-Classes"]=true
manifest.attributes["Can-Redefine-Classes"]=true

 

5.2 大包结果确认,最好准备一个反编译工具

mac我用:JD-GUI

 

5.3 javassist

getDeclaredMethods 才能修改,getMethod不能修改,自己没留意坑了不少时间,想想也是只能本类声明的方法

参考:

用 Javassist 进行类转换:https://www.ibm.com/developerworks/cn/java/j-dyn0916/

API:https://jboss-javassist.github.io/javassist/tutorial/tutorial2.html

 

5.4 打包可能会包多个包打到一起或者copy

有些可能比较累赘,没有细究,maven例子如下:


        
            
                maven-clean-plugin
                
                    
                        clean-first
                        generate-resources
                        
                            clean
                        
                    
                
            
            
                maven-dependency-plugin
                
                    
                    
                        locate-dependencies
                        initialize
                        
                            properties
                        
                    

                    
                    
                        unpack-sources
                        prepare-package
                        
                            unpack-dependencies
                        
                        
                            sources
                            com/xxx/gtrace/**
                            runtime
                            ${project.groupId}
                            ${generatedSourceDir}
                        
                    

                    
                    
                        unpack-jars
                        prepare-package
                        
                            unpack-dependencies
                        
                        
                            com/xxx/examples/**,com/xxx/gtrace/opentracing/plugin/**,com/xxx/gtrace/opentracing/brave/**
                            com/xxx/**,META-INF/**,gtrace*
                            runtime
                            ${project.groupId}
                            ${project.build.outputDirectory}
                        
                    

                    

                    
                        copy
                        package
                        
                            copy-dependencies
                        
                        
                            io.opentracing
                            io.opentracing.brave
                            
                                provided
                            
                            
                                ${project.build.directory}/release
                            
                        
                    

                    
                        copy-tracer
                        package
                        
                            copy-dependencies
                        
                        
                            
                                com.xxx.gtrace
                            
                            
                                provided
                            
                            opentracing-api,opentracing-noop,opentracing-util
                            
                                ${project.build.directory}/release/tracer
                            
                        
                    

                    
                        copy-plugin
                        package
                        
                            copy-dependencies
                        
                        
                            
                                gtrace-opentracing-plugin-hessian,gtrace-opentracing-plugin-dubbo,gtrace-opentracing-plugin-httpclient4,
                                gtrace-opentracing-plugin-servlet3,gtrace-opentracing-plugin-servlet25,gtrace-opentracing-plugin-springmvc
                            
                            
                                ${project.build.directory}/release/plugin
                            
                        
                    

                
            

            
                maven-antrun-plugin
                
                    
                    
                    
                        clean-source-directory
                        package
                        
                            run
                        
                        
                            
                                
                                
                                
                            
                        
                    
                
            

            
            
                org.codehaus.mojo
                build-helper-maven-plugin
                
                    
                        add-source
                        prepare-package
                        
                            add-source
                        
                        
                            
                                ${generatedSourceDir}
                            
                        
                    
                
            

            
            
                org.apache.felix
                maven-bundle-plugin
                
                    
                        generate-manifest
                        none
                    
                
            
            
            
                maven-jar-plugin
                
                    
                        default-jar
                        none
                    
                    
                        all-in-one-jar
                        package
                        
                            jar
                        
                        
                            
                                
                                    true
                                
                                true
                                
                                    
                                        com.xxx.gtrace.opentracing.agent.GtraceAgent
                                    
                                    true
                                    true
                                    ${project.version}
                                
                            
                            ${project.build.directory}/release
                        
                    
                
            

            
            
                org.codehaus.mojo
                animal-sniffer-maven-plugin
                
                    
                        default
                        none
                    
                
            

            
            
                maven-checkstyle-plugin
                
                    
                        check-style
                        none
                    
                
            

            
            
                maven-resources-plugin
                
                    
                        default-resources
                        none
                    
                    
                        default-testResources
                        none
                    
                
            
            
                maven-compiler-plugin
                
                    
                        default-compile
                        none
                    
                    
                        default-testCompile
                        none
                    
                
            
            
                maven-surefire-plugin
                
                    
                        default-test
                        none
                    
                
            
        
    

5.5 修改字节码➕拦截的实现思路(javassist)

在做APM的时候想简单实现下AOP,对plugin的拦截记录下思路,(pinpoint有一种较好的实现,想对麻烦点,要求学习字节码的一些知识,主要看InstrumentClass实现,JavassistClass和ASMClass )

1.通过子类方式(这类方式,有相对统一的对象创建的地方)

http://exolution.iteye.com/blog/1460833

动态创建一个被代理类的子类,子类里实现拦截。

2.通过创建父亲类的方式(这种方式,直接修改字节码)

代码写一个需要修改的代理抽象类,保持父类、实现接口类跟目标一致。

public abstract class HXXXProxy implements InvocationHandler, Serializable {
    //目标修改类的方法,重命名成一致的名字
    public abstract Object invoke$impl(Object proxy, Method method, Object[] args) throws Throwable;
    public Object invoke$trace(final Object proxy, final Method method, final Object[] args) throws Throwable {
                //xxxxxx
                //调用原方法
                Object reponse = invoke$impl(proxy, method, args);

                //xxxxx
                return reponse;

    }
}

修改字节码的时候,把目标类的父类,设置成代理类,目标方法重命名invoke$impl。

copy一个目标方法,把方法体改成 { return  methodName$trace($$);}。

转载于:https://my.oschina.net/greki/blog/1540544

你可能感兴趣的:(APM JAVA 探针(-javaagent)涉及问题和实现思路)