微信搜索 程序员的起飞之路 可以加我公众号,保证一有干货就更新~
(回复关键字“资料”可以获取小弟多年精华,懂的都懂~)
是否还为接口计时而烦恼?
是否还在无脑的复制 System.currentTimeMillis()
?
是否还在为定位“慢代码瓶颈”而苦苦思索?
我为大家来介绍一个神器 —— StopWatch!让 Spring 来帮你统计时间吧!
一、背景
相信大家肯定遇到过我开头提到过的几种问题吧。也相信各位一定写过如下重复无意义的计时代码吧。当一段代码耗时极长,并且调用接口众多时,我们就不得不去分步统计看到底是哪个接口拖后腿,并以此定位接口性能瓶颈在哪里。那有如下代码就不奇怪了,我常常在想,是否有一套工具来帮我们统计接口耗时、占比,帮我们分析慢接口、慢调用呢?直到我遇到了他——StopWatch!
1 | java复制代码 public static void func1() throws InterruptedException { |
二、初遇
初遇 StopWatch
是同事跟我讲,说有个东西可以替代你这里的 end - start
代码。我抱着不屑一顾的态度去看了一眼他的代码,看到了 StopWatch
,追进源码大致一看,这归根结底不还是用了 System.nanoTime
去减了一下,跟我的有什么区别呀。当然,如果故事终结于此,也就不会有这篇博客了。让我决定深追下去的只有一个原因,这个东西是 Spring 家的,而且 Spring 用了他去做接口时间的统计如图所示:
由于我个人对 Spring 的盲目崇拜,觉得他用啥都是好的!我决定好好研究一下这个 StopWatch
,事实证明,真香!
三、深究
3.1 使用
对一切事物的认知,都是从使用开始,那就先来看看它的用法。开头一段的代码,在替换成 StopWatch
后,会如下所示:
1 | java复制代码 public static void func2() throws InterruptedException { |
乍一眼看上去,是不是觉得这代码反而比之前的还要多了。但是如果实际写起来,实际上是要比第一种好写许多的。只管控制每段代码统计的开始和结束即可,不用关心是哪个时间减哪个时间。执行结果更为喜人:
如图所示,StopWatch
不仅正确记录了上个任务的执行时间,并且在最后还可以给出精确的任务执行时间(纳秒级别)和耗时占比。其实并不止于此,StopWatch
还可以记录整个任务的走向流程,例如走过了哪几个任务,各个耗时都是可以通过方法拿到的。例如:
1 | java复制代码System.out.println("stopWatch.getLastTaskName() = " + stopWatch.getLastTaskName()); |
执行结果如下:
这个链路和信息目前看上去可能没什么用,在后面的扩展章节我会说明
3.2 源码
老规矩,由浅入深。看完用法,我们来看看源码。先看下组成 StopWatch
的属性
1 | java复制代码 /** |
StopWatch
内部持有一个内部类 TaskInfo
,内有两个属性
1 | java复制代码 /** |
这里要重点提一下,可能有的读者朋友看到的源码跟我这里贴出来的不一样。这是因为在 Spring 5.2 之前的 StopWatch
中统一使用的毫秒,即 TimeMillis
。而到 Spring 5.2 及其以后的版本,都统一改为纳秒即 TimeNanos
。
看完属性相信大家也差不多猜出来具体的实现了,不过就是维护了一个任务链表,然后开始的时候记一个时间,结束的时候记一个时间,最后取的时候减一下。此处由于篇幅原因就仅放 start
和 stop
两个方法的源码。大家如果对其余的方法感兴趣,请自行查阅源码(spring-core 模块 org.springframework.util.StopWatch 类)。
1 | java复制代码 /** |
3.3 拓展
这里想聊一下 StopWatch
的用法,其实上述代码这种方法内定义一个,然后在同一个方法中使用是最基础的用法。下面给大家提供几个思路。(用法不仅限于此,大家可开动脑筋自行开发~)
- 在 Controller 层或 Service 层将
StopWatch
放入ThreadLocal
当中。在整条 Service 调用链中,使用同一个StopWatch
对象记录方法调用的耗时和路线。最后在结束时生成方法的耗时占比,以定位性能瓶颈。 - 利用
Spring-AOP
方式,在每个方法的开始和结束使用StopWatch
记录接口耗时时间。 - 声明一个注解,利用
Spring-AOP
和注解的方式,自定义分析带注解的方法耗时。
在这几种情况下,3.1 章节中所说的任务调用链路就非常重要了。这个调用链路将是定位耗时的非常重要的手段。
四、总结
任何东西都有利害两面,最后我们来看下优点和缺点
优点:
- 方便统计耗时,通过指定任务名称和耗时占比分析,可以清晰明确的定位到耗时慢的接口和调用。
- 记录整条任务链路,方便全链路分析。
- 仅需要关注代码耗时的起始点和终点,无需关注到底需要哪个时间减哪个时间
缺点:
- 同一个
StopWatch
只能开启一个任务,无法统计 “包含” 类的耗时。例如 A 任务中包含 B 任务这种,只能再开启一个StopWatch
实例 - 无法中间插入一个任务,例如 先执行 A 任务,再执行 B 任务,再执行 A 任务。无法将 B 任务的执行时间单独隔离开。
读书越多越发现自己的无知,Keep Fighting!
欢迎友善交流,不喜勿喷~
Hope can help~
我的博客即将同步至 OSCHINA 社区,这是我的 OSCHINA ID:osc_13668000,邀请大家一同入驻:www.oschina.net/sharing-pla…
本文转载自: 掘金