Skip to content

Commit

Permalink
升级项目版本号至1.2;完善README
Browse files Browse the repository at this point in the history
  • Loading branch information
linshunkang committed Apr 29, 2018
1 parent 57abe5e commit 9c21eea
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 130 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ target
*.iml
.idea
.idea/*.xml
*.versionsBackup
12 changes: 9 additions & 3 deletions MyPerf4J-ASM/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>MyPerf4J</artifactId>
<groupId>MyPerf4J</groupId>
<version>1.0-SNAPSHOT</version>
<version>1.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand All @@ -22,8 +22,14 @@
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-all</artifactId>
<version>5.1</version>
<artifactId>asm</artifactId>
<version>6.1.1</version>
</dependency>

<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>6.1.1</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public boolean initOther() {

private boolean initProfilerAspect() {
try {
ProfilerAspect.setRecorderMaintainer(maintainer);
ProfilerAspect.setRecorderMaintainer((ASMRecorderMaintainer) maintainer);
ProfilerAspect.setRunning(true);
return true;
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package cn.myperf4j.asm;

import cn.myperf4j.core.AbstractRecorder;
import cn.myperf4j.core.AbstractRecorderMaintainer;
import cn.myperf4j.core.ProfilerParams;
import cn.myperf4j.core.util.MapUtils;
Expand Down Expand Up @@ -31,6 +32,11 @@ public boolean initOther() {
return true;
}

@Override
public AbstractRecorder getRecorder(String api) {
return recorderMap.get(api);
}

@Override
public void addRecorder(String tag, ProfilerParams params) {
synchronized (locker) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
package cn.myperf4j.asm.aop;

import cn.myperf4j.asm.ASMBootstrap;
import cn.myperf4j.asm.ASMRecorderMaintainer;
import cn.myperf4j.core.AbstractRecorder;
import cn.myperf4j.core.AbstractRecorderMaintainer;
import cn.myperf4j.core.util.Logger;

/**
* Created by LinShunkang on 2018/4/15
*/
public final class ProfilerAspect {

private static AbstractRecorderMaintainer recorderMaintainer;
private static ASMRecorderMaintainer recorderMaintainer;

private static boolean running = false;

static {
ASMBootstrap.getInstance().initial();
}

public static void profiling(long startNanos, String tag) {
try {
if (!running) {
Expand All @@ -33,13 +28,12 @@ public static void profiling(long startNanos, String tag) {

long stopNanos = System.nanoTime();
recorder.recordTime(startNanos, stopNanos);
// Logger.info("ProfilerAspect.doProfiling(): tag=" + tag + ", cost: " + (stopNanos - startNanos) + "ns, RECORDED!!!");
} catch (Exception e) {
Logger.error("ProfilerAspect.doProfiling(" + startNanos + ", " + tag + ")", e);
}
}

public static void setRecorderMaintainer(AbstractRecorderMaintainer recorderMaintainer) {
public static void setRecorderMaintainer(ASMRecorderMaintainer recorderMaintainer) {
ProfilerAspect.recorderMaintainer = recorderMaintainer;
}

Expand Down
2 changes: 1 addition & 1 deletion MyPerf4J-AspectJ/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>MyPerf4J</artifactId>
<groupId>MyPerf4J</groupId>
<version>1.0-SNAPSHOT</version>
<version>1.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
2 changes: 1 addition & 1 deletion MyPerf4J-Core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>MyPerf4J</artifactId>
<groupId>MyPerf4J</groupId>
<version>1.0-SNAPSHOT</version>
<version>1.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public void recordTime(long startNanoTime, long endNanoTime) {
int elapsedTime = (int) ((endNanoTime - startNanoTime) / 1000000);
if (elapsedTime < timingArr.length()) {
timingArr.incrementAndGet(elapsedTime);
return;
}

//丢弃timingArr无法存储的记录!!!
Expand Down
157 changes: 99 additions & 58 deletions README.CN.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
# MyPerf4J
一个简单快速的Java接口性能监控和统计工具。受[perf4j](https://github.com/perf4j/perf4j)启发而来。
一个极快的Java接口性能监控和统计工具。受[perf4j](https://github.com/perf4j/perf4j)启发而来。

## 多语言文档
* English [README.md](https://github.com/ThinkpadNC5/MyPerf4J/blob/develop/README.md)
* 中文

## 背景
* 我需要一个能统计接口响应时间的程序
* [perf4j](https://github.com/perf4j/perf4j)现有的统计结果不能满足我的需求

## 需求
* 能统计出接口的RPS、TP50、TP90、TP95、TP99、TP999、TP9999等性能指标
* 可以通过注解进行配置,注解可以配置到类和/或接口上
* 可以通过注解进行配置,注解可以配置到类和/或方法上
* 尽量不占用过多的内存、不影响程序的正常响应
* 性能指标的处理可以定制化,例如:日志收集、上报给日志收集服务等

Expand All @@ -17,7 +21,7 @@
- 如果单纯的使用Map来存储数据,必然会占用大量不必要的内存
- 根据二八定律,绝大多数的接口响应时间在很小的范围内,这个很小的范围特别适合使用数组来存储,数组下标即为响应时间,对应元素即为该响应时间对应的数量;而少数的接口响应时间分布的范围则会比较大,适合用Map来存储;
- 综上所述,核心数据结构为:数组+Map,将小于某一阈值的响应时间记录在数组中,将大于等于该阈值的响应时间记录到Map中
* 利用AOP进行响应时间的采集
* 利用AOP进行响应时间的采集,包括[AspectJ](https://github.com/eclipse/org.aspectj)[ASM](http://asm.ow2.io/)
* 通过注解的方式进行配置,并可以通过参数配置调优核心数据结构的内存占用
* 通过同步的方式采集响应时间,避免创建过多的Runnable对象影响程序的GC
* 通过异步的方式处理采集结果,避免影响接口的响应时间
Expand All @@ -44,73 +48,96 @@
- 机器配置
- CPU Intel(R) Core(TM) i7-7920HQ CPU@3.10GHz
- RAM 16GB 2133MHz LPDDR3
* 核心数据结构压测

* MyPerf4J-AspectJ
- 为了避免由于空方法执行过快导致多个线程高度竞争资源导致压测结果出现明显的性能下降,通过轮询的方式执行8个空方法,然后把8个方法的RPS相加得出结果
- 时间片为10s,每次压测中间停顿20s,并且执行`System.gc();`

| 线程数 | 每线程循环数| RPS |
|-------|-----|------|
|1|100000000|16666666|
|2|100000000|18181818|
|4|100000000|22222222|
|8|100000000|29629629|
|1|500000000|3114928|
|2|500000000|5407560|
|4|500000000|9620888|
|8|500000000|11682240|

* 整体压测 - 包括AOP
- 只压测一个接口并且被压测接口的实现为空方法
* MyPerf4J-ASM
- 为了避免由于空方法执行过快导致多个线程高度竞争资源导致压测结果出现明显的性能下降,通过轮询的方式执行8个空方法,然后把8个方法的RPS相加得出结果
- 时间片为10s,每次压测中间停顿20s,并且执行`System.gc();`

| 线程数 | 每线程循环数| RPS |
|-------|-----|------|
|1|100000000|3550396|
|2|100000000|5177145|
|4|100000000|9153783|
|8|100000000|11994302|
|1|500000000|11767056|
|2|500000000|14972208|
|4|500000000|28561824|
|8|500000000|45966888|

* 压测结论
- 从整体压测结果来看,在单线程下每秒可支持143万次的方法调用,平均每次方法调用耗时1.43us,能够满足绝大部分人的要求,不会对程序本身的响应时间造成影响
- 通过对比核心数结构和整体压测的结果,核心数据结构本身并不是瓶颈,瓶颈在于切面及反射所带来的耗时
- 从压测结果来看:
- MyPerf4J-AspectJ在单线程下每秒可支持311万次的方法调用,平均每次方法调用耗时321ns
- MyPerf4J-ASM在单线程下每秒可支持1176万次的方法调用,平均每次方法调用耗时84ns
- 无论是MyPerf4J-AspectJ还是MyPerf4J-ASM都能够满足绝大部分人的要求,不会对程序本身的响应时间造成影响
- 性能差异原因:
- MyPerf4J-AspectJ在每次方法调用时都会生成一个JoinPoint对象,随着调用次数的增加JVM会不断的触发YoungGC
- MyPerf4J-ASM是通过ASM框架修改类的字节码,在方法前后插入两行方法,不产生多余的对象,在整个压测过程中不会触发任何的GC(除了代码中执行的`System.gc();`

## 使用
*`pom.xml`引入Maven依赖

```
<dependency>
<groupId>MyPerf4J</groupId>
<artifactId>MyPerf4J</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
```

*`pom.xml`加入build plugin

```
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.10</version>
<configuration>
<complianceLevel>1.7</complianceLevel>
<source>1.7</source>
<aspectLibraries>
<aspectLibrary>
<groupId>MyPerf4J</groupId>
<artifactId>MyPerf4J</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
```

* 在你想要分析性能的类或方法明上加上 `@Profiler`注解,同时对于不想进行性能分析的接口上加上 `@NonProfiler`
* 使用MyPerf4J-ASM
*`pom.xml`引入Maven依赖

```
<dependency>
<groupId>MyPerf4J</groupId>
<artifactId>MyPerf4J-ASM</artifactId>
<version>1.2</version>
</dependency>
```
* 在JVM启动参数里加上: -javaagent:/your/path/to/MyPerf4J-ASM-1.2.jar
* 使用MyPerf4J-AspectJ
* 在`pom.xml`引入Maven依赖
```
<dependency>
<groupId>MyPerf4J</groupId>
<artifactId>MyPerf4J-AspectJ</artifactId>
<version>1.2</version>
</dependency>
```
* 在`pom.xml`加入build plugin
```
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.10</version>
<configuration>
<complianceLevel>1.7</complianceLevel>
<source>1.7</source>
<aspectLibraries>
<aspectLibrary>
<groupId>MyPerf4J</groupId>
<artifactId>MyPerf4J-AspectJ</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
```
* 在你想要分析性能的类或方法明上加上 `@Profiler`注解,同时对于不想进行性能分析的方法上加上 `@NonProfiler`
```
package cn.perf4j.test.profiler;
Expand Down Expand Up @@ -173,10 +200,15 @@ public class MyPerfStatsProcessor implements PerfStatsProcessor {
MyPerf4J.PSP=cn.perf4j.test.profiler.MyPerfStatsProcessor
MyPerf4J.RecMode=accurate
MyPerf4J.MillTimeSlice=60000
MyPerf4J.IncludePackages=cn.perf4j;org.myperf4j
MyPerf4J.ExcludePackages=org.spring;
MyPerf4J.Debug=true
```
* 执行命令 `mvn clean package`
* 运行你的程序
* 输出结果
```
Expand All @@ -203,3 +235,12 @@ ProfilerTestApiImpl.test3 0 -1 -1 -1 -1 -1
- 对于内存敏感或精度要求不是特别高的应用,推荐使用Rough模式
- 对于内存不敏感且精度要求特别高的应用,推荐使用Accurate模式
## 关于MyPerf4J-AspectJ与MyPerf4J-ASM
* MyPerf4J-AspectJ
* 利用[AspectJ](https://github.com/eclipse/org.aspectj)实现切面的织入
* 从技术角度来说更加成熟
* 速度稍慢,会产生不必要的对象而影响程序本身的GC
* MyPerf4J-ASM
* 利用[ASM](http://asm.ow2.io/)来实现切面的织入
* 从技术角度来说,由于是自己编写修改字节码的逻辑,存在一定的风险
* 速度极快!不产生任何不必要的对象,不影响程序本身的GC
Loading

0 comments on commit 9c21eea

Please sign in to comment.