一个针对高并发、低延迟应用设计的高性能 Java 性能监控和统计工具。
一个针对高并发、低延迟应用设计的简单、快速且无侵入的Java方法性能监控和统计工具。受 perf4j 和 TProfiler 启发而来。
perf4j开源项目地址:https://github.com/perf4j/perf4j
TProfiler开源项目地址:https://github.com/alibaba/TProfiler
MyPerf4J 具有以下几个优势:
高性能: 单线程支持每秒 1600 万次 响应时间的记录,每次记录只花费 63 纳秒
无侵入: 采用 JavaAgent 方式,对应用程序完全无侵入,无需修改应用代码
低内存: 采用内存复用的方式,整个生命周期只产生极少的临时对象,不影响应用程序的 GC
高实时: 支持秒级统计,最低统计粒度为 1 秒,并且是全量统计,不丢失任何一次记录
以下是 MyPerf4J 的最基本的几个需求:
能统计出方法的 RPS、Avg、Min、Max、StdDev、TP90、TP95、TP99、TP999 等性能指标
可配置
可指定统计某些类、某些方法
可指定不统计某些类、某些方法
拥有极致的性能
不影响应用的 GC
不影响应用的 RT
性能指标的处理可以定制化,例如:日志收集、上报给日志收集服务等
MyPerf4J 可以统计以下几个性能指标:
MyPerf4J 支持两种部署结构:
3.x
3.x 及其之前
各组件说明
组件 | 说明 |
Java Application | MyPerf4J 的运行容器 |
MyPerf4J | Metrics 收集和统计 |
Collector | 日志收集器 |
Storage | 存储平台 |
Dashboard | 可视化平台 |
各组件关系说明
MyPerf4J 定时把指定时间片内的统计数据写入日志文件
Collector 从日志文件中读取统计数据,并写入 Storage
Dashboard 从 Storage 中读取数据并展示
保持 MyPerf4J 的精简
健壮性,不论是 Collector、Storage 还是 Dashboard 宕掉都不影响 MyPerf4J 的数据采集,也不丢失采集到的数据
多样性,Collector 可以是 Telegraf 也可以是 Filebeat;Storage 可以是 InfluxDB 也可以是 OpenTSDB; Dashboard 可以是 Grafana 也可以是 Chronograf
1、数据采集
MyPerf4J 目前采用 ASM 字节码修改框架在 JVM 加载类时修改 Java 方法的字节码:
在方法的开头加入 long start = System.nanoTime();
在方法的结尾加入 ProfilingAspect.profiling(start, methodId);
,其中 methodId 为类加载时为每一个方法分配的唯一 ID
2、数据存储
timingArr
) + Map(timingMap
) 的方式把方法的响应时间存储到 Recorder中:在 timingArr 中,数组下标代表方法的响应时间,而下标对应的元素值代表该响应时间出现的次数,例如从上图中可以看出,timingArr 中记录了 100次 0ms 的响应,0次 1ms 的响应,80次 2ms 的响应 和 20次 3ms 的响应
在 timingMap 中,Map 的 key 代表方法的响应时间,而 key 对应的 value 代表该响应时间出现的次数,例如,从上图中可以看出,timingMap 中记录了 120次 1010ms 的响应,102次 1025ms 的响应,95次 1041ms 的响应,1次 2094ms 的响应。
3、MyPerf4J 内存占用分析
timingArr
长度为 1000,timingMap
大小为 128,Recorders
转盘的数量为 3每个 timingArr
占用 1000 * 4B = 4KB 的空间
timingMap
占用的空间大约为 128 * (96B + 4B) = 12.5KB,其中,每个 Map.Node 占用 96BtimingMap
扩容和缩容时会分配少量内存外,所有的对象在 MyPerf4J 的整个生命周期中只分配一次!支持 弹性内存管理 后,MyPerf4J 可以降低数十倍的内存占用!为简化处理流程,以 timingArr 为例进行说明:
第一步,通过遍历一次 timingArr 可以计算出所有响应时间及其出现的次数,把这份数据存入 sortedRecords 中,在 sortedRecords 偶数下标代表响应时间,奇数下标代表次数,以上图为例,0ms 出现 100次,2ms 出现 80次,3ms 出现 20次。
第二步,把 sortedRecords 按逻辑展开,即可快速计算出 TP50、TP90 和 TP99 等指标,以上图为例,把 sortedRecords 按逻辑展开,可以得到连续存放的 100 个 0、80 个 2 和 20 个 3,一共 200 个响应时间,那么 TP50 对应于下标为 200 * 50% = 100 的值,也就是 2ms。
Recorder
来记录方法的响应时间,采用 Recorders
存放同一个时间片内的所有 Recorder
,采用 List<Recorders>
存放所有的 Recorders
并且维护一个 curIndex
指向当前时间片的 Recorders
;所有的 Recorder
和 Recorders
均在应用启动时就分配好,当当前时间片结束时,curIndex
便指向 List<Recorders>
中下一个 Recorders
作为下一个时间片的 Recorders
,并计算出当前时间片的性能指标,如此往复。MyPerf4J 自 2.8.0 开始支持弹性内存管理,MyPerf4J 在应用启动时,会根据应用上一次运行期间的方法响应时间的分布计算出本次运行所需分配的内存,可以有效的减小每个 Recorder
的 timingArr
的大小,既可以大幅减少 MyPerf4J 的内存占用,也可以提高性能指标的计算速度!
下载并解压 MyPerf4J-ASM.zip
阅读解压出的 README
文件
修改解压出的 MyPerf4J.properties
配置文件中 app_name
、metrics.log.xxx
、 filter.packages.include
的配置值
2、配置模板文件MyPerf4J.properties如下:
# MyPerf4J 所有配置请参考:https://github.com/LinShunKang/MyPerf4J/wiki/%E9%85%8D%E7%BD%AE
# 配置监控应用的名称
app_name = MyApp
# 是否开启 debug 模式,可配置为 true/false
debug = true
###############################################################################
# HTTP Server Configuration #
###############################################################################
# 配置 HTTP Server 端口号
# 格式为:首选端口,备选最小端口,备选最大端口
http.server.port = 2048,2000,2040
# 配置 HTTP Server 最小工作线程数
http.server.min_workers = 1
# 配置 HTTP Server 最大工作线程数
http.server.max_workers = 2
# 配置 HTTP Server 排队数, worker 线程数达到最大时,接受排队的请求个数
http.server.accept_count = 1024
###############################################################################
# Metrics Configuration #
###############################################################################
# 配置 MetricsExporter 类型
# log.stdout: 以标准格式化结构输出到 stdout.log
# log.standard: 以标准格式化结构输出到磁盘
# log.influxdb: 以 InfluxDB LineProtocol 格式输出到磁盘
# http.influxdb: 以 InfluxDB LineProtocol 格式发送至 InfluxDB server
metrics.exporter = log.stdout
# 配置各项监控指标日志的文件路径
# 如果 metrics.exporter 配置为 log.influxdb,建议把所有的 metrics.log.* 路径配置成一样以方便 Telegraf 收集
metrics.log.method = /data/logs/MyPerf4J/metrics.log
metrics.log.class_loading = /data/logs/MyPerf4J/metrics.log
metrics.log.gc = /data/logs/MyPerf4J/metrics.log
metrics.log.memory = /data/logs/MyPerf4J/metrics.log
metrics.log.buff_pool = /data/logs/MyPerf4J/metrics.log
metrics.log.thread = /data/logs/MyPerf4J/metrics.log
metrics.log.file_desc = /data/logs/MyPerf4J/metrics.log
metrics.log.compilation = /data/logs/MyPerf4J/metrics.log
# 配置日志文件滚动时间间隔,分别有 MINUTELY、HOURLY 和 DAILY 三个值
metrics.log.rolling.time_unit = DAILY
# 配置历史日志文件保留个数
metrics.log.reserve.count = 7
# 配置方法指标采集的时间片,单位为 ms,最小 1s,最大 600s
metrics.time_slice.method = 10000
# 配置 JVM 指标采集的时间片,单位为ms,最小1s,最大600s
metrics.time_slice.jvm = 10000
# 是否展示方法参数类型
metrics.method.show_params = true
# 配置 Java 类的层级映射关系
# 规则为:LevelA:[classNameExpA1,classNameExpA2];LevelB:[classNameExpB1,classNameExpB2];
metrics.method.class_level_mapping = Filter:[*Filter];Handler:[*Handler];
###############################################################################
# InfluxDB Configuration #
###############################################################################
influxdb.version = 1.0
influxdb.orgName = <yourOrgnation>
influxdb.host = 127.0.0.1
influxdb.port = 8086
influxdb.database = <yourDatabase>
influxdb.username = admin
influxdb.password = admin123
# 配置超时时间,单位:ms
influxdb.conn_timeout = 3000
influxdb.read_timeout = 5000
###############################################################################
# Filter Configuration #
###############################################################################
# 配置需要监控的package,可配置多个,用英文';'分隔
# com.demo.p1 代表包含以 com.demo.p1 为前缀的所有包和类
# [] 表示集合的概念:例如,com.demo.[p1,p2,p3] 代表包含以 com.demo.p1、com.demo.p2 和 com.demo.p3 为前缀的所有包和类,等价于 com.demo.p1;com.demo.p2;com.demo.p3
# * 表示通配符:可以指代零个或多个字符,例如,com.*.demo.*
filter.packages.include = cn.perf4j.demo;
# 配置不需要监控的package,可配置多个,用英文';'分隔
filter.packages.exclude = cn.perf4j.demo.dao.DemoDAOImpl
# 配置不需要进行监控的方法名,每个方法名用英文 ';' 分隔
#filter.methods.exclude = getId1 代表排除所有方法名为 getId1 的方法
#filter.methods.exclude = DemoServiceImpl.getId1 代表排除类 DemoServiceImpl 中所有方法名为 getId1 的方法
#filter.methods.exclude = DemoServiceImpl.getId1(long) 代表排除类 DemoServiceImpl 中方法签名为 getId1(long) 的方法
filter.methods.exclude = equals;toString;hashCode;wait
# 配置是否排除私有方法,true/false
filter.methods.exclude_private = true
# 配置需要排除的 ClassLoader,可配置多个ClassLoader,用英文';'分隔
filter.class_loaders.exclude =
###############################################################################
# Recorder Configuration #
###############################################################################
# 配置 Recorder 模式,可配置为 accurate/rough
recorder.mode = ACCURATE
# 配置 Recorders 转盘的数量
recorders.backup_count = 3
# 指定通用的方法执行时间阈值,单位为 ms
recorder.size.timing_arr = 1500
# 指定一个时间片内,超过方法执行时间阈值的次数,仅在 recorder.mode = accurate 时有效
recorder.size.timing_map = 128
在 JVM 启动参数里加上以下两个参数
-javaagent:/path/to/MyPerf4J-ASM.jar
-DMyPerf4JPropFile=/path/to/MyPerf4J.properties
形如:java -javaagent:/path/to/MyPerf4J-ASM.jar -DMyPerf4JPropFile=/path/to/MyPerf4J.properties
-jar yourApp.jar
注意:如果您使用 JDK9 及其之上的版本,请额外添加
--add-opens java.base/java.lang=ALL-UNNAMED
启动应用,监控日志输出到 /path/to/log/method_metrics.log:
MyPerf4J Method Metrics [2020-01-01 12:49:57, 2020-01-01 12:49:58]
Method[6] Type Level TimePercent RPS Avg(ms) Min(ms) Max(ms) StdDev Count TP50 TP90 TP95 TP99 TP999 TP9999
DemoServiceImpl.getId2(long) General Service 322.50% 6524 0.49 0 1 0.50 6524 0 1 1 1 1 1
DemoServiceImpl.getId3(long) General Service 296.10% 4350 0.68 0 1 0.47 4350 1 1 1 1 1 1
DemoServiceImpl.getId4(long) General Service 164.60% 2176 0.76 0 1 0.43 2176 1 1 1 1 1 1
DemoServiceImpl.getId1(long) General Service 0.00% 8704 0.00 0 0 0.00 8704 0 0 0 0 0 0
DemoDAO.getId1(long) DynamicProxy DAO 0.00% 2176 0.00 0 0 0.00 2176 0 0 0 0 0 0
DemoDAO.getId2() DynamicProxy DAO 0.00% 2176 0.00 0 0 0.00 2176 0 0 0 0 0 0
在 JVM 启动参数中去掉以下两个参数,重启即可卸载此工具。
-javaagent:/path/to/MyPerf4J-ASM.jar
-DMyPerf4JPropFile=/path/to/MyPerf4J.properties
git clone git@github.com:LinShunKang/MyPerf4J.git
mvn clean package
MyPerf4J-ASM-${MyPerf4J-version}.jar 在 MyPerf4J-ASM/target/ 目录下
最后附上项目地址:
github地址:https://github.com/LinShunKang/MyPerf4J
gitee地址:https://gitee.com/mirrors/MyPerf4J
中文文档:https://github.com/LinShunKang/MyPerf4J/wiki/Chinese-Doc
资料获取
VeryCapture,一款堪称最强的截图、录制、识别工具!人人必备!