引言

在平时的开发调试工作或线上中,有时会遇到程序执行效率非常慢,通过一般的经验只能判断出部分逻辑有问题,但判断并不直观且效率较低,不知道方法中哪个阶段比较耗时。目前spring-framework提供了一个StopWatch类可以做类似任务执行时间控制,于是利用此思想重新实现了一套自己的逻辑。

Spring中StopWatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Slf4j
public class TestStopWatch {

private void test() throws InterruptedException {
StopWatch sw = new StopWatch();

sw.start("起床");
Thread.sleep(1000);
sw.stop();

sw.start("洗漱");
Thread.sleep(2000);
sw.stop();

sw.start("锁门");
Thread.sleep(500);
sw.stop();

log.warn("prettyPrint = {}", sw.prettyPrint());
log.info("totalTimeMillis = {}", sw.getTotalTimeMillis());
log.warn("lastTaskName = {}", sw.getLastTaskName());
log.info("lastTaskInfo = {}", sw.getLastTaskInfo());
log.warn("taskCount = {}", sw.getTaskCount());
}


public static void main(String[] args) throws InterruptedException {
TestStopWatch testStopWatch = new TestStopWatch();
testStopWatch.test();
}
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
12-22 21:43:49.468 WARN  c.f.o.d.TestStopWatch - prettyPrint = StopWatch '': running time (millis) = 3505
-----------------------------------------
ms % Task name
-----------------------------------------
01004 029% 起床
02001 057% 洗漱
00500 014% 锁门

12-22 21:43:49.476 INFO c.f.o.d.TestStopWatch - totalTimeMillis = 3505
12-22 21:43:49.476 WARN c.f.o.d.TestStopWatch - lastTaskName = 锁门
12-22 21:43:49.477 INFO c.f.o.d.TestStopWatch - lastTaskInfo = org.springframework.util.StopWatch$TaskInfo@5bd03f44
12-22 21:43:49.477 WARN c.f.o.d.TestStopWatch - taskCount = 3

可以看到,Spring中的StopWatch可以方便的排查程序执行效率,但是结果并不直观且代码侵入性太大。

自定义StopWatch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
@Slf4j
public class StopWatch {
private long startTime;

private long lapStartTime;

private String tagName;

private List<String> steps = new ArrayList<>();

private StopWatch(String tagName) {
this.tagName = tagName;

long start = System.nanoTime();

this.startTime = start;
this.lapStartTime = start;
}

public static StopWatch create(String tagName) {

return new StopWatch(tagName);
}

public void lap(String stepName) {

long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - lapStartTime);
int index = steps.size() + 1;
String step = String.format("T%d(%s)/%d", index, stepName, elapsedTime);
steps.add(step);

//reset
this.lapStartTime = System.nanoTime();
}

public void log() {
StringBuilder stringBuilder = createLog();

log.warn(stringBuilder.toString());
}

public void logSlow(long slow) {

long totalElapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);
if (totalElapsedTime > slow) {
StringBuilder stringBuilder = createLog();
log.warn(stringBuilder.toString());
}
}

private StringBuilder createLog() {
long totalElapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime);

StringBuilder stringBuilder = new StringBuilder(tagName);
stringBuilder.append(' ');
stringBuilder.append(" Total/");
stringBuilder.append(totalElapsedTime);
stringBuilder.append(' ');

for (String step : steps) {
stringBuilder.append(step);
stringBuilder.append(' ');
}
return stringBuilder;
}
}

测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Slf4j
public class TestStopWatch {

private void test() throws InterruptedException {
StopWatch stopWatch = StopWatch.create("test");

Thread.sleep(1000);
stopWatch.lap("起床");

Thread.sleep(2000);
stopWatch.lap("洗漱");

Thread.sleep(500);
stopWatch.lap("锁门");

stopWatch.log();
}

public static void main(String[] args) throws InterruptedException {
TestStopWatch testStopWatch = new TestStopWatch();
testStopWatch.test();
}
}

结果:

1
12-22 21:53:32.696 WARN  c.f.o.r.c.StopWatch - test  Total/3509 T1(起床)/1001 T2(洗漱)/2005 T3(锁门)/500 

通过对比,可以发现自实现的StopWatch类更为简洁,同时也包含了必要的信息,但是功能不如Spring自带的强大。可针对不同的场景使用。