java agent-02-Java Instrumentation API
Java Instrumentation API是Java平台提供的一组API,它允许开发者在运行时通过修改字节码来监测和改变Java应用程序的行为。
这个API在Java SE 5(JDK 5)引入,为开发者提供了访问和操作类加载、字节码修改和类转换的能力,使得在Java应用程序运行时进行动态修改成为可能。
Java Instrumentation API的主要组成部分包括两个接口:Instrumentation和ClassFileTransformer。
1.Instrumentation接口:
Instrumentation接口是Java Instrumentation API的核心部分,它位于java.lang.instrument包中。
该接口允许代理程序(即Java代理)与JVM进行交互,并在类加载过程中对类进行修改。
通过Instrumentation接口,代理程序可以实现以下功能:
•获取已加载的类信息,包括类的名称、方法、字段等。•修改类的字节码,实现对类的增强和拦截。•动态定义新的类。•获取和修改类加载器,实现自定义类加载器的功能。
要使用Instrumentation接口,代理程序需要在premain或agentmain方法中获取一个Instrumentation实例。这些方法是Java代理程序的入口点,premain在应用程序启动时调用,agentmain在应用程序运行时动态加载代理时调用。
2.ClassFileTransformer接口: ClassFileTransformer接口是用于在类加载过程中修改类字节码的关键接口。它位于java.lang.instrument包中。
通过实现ClassFileTransformer接口,代理程序可以在类被加载之前或之后,对类的字节码进行修改。在类加载期间,JVM会调用ClassFileTransformer的transform方法,代理程序可以在该方法中实现对类字节码的修改,并返回修改后的字节码。
ClassFileTransformer接口的方法:
•loader:表示加载当前类的类加载器。•className:表示正在加载的类的全限定名。•classBeingRedefined:表示正在重定义的类的Class对象,若不是重定义,则为null。•protectionDomain:表示保护该类域的域对象。•classfileBuffer:表示类的字节码内容。
Java Instrumentation API为开发者提供了强大的能力,使得在Java应用程序运行时对类进行修改和增强成为可能。
但同时也需要注意,使用Java Instrumentation API需要小心谨慎,确保代理程序的操作是合理的,并且不会影响应用程序的稳定性和安全性。
实现接口
•MyAgent.java
最核心的就是最下面的方法。
•MyTransformer.java
我们只针对 com 开头的方法进行增强。
我们新建一个文件 src\\main\\resources\\META-INF\\MANIFEST.MF,内容如下:
需要指定我们定义的 Agent 路径。
生成的 agent jar 完整路径:
第一次测试的时候,发现自定义的 MANIFEST.MF 并没有被打包到最总的 jar 中,会导致找不到 Agent 信息。
我们可以在 pom 中指定:
我们创建一个简单的测试类
1) 编译
编译上面的类,然后到 classes 目录下
2) 运行
效果如下
https://blog.csdn.net/xixi8865/article/details/23849125
提供方耗时正常,调用方毛刺频频
作者:京东零售 王森
调用方A -> JSF -> 提供方B
大多数情况下,调用方耗时 和 提供方耗时 基本没有差别
个别情况下,调用方耗时 远高于 提供方耗时,大概5分钟20+次
在调用JSF接口前后加的监控,没有其他任何逻辑,包括日志打印
在代码最外层JSF接口加的监控,之外没有任何代码逻辑
调用方从请求到接收数据,除了提供方业务耗时,还有其他环节,分别是
1.调用方容器和宿主机
2.调用方->提供方经过的网络环节
3.提供方容器和宿主机的环节
4.提供方->调用方的网络环节
容器和宿主机之间由于流量过大,处理压力大导致的瓶颈
网络波动
一步一步排除,先看网络
找到监控相关的技术同学,回答说没有网络的监控
于是找到了JDOS的同学,排查后提供了一种怀疑方向,如下图
容器内存使用率(包含cache)基本一直保持在99%以上,建议先确定该指标的影响,并降低该指标
指标定义文档解释如下
还是看不太懂指标的含义,懵B状态
提工单咨询,给出的解决方案如下
java应用,无ngix,还是懵,继续求助
最后得出结论:
这个之前在营销中心那边有遇到C++ 使用page cache 还有使用zgc的 需要参考一下cache这个指标,其他的场景 目前看 系统会在物理内存不够用的时候 释放cache;
这个是指有的c++应用底层接口直接使用了pagecache,java可以忽略
更详细解释:
内存那部分是这样的,每个容器的 Memory Cgroup 在统计每个控制组的内存使用时包含了两部分,RSS 和 Page Cache。
RSS 是每个进程实际占用的物理内存,它包括了进程的代码段内存,进程运行时需要的堆和栈的内存,这部分内存是进程运行所必须的。
Page Cache 是进程在运行中读写磁盘文件后,作为 Cache 而继续保留在内存中的,它的目的是为了提高磁盘文件的读写性能。(Java程序只要操作磁盘读写也会用到 page cache)
有时会看到这样一种情况:容器里的应用有很多文件读写,你会发现整个容器的内存使用量已经很接近 Memory Cgroup 的上限值了,但是在容器中我们接着再申请内存,还是可以申请出来,并且没有发生 OOM。那是因为容器中有部分是PageCache,当容器需要更多内存时,释放了PageCache,所以总大小并没有变化。
结论:对于java系统来说,容器内存使用率(包含cache)没有影响(cache会自动释放)
虽说没有影响,还是想办法降低试试效果(非常相信大佬)
看了其他几个java集群
看到最后一个图,小小分析了下,发现三个小时会降低一波,正好和日志清除的时间间隔一致。
对提供方B清除日志后发现果然降低,如下
但是毛刺依然存在!!
扩容前,CPU和内存也处于正常水平
扩容后(汇天4台 -> 汇天8台),CPU和内存没啥太多变化
调用方耗时如下,基本没啥变化,头大
运维的同事帮忙分析了一波,给出年轻代GC耗时较高可能会影响耗时;如下
找了两个毛刺的数据,找到对应提供方的机器,查看那一分钟内有yanggc耗时(分钟的粒度),计算下来,调用方耗时比较接近 提供方耗时+提供方yanggc耗时,但是没有直接采取措施,主要一下原因
1.yanggc粒度比较粗,分钟级
2.一直认为FullGC会导致STW,增加耗时,yangGc不会有太大影响
3.只有一两次的数据分析,数据也没有那么准确
4.备战期间,线上机器封板,动起来比较麻烦,想找下其他原因,双管齐下
1.调用方多台机器,提供方也是多台机器,网络抓包要想抓全得N*M,比较费劲
2.PFinder也是随机抓包
3.毛刺也是随机产生的
想保证,抓到毛刺请求,且,PFinder有数据,采取如下对策
1.选择调用方的一台机器 X 提供方的一台机器,进行抓包
2.监控调用方的这台机器的UMP监控
3.调用方的UMP监控有毛刺时,查看是否有PFinder监控数据,如果没有则继续抓,有则停止
最后抓到了想要的数据
网络运维的同事帮忙对抓网络包,左边是调用方,右边是提供方,如下
调用方的PFinder的数据如下
提供方代码开始PFinder的数据如下
数据分析后结论如下:
调用方22:24:50.775730 发出 22:24:50.988867收到 耗时213ms
提供方22:24:50.775723收到数据包,等到22:24:50.983才处理,22:24:50.988776处理结束回包
提供方等待208ms左右,实际处理4.55ms,加起来 213左右,和调用方耗时对应上了
网络抓包是从容器到容器的抓包
阻塞原因猜测:
1.容器瓶颈,处理不过来 – CPU、内存正常,且当天下午扩容一倍,没有明显好转
2.yanggc照成延迟 – 和运维同学张宪波大佬分析的不谋而合,且是有数据支撑的
1.增大堆内存(年轻代)
2.扩容(已经扩了一次,没有明显变化)
3.mq消费流量切到其他分组(一般先反序列化,根据参数过滤),减少新对象创建
调用方耗时如下
提供方耗时如下
提供方yanggc
调整前后调用方耗时
最终提供方和调用方耗时不一致的问题得到解决
SpringBoot项目调用.Net WebService
最近有个需求,需要在SpringBoot项目中调用.Net开发的WebSerivice接口。由于是第一次搞,自然而然就会出现各种问题,下面就来盘点一下本地调用历程:
我们调用的方法有4个参数,其中有一个复杂类型msgHeader,它包含sourceSystemId、sourceSystemName、pageSize、currentPage等四个参数,所以我们新建一个MsgHeader的实体类来进行该参数的赋值和传参。
接下来,我们采用比较普遍使用的Cxf来进行接口的调用:
开始调用接口,报错:
不能将我本地定义的MsgHeader转换为接口参数的MsgHeader。
于是,网上搜索了一大堆的资料,要在MsgHeader加上命名空间之类的,那就加上试试呗。
谁知道,加完之后还是报错,无语死了,估计是java项目与.net项目之间的类型转换有问题。看来这种方式不行,换种方式吧,突然想到之前用Postman可以调通这个接口,那我们是不是也可以使用Http请求这种方式来进行呢?说干就干,代码很快写好了:
执行,成功获取到结果:
但是结果是字符串,需要解析,比较费劲,还是忍痛放弃吧
那这两种方式都不行,到底怎样才可以呢?突然想起来之前用.Net项目调.Net的WebService的时候,可以用生成WebService代理类的方式进行调用。那我们用SpringBoot项目调用.Net的WebService的时候,完全也可以用生成代理类的方式啊!
那就开始行动吧!首页,先把对应接口的代理类生成了,我使用的是apache-cxf-3.5.10,接下来生成代理类:
输入cmd,执行以下命令:
wsdl2java -keep -encoding UTF-8 -d D:/temp/webservice/cxf/java -compile -classdir D:/temp/webservice/cxf/class -p com.example.webservicedemo.webservices -verbose http://XX.XXX.X.XX:XXXX/Services/QuirySkyProjectEffectSrv.asmx?wsdl
生成的代理类如下:
整理调用接口的代码:
完美!结果完全符合预期!
综上所述,3种方法里生成代理类这种方法是完全可取的。
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。