APM JAVA 探针(

Stella981
• 阅读 875

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例子如下:

<build>
        <plugins>
            <plugin>
                <artifactId>maven-clean-plugin</artifactId>
                <executions>
                    <execution>
                        <id>clean-first</id>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>clean</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <!-- Populate the properties whose key is groupId:artifactId:type
                                             and whose value is the path to the artifact -->
                    <execution>
                        <id>locate-dependencies</id>
                        <phase>initialize</phase>
                        <goals>
                            <goal>properties</goal>
                        </goals>
                    </execution>

                    <!-- Unpack all source files -->
                    <execution>
                        <id>unpack-sources</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>unpack-dependencies</goal>
                        </goals>
                        <configuration>
                            <classifier>sources</classifier>
                            <includes>com/xxx/gtrace/**</includes>
                            <includeScope>runtime</includeScope>
                            <includeGroupIds>${project.groupId}</includeGroupIds>
                            <outputDirectory>${generatedSourceDir}</outputDirectory>
                        </configuration>
                    </execution>

                    <!-- Unpack all class files -->
                    <execution>
                        <id>unpack-jars</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>unpack-dependencies</goal>
                        </goals>
                        <configuration>
                            <excludes>com/xxx/examples/**,com/xxx/gtrace/opentracing/plugin/**,com/xxx/gtrace/opentracing/brave/**</excludes>
                            <includes>com/xxx/**,META-INF/**,gtrace*</includes>
                            <includeScope>runtime</includeScope>
                            <includeGroupIds>${project.groupId}</includeGroupIds>
                            <outputDirectory>${project.build.outputDirectory}</outputDirectory>
                        </configuration>
                    </execution>

                    <!--copy jar-->

                    <execution>
                        <id>copy</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <includeGroupIds>io.opentracing</includeGroupIds>
                            <excludeGroupIds>io.opentracing.brave</excludeGroupIds>
                            <excludeScope>
                                provided
                            </excludeScope>
                            <outputDirectory>
                                ${project.build.directory}/release
                            </outputDirectory>
                        </configuration>
                    </execution>

                    <execution>
                        <id>copy-tracer</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <excludeGroupIds>
                                com.xxx.gtrace
                            </excludeGroupIds>
                            <excludeScope>
                                provided
                            </excludeScope>
                            <excludeArtifactIds>opentracing-api,opentracing-noop,opentracing-util</excludeArtifactIds>
                            <outputDirectory>
                                ${project.build.directory}/release/tracer
                            </outputDirectory>
                        </configuration>
                    </execution>

                    <execution>
                        <id>copy-plugin</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <includeArtifactIds>
                                gtrace-opentracing-plugin-hessian,gtrace-opentracing-plugin-dubbo,gtrace-opentracing-plugin-httpclient4,
                                gtrace-opentracing-plugin-servlet3,gtrace-opentracing-plugin-servlet25,gtrace-opentracing-plugin-springmvc
                            </includeArtifactIds>
                            <outputDirectory>
                                ${project.build.directory}/release/plugin
                            </outputDirectory>
                        </configuration>
                    </execution>

                </executions>
            </plugin>

            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <executions>
                    <!-- Instead of generating a new version property file, merge others' version property files into one.
                    <execution>
                        <id>write-version-properties</id>
                        <phase>none</phase>
                    </execution>
                    <execution>
                        <id>merge-version-properties</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <taskdef resource="net/sf/antcontrib/antlib.xml" />
                                <propertyselector property="versions" match="^(${project.groupId}:(?!netty-example)[^:]+:jar(?::[^:]+)?)$" select="\1" />
                                <for list="${versions}" param="x">
                                    <sequential>
                                        <unzip src="${@{x}}" dest="${dependencyVersionsDir}">
                                            <patternset>
                                                <include name="META-INF/${project.groupId}.versions.properties" />
                                            </patternset>
                                        </unzip>
                                        <concat destfile="${project.build.outputDirectory}/META-INF/${project.groupId}.versions.properties" append="true">
                                            <path path="${dependencyVersionsDir}/META-INF/${project.groupId}.versions.properties" />
                                        </concat>
                                    </sequential>
                                </for>
                                <delete dir="${dependencyVersionsDir}" quiet="true" />
                            </target>
                        </configuration>
                    </execution>
                    -->
                    <!-- Clean everything once finished so that IDE doesn't find the unpacked files. -->
                    <execution>
                        <id>clean-source-directory</id>
                        <phase>package</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                        <configuration>
                            <target>
                                <delete dir="${generatedSourceDir}" quiet="true"/>
                                <!--<delete dir="${dependencyVersionsDir}" quiet="true" />-->
                                <delete dir="${project.build.outputDirectory}" quiet="true"/>
                            </target>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- Include the directory where the source files were unpacked -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>add-source</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${generatedSourceDir}</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- Disable OSGi bundle manifest generation -->
            <plugin>
                <groupId>org.apache.felix</groupId>
                <artifactId>maven-bundle-plugin</artifactId>
                <executions>
                    <execution>
                        <id>generate-manifest</id>
                        <phase>none</phase>
                    </execution>
                </executions>
            </plugin>
            <!-- Override the default JAR configuration -->
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default-jar</id>
                        <phase>none</phase>
                    </execution>
                    <execution>
                        <id>all-in-one-jar</id>
                        <phase>package</phase>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                        <configuration>
                            <archive>
                                <manifest>
                                    <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                                </manifest>
                                <index>true</index>
                                <manifestEntries>
                                    <Premain-Class>
                                        com.xxx.gtrace.opentracing.agent.GtraceAgent
                                    </Premain-Class>
                                    <Can-Redefine-Classes>true</Can-Redefine-Classes>
                                    <Can-Retransform-Classes>true</Can-Retransform-Classes>
                                    <Pinpoint-Version>${project.version}</Pinpoint-Version>
                                </manifestEntries>
                            </archive>
                            <outputDirectory>${project.build.directory}/release</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- Disable animal sniffer -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>animal-sniffer-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default</id>
                        <phase>none</phase>
                    </execution>
                </executions>
            </plugin>

            <!-- Disable checkstyle -->
            <plugin>
                <artifactId>maven-checkstyle-plugin</artifactId>
                <executions>
                    <execution>
                        <id>check-style</id>
                        <phase>none</phase>
                    </execution>
                </executions>
            </plugin>

            <!-- Disable all plugin executions configured by jar packaging -->
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default-resources</id>
                        <phase>none</phase>
                    </execution>
                    <execution>
                        <id>default-testResources</id>
                        <phase>none</phase>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default-compile</id>
                        <phase>none</phase>
                    </execution>
                    <execution>
                        <id>default-testCompile</id>
                        <phase>none</phase>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <executions>
                    <execution>
                        <id>default-test</id>
                        <phase>none</phase>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

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($$);}。

点赞
收藏
评论区
推荐文章
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Easter79 Easter79
3年前
springboot读取外部配置文件
springboot项目打成jar包后不好进行配置文件修改,可设置为读取外部配置文件,方便进行配置修改.步骤:1.将jar包中的application.properties配置文件复制到自定义路径下;2.运行jar包命令指定外部配置文件路径:nohupjavajar.jarspring.config.location
Tommy744 Tommy744
3年前
一份DevOps工程师职责清单,待你查阅
如果一个组织的开发人员和运维人员是独立工作的模式,实施DevOps就需要对组织进行大的调整。因为,只有具备合适的组织人员,文化和工具来才能成功实施DevOps。根据显示,实施DevOps的最常见的障碍之一是员工缺乏技能。什么是DevOps工程师?DevOps工程师是一位IT专家,应该对开发和运维工作都有广泛的了解,包括编码,基础
Stella981 Stella981
3年前
OpenWrt 对外网开放vsftp服务和samba服务
对WAN开放vsFTPOpenWrt默认启动了vsftp服务,在Luci上没找到配置界面,但是后台是有这个服务的,如果在Openwrt的lan下,可以直接使用FileZilla之类的客户端连接.其配置文件位于/etc/vsftpd.conf如果需要对wan提供服务,需要做以下设置:1\.修改/etc/v
Stella981 Stella981
3年前
SkyWalking链路监控(一):SkyWalking快速搭建
简介当分布式系统服务比较多,特别是微服务,出现故障就很难排查。所以需要借助APM系统进行排查(ApplicationPerformanceManagement,即应用性能管理),SkyWalking是APM系统的一种,类似的产品还有CAT、Zipkin、Pinpoint。SkyWalking和Pinpoint相比其他系统,做到了无侵入性
Wesley13 Wesley13
3年前
MySQL数据库优化技巧
MySQL优化三大方向①优化MySQL所在服务器内核(此优化一般由运维人员完成)。②对MySQL配置参数进行优化(my.cnf)此优化需要进行压力测试来进行参数调整。③对SQL语句以及表优化。MySQL参数优化1:MySQL默认的最大连接数为100,可以在mysql客户端使用以下命令查看mysql
Stella981 Stella981
3年前
CODING DevOps 系列第六课:IT 运维之智能化告警实践
IT运维告警现状目前IT运维领域保证服务运行正常的主要方法是对相关运维指标进行实时监控,并根据经验设定一些规则,通过将实时监控的数据与规则进行对比,当某个指标监控值不符合设定的规则时,则判定为异常的状况,这样的话就会发送对应的告警到告警平台。告警平台收到通知后,会分配给对应的运维人员进行处理,运维人员去根据告警信息来排查,最终定
Stella981 Stella981
3年前
DevOps背景下的分合之事
DevOps倡导“谁开发,谁运维”和开发运维一体化。那么是不是简单地把开发和运维人员放在一起就完事了呢?01—“插队”的故事小明入职时是运维专员,原来隶属于运维部门,负责某业务线系统的应用维护工作。一旦系统的生产环境出现任何故障,或者业务人员在生产环境上有任何请求,都是由小明所在的运维部门先处理,处理不了的,再联系该系
初识DevOps
基本概念和延伸的思考DevOps,是Development(开发)和Operations(运维)组成的复合词,一般译为“开发运维一体化”。看到这个概念,首先会产生几个问题:开发是什么,哪些环节是开发?运维是什么,哪些环节是运维?开发人员写好代码在本地调试,环境出问题了自己来调整,这是开发工作还是运维工作?系统故障后,运维人员发现是配置文件内容出错了就改成了正
芝士年糕 芝士年糕
2年前
什么是堡垒机
一:运维审计型堡垒机基本概念介绍跳板机→堡垒机,被称为跳板机的原因就是,运维人员通过它和更多的设备联系→堡垒机还会审核运维的权限再返回请求,但是这样还是存在误操作等问题,有安全隐患,跳板机被攻入,会导致整个网络资源完全暴露。同时跳板机对一些资源f