APM JAVA 探针(

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

1apply plugin: 'org.anarres.jarjar' 2 compile jarjar.repackage { 3 4 from "org.slf4j:slf4j-api:${vLogSlf4j}" 5 6 //classDelete "org.slf4j.**" 7 8 classRename "org.slf4j.**","com.xxx.org.slf4j.@1" 9 10 setDestinationName "slf4j-api-${vLogSlf4j}-jarjar.jar" 11 }

maven shade重命名:

<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.4.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> </execution> </executions> <configuration> <keepDependenciesWithProvidedScope>false</keepDependenciesWithProvidedScope> <promoteTransitiveDependencies>false</promoteTransitiveDependencies> <createDependencyReducedPom>true</createDependencyReducedPom> <minimizeJar>false</minimizeJar> <createSourcesJar>true</createSourcesJar> <artifactSet> <includes> <include>org.javassist:javassist</include> <!--<include>io.netty:netty-all</include>--> </includes> </artifactSet> <relocations> <relocation> <pattern>javassist</pattern> <shadedPattern>com.xxx.gtrace.javassist</shadedPattern> </relocation> </relocations> </configuration> </plugin>

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

1<build> 2 <plugins> 3 <plugin> 4 <artifactId>maven-clean-plugin</artifactId> 5 <executions> 6 <execution> 7 <id>clean-first</id> 8 <phase>generate-resources</phase> 9 <goals> 10 <goal>clean</goal> 11 </goals> 12 </execution> 13 </executions> 14 </plugin> 15 <plugin> 16 <artifactId>maven-dependency-plugin</artifactId> 17 <executions> 18 <!-- Populate the properties whose key is groupId:artifactId:type 19 and whose value is the path to the artifact --> 20 <execution> 21 <id>locate-dependencies</id> 22 <phase>initialize</phase> 23 <goals> 24 <goal>properties</goal> 25 </goals> 26 </execution> 27 28 <!-- Unpack all source files --> 29 <execution> 30 <id>unpack-sources</id> 31 <phase>prepare-package</phase> 32 <goals> 33 <goal>unpack-dependencies</goal> 34 </goals> 35 <configuration> 36 <classifier>sources</classifier> 37 <includes>com/xxx/gtrace/**</includes> 38 <includeScope>runtime</includeScope> 39 <includeGroupIds>${project.groupId}</includeGroupIds> 40 <outputDirectory>${generatedSourceDir}</outputDirectory> 41 </configuration> 42 </execution> 43 44 <!-- Unpack all class files --> 45 <execution> 46 <id>unpack-jars</id> 47 <phase>prepare-package</phase> 48 <goals> 49 <goal>unpack-dependencies</goal> 50 </goals> 51 <configuration> 52 <excludes>com/xxx/examples/**,com/xxx/gtrace/opentracing/plugin/**,com/xxx/gtrace/opentracing/brave/**</excludes> 53 <includes>com/xxx/**,META-INF/**,gtrace*</includes> 54 <includeScope>runtime</includeScope> 55 <includeGroupIds>${project.groupId}</includeGroupIds> 56 <outputDirectory>${project.build.outputDirectory}</outputDirectory> 57 </configuration> 58 </execution> 59 60 <!--copy jar--> 61 62 <execution> 63 <id>copy</id> 64 <phase>package</phase> 65 <goals> 66 <goal>copy-dependencies</goal> 67 </goals> 68 <configuration> 69 <includeGroupIds>io.opentracing</includeGroupIds> 70 <excludeGroupIds>io.opentracing.brave</excludeGroupIds> 71 <excludeScope> 72 provided 73 </excludeScope> 74 <outputDirectory> 75 ${project.build.directory}/release 76 </outputDirectory> 77 </configuration> 78 </execution> 79 80 <execution> 81 <id>copy-tracer</id> 82 <phase>package</phase> 83 <goals> 84 <goal>copy-dependencies</goal> 85 </goals> 86 <configuration> 87 <excludeGroupIds> 88 com.xxx.gtrace 89 </excludeGroupIds> 90 <excludeScope> 91 provided 92 </excludeScope> 93 <excludeArtifactIds>opentracing-api,opentracing-noop,opentracing-util</excludeArtifactIds> 94 <outputDirectory> 95 ${project.build.directory}/release/tracer 96 </outputDirectory> 97 </configuration> 98 </execution> 99 100 <execution> 101 <id>copy-plugin</id> 102 <phase>package</phase> 103 <goals> 104 <goal>copy-dependencies</goal> 105 </goals> 106 <configuration> 107 <includeArtifactIds> 108 gtrace-opentracing-plugin-hessian,gtrace-opentracing-plugin-dubbo,gtrace-opentracing-plugin-httpclient4, 109 gtrace-opentracing-plugin-servlet3,gtrace-opentracing-plugin-servlet25,gtrace-opentracing-plugin-springmvc 110 </includeArtifactIds> 111 <outputDirectory> 112 ${project.build.directory}/release/plugin 113 </outputDirectory> 114 </configuration> 115 </execution> 116 117 </executions> 118 </plugin> 119 120 <plugin> 121 <artifactId>maven-antrun-plugin</artifactId> 122 <executions> 123 <!-- Instead of generating a new version property file, merge others' version property files into one. 124 <execution> 125 <id>write-version-properties</id> 126 <phase>none</phase> 127 </execution> 128 <execution> 129 <id>merge-version-properties</id> 130 <phase>prepare-package</phase> 131 <goals> 132 <goal>run</goal> 133 </goals> 134 <configuration> 135 <target> 136 <taskdef resource="net/sf/antcontrib/antlib.xml" /> 137 <propertyselector property="versions" match="^(${project.groupId}:(?!netty-example)[^:]+:jar(?::[^:]+)?)$" select="\1" /> 138 <for list="${versions}" param="x"> 139 <sequential> 140 <unzip src="${@{x}}" dest="${dependencyVersionsDir}"> 141 <patternset> 142 <include name="META-INF/${project.groupId}.versions.properties" /> 143 </patternset> 144 </unzip> 145 <concat destfile="${project.build.outputDirectory}/META-INF/${project.groupId}.versions.properties" append="true"> 146 <path path="${dependencyVersionsDir}/META-INF/${project.groupId}.versions.properties" /> 147 </concat> 148 </sequential> 149 </for> 150 <delete dir="${dependencyVersionsDir}" quiet="true" /> 151 </target> 152 </configuration> 153 </execution> 154 --> 155 <!-- Clean everything once finished so that IDE doesn't find the unpacked files. --> 156 <execution> 157 <id>clean-source-directory</id> 158 <phase>package</phase> 159 <goals> 160 <goal>run</goal> 161 </goals> 162 <configuration> 163 <target> 164 <delete dir="${generatedSourceDir}" quiet="true"/> 165 <!--<delete dir="${dependencyVersionsDir}" quiet="true" />--> 166 <delete dir="${project.build.outputDirectory}" quiet="true"/> 167 </target> 168 </configuration> 169 </execution> 170 </executions> 171 </plugin> 172 173 <!-- Include the directory where the source files were unpacked --> 174 <plugin> 175 <groupId>org.codehaus.mojo</groupId> 176 <artifactId>build-helper-maven-plugin</artifactId> 177 <executions> 178 <execution> 179 <id>add-source</id> 180 <phase>prepare-package</phase> 181 <goals> 182 <goal>add-source</goal> 183 </goals> 184 <configuration> 185 <sources> 186 <source>${generatedSourceDir}</source> 187 </sources> 188 </configuration> 189 </execution> 190 </executions> 191 </plugin> 192 193 <!-- Disable OSGi bundle manifest generation --> 194 <plugin> 195 <groupId>org.apache.felix</groupId> 196 <artifactId>maven-bundle-plugin</artifactId> 197 <executions> 198 <execution> 199 <id>generate-manifest</id> 200 <phase>none</phase> 201 </execution> 202 </executions> 203 </plugin> 204 <!-- Override the default JAR configuration --> 205 <plugin> 206 <artifactId>maven-jar-plugin</artifactId> 207 <executions> 208 <execution> 209 <id>default-jar</id> 210 <phase>none</phase> 211 </execution> 212 <execution> 213 <id>all-in-one-jar</id> 214 <phase>package</phase> 215 <goals> 216 <goal>jar</goal> 217 </goals> 218 <configuration> 219 <archive> 220 <manifest> 221 <addDefaultImplementationEntries>true</addDefaultImplementationEntries> 222 </manifest> 223 <index>true</index> 224 <manifestEntries> 225 <Premain-Class> 226 com.xxx.gtrace.opentracing.agent.GtraceAgent 227 </Premain-Class> 228 <Can-Redefine-Classes>true</Can-Redefine-Classes> 229 <Can-Retransform-Classes>true</Can-Retransform-Classes> 230 <Pinpoint-Version>${project.version}</Pinpoint-Version> 231 </manifestEntries> 232 </archive> 233 <outputDirectory>${project.build.directory}/release</outputDirectory> 234 </configuration> 235 </execution> 236 </executions> 237 </plugin> 238 239 <!-- Disable animal sniffer --> 240 <plugin> 241 <groupId>org.codehaus.mojo</groupId> 242 <artifactId>animal-sniffer-maven-plugin</artifactId> 243 <executions> 244 <execution> 245 <id>default</id> 246 <phase>none</phase> 247 </execution> 248 </executions> 249 </plugin> 250 251 <!-- Disable checkstyle --> 252 <plugin> 253 <artifactId>maven-checkstyle-plugin</artifactId> 254 <executions> 255 <execution> 256 <id>check-style</id> 257 <phase>none</phase> 258 </execution> 259 </executions> 260 </plugin> 261 262 <!-- Disable all plugin executions configured by jar packaging --> 263 <plugin> 264 <artifactId>maven-resources-plugin</artifactId> 265 <executions> 266 <execution> 267 <id>default-resources</id> 268 <phase>none</phase> 269 </execution> 270 <execution> 271 <id>default-testResources</id> 272 <phase>none</phase> 273 </execution> 274 </executions> 275 </plugin> 276 <plugin> 277 <artifactId>maven-compiler-plugin</artifactId> 278 <executions> 279 <execution> 280 <id>default-compile</id> 281 <phase>none</phase> 282 </execution> 283 <execution> 284 <id>default-testCompile</id> 285 <phase>none</phase> 286 </execution> 287 </executions> 288 </plugin> 289 <plugin> 290 <artifactId>maven-surefire-plugin</artifactId> 291 <executions> 292 <execution> 293 <id>default-test</id> 294 <phase>none</phase> 295 </execution> 296 </executions> 297 </plugin> 298 </plugins> 299 </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($$);}。

点赞
收藏

评论区

加载中...

相关推荐

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

springboot读取外部配置文件

springboot项目打成jar包后不好进行配置文件修改,可设置为读取外部配置文件,方便进行配置修改.步骤:1.将jar包中的application.properties配置文件复制到自定义路径下;2.运行jar包命令指定外部配置文件路径:nohupjavajar.jarspring.config.location

一份DevOps工程师职责清单,待你查阅

如果一个组织的开发人员和运维人员是独立工作的模式,实施DevOps就需要对组织进行大的调整。因为,只有具备合适的组织人员,文化和工具来才能成功实施DevOps。根据显示,实施DevOps的最常见的障碍之一是员工缺乏技能。什么是DevOps工程师?DevOps工程师是一位IT专家,应该对开发和运维工作都有广泛的了解,包括编码,基础

OpenWrt 对外网开放vsftp服务和samba服务

对WAN开放vsFTPOpenWrt默认启动了vsftp服务,在Luci上没找到配置界面,但是后台是有这个服务的,如果在Openwrt的lan下,可以直接使用FileZilla之类的客户端连接.其配置文件位于/etc/vsftpd.conf如果需要对wan提供服务,需要做以下设置:1\.修改/etc/v

SkyWalking链路监控(一):SkyWalking快速搭建

简介当分布式系统服务比较多,特别是微服务,出现故障就很难排查。所以需要借助APM系统进行排查(ApplicationPerformanceManagement,即应用性能管理),SkyWalking是APM系统的一种,类似的产品还有CAT、Zipkin、Pinpoint。SkyWalking和Pinpoint相比其他系统,做到了无侵入性

MySQL数据库优化技巧

MySQL优化三大方向①优化MySQL所在服务器内核(此优化一般由运维人员完成)。②对MySQL配置参数进行优化(my.cnf)此优化需要进行压力测试来进行参数调整。③对SQL语句以及表优化。MySQL参数优化1:MySQL默认的最大连接数为100,可以在mysql客户端使用以下命令查看mysql