spring,真是一个好东西;性能,真是个让人头疼又不得不面对的问题。如何排查出项目中性能瓶颈?如何迅速定位系统的慢查询?在这我就不说spring自带的性能监控器了,实在是有些简陋。下面就说说我自己写的这个性能监控器。先看看效果:

2013-07-07 19:19:50,440 WARN [main] [aop.PerformanceInterceptor]

|-144 ms; [HelloService.hellpAop]

|+---10 ms; [AnotherService.childMehhod]

|+---21 ms; [AnotherService.childMehhod3]

|+---+---8 ms; [HelloService.parentMehtod]

|+---12 ms; [AnotherService.childMehhod2]

其实,利用spring AOP,任何人都可以写一个定制的监控,但大体思路是一样的,就是在被调用方法的开始之前,记录一下开始时间,在调用方法结束之后,记录结束时间,然后,在调用栈退出前,将日志打印出来。光说不练假把式,下面一步一步构建一个性能监控器。

step1.构造拦截器。

直接上代码,拦截器核心代码:

public Object invoke(MethodInvocation invocation) throws Throwable {

try {

String name = extractLogName(invocation);

//记录开始时间

start(name);

return invocation.proceed();

} finally {

//记录方法结束时间

stop();

}

}

因为最终要打印出来,因此,打印的名称必须在记录时间时把被调用方法的名称也记录下来。方法extractLogName就是干这个的。

step2.构造数据结构

首先,我们需要一个线程变量,存储AOP拦截的每个方法的开始时间及结束时间。就用threadLocal变量了,本人还没想到更好的方法。其次,调用过程其实是在一个方法栈(Stack)中旅行了一遍,被调用的方法总是后进先出。每进一个方法,都要记录一个开始时间,每当退出一个方法,都要记录这个方法运行的结束时间。最终,我们将得到一个类似树形的结构。我们需要定义这个结构,以便我们在退出方法栈时能够将每一个方法所耗费的时间都打印出来。

/**

* 保存监控信息变量

* @author donlianli@126.com

*/

private static class StackData {

/**

* 记录根根节点

*/

public StackEntry root;

/**

* 当前正在调用方法节点

*/

public StackEntry currentEntry;

/**

* 堆栈树高度

*/

public int level;

}

/**

* aop方法性能统计实体

* @author donlianli@126.com

*/

private static class StackEntry {

public String logName ;

public long beginTime;

public long endTime;

/**

* 节点所处高度

*/

public int level;

/**

* 调用的子方法

*/

public List child;

/**

* 上级节点

*/

public StackEntry parent ;

public StackEntry(String logName, long currentTimeMillis) {

this.logName = logName;

this.beginTime = currentTimeMillis;

this.child = new ArrayList(3);

}

}

StackData定义了根节点的结构,StackEntry存储每个方法的开始结束时间,另外在StackData和StackEntry加入level字段,方便后面打印日志。StackData和StackEntry都是作为一个内部类引入的,因为这两个类为了性能,都没有提供一些封装方法,不宜暴露出去(出去多丢人啊)。

好了,结构和拦截器都写好了。只需两步,大工基本就告成了,在拦截器中,在调用方法的前面及后面,记录一个StackEntry对象就可以了。start和stop的代码如下:

public static void start(String logName) {

StackData data = dataHolder.get();

StackEntry currentEntry = new StackEntry(logName, System.currentTimeMillis());

if (data == null) {

data = new StackData();

data.root = currentEntry;

data.level = 1;

dataHolder.set(data);

} else {

StackEntry parent = data.currentEntry;

currentEntry.parent=parent;

parent.child.add(currentEntry);

}

data.currentEntry = currentEntry;

currentEntry.level=data.level;

data.level++;

}

public static void stop() {

StackData data = dataHolder.get();

StackEntry self = data.currentEntry;

self.endTime = System.currentTimeMillis();

data.currentEntry = self.parent;

data.level--;

printStack(data);

}

/**

* 此处还可以进行改进,可以将超时的数据放入一个有界队列

* 里,在另一个线程进行打印。

* @param data

*/

private static void printStack(StackData data) {

if(logger.isWarnEnabled()){

StringBuilder sb = new StringBuilder("\r\n");

StackEntry root = data.root;

appendNode(root,sb);

logger.warn(sb.toString());

}

}

private static void appendNode(StackEntry entry, StringBuilder sb) {

long totalTime = entry.endTime-entry.beginTime ;

if(entry.level ==1){

sb.append("|-");

}

sb.append(totalTime);

sb.append(" ms; [");

sb.append(entry.logName);

sb.append("]");

for(StackEntry cnode : entry.child){

sb.append("\r\n|");

for(int i=0,l=entry.level;i

sb.append("+---");

}

appendNode(cnode,sb);

}

}

等等,还有需求?

1、我们只想找出慢查询,而不想把所有的方法的运行时间都打印出来

2、希望有一个开关,平常不需要监控,在出现问题的时候,才把这个监控打开。

好吧,程序员都是被这些需求给搞死的。

在拦截器中增加一个开关switchOn和一个阈值threshold,当switchOn==true的时候,才进行监控,否则不监控。在监控时,如果整个方法的运行时间小于threshold,不打印日志,因为打印日志会IO,会给方法增加额外的开销。改进后代码如下:

/**

* 性能监控开关

* 可以在运行时动态设置开关

*/

private volatile boolean switchOn = true;

/**

* 方法执行阈值

*/

private volatile int threshold = 100;

public Object invoke(MethodInvocation invocation) throws Throwable {

if(switchOn){

String name = extractLogName(invocation);

try {

start(name);

return invocation.proceed();

} finally {

stop(threshold);

}

}

else {

return invocation.proceed();

}

}

打印日志阈值:

/**

* @param threshold 打印日志的阈值

*/

public static void stop(int threshold) {

StackData data = dataHolder.get();

StackEntry self = data.currentEntry;

self.endTime = System.currentTimeMillis();

data.currentEntry = self.parent;

data.level--;

if(data.root == self && (self.endTime -self.beginTime) > threshold){

printStack(data);

}

}

到此,这个性能监控器几乎算完美了。

但这个监控器是运行在spring AOP上面的,并且,监控的方法必须都是通过interface调用的。所以,如果你要使用这个方法,还要确保你是使用的面向接口的编程。不过,如果你的项目没有使用面向接口,可以利用eclipse自带的工具,将公用方法Extract成interface。

spring怎么配置?拦截器怎么配置?你不会连这个都不会吧,那你搜索一下吧。

PS:暂时未提供spring3.0的实现。

对这类话题感兴趣?欢迎发送邮件至donlianli@126.com

关于我:邯郸人,擅长Java,Javascript,Extjs,oracle sql。

更多我之前的文章,可以访问 我的空间

java aop性能检测_Spring AOP 性能监控器相关推荐

  1. java 切面详解_Spring AOP 切面表达式语法详解

    简述 由于Spring切面粒度最小是达到方法级别,而execution表达式可以用于明确指定方法返回类型,类名,方法名和参数名等与方法相关的部件,并且在Spring中,大部分需要使用AOP的业务场景也 ...

  2. java spring aop 注解包_Spring AOP 注解配置实例

    Spring AOP注解例子 一:导入相关jar包. 首先导入Spring的相关包(这里就不多说了,我这里是3.2.4版本的) 然后导入AOP注解的相关包(不是spring的包)aspectjrt-1 ...

  3. java aop注解拦截_Spring AOP 拦截指定注解标识的类或方法

    代码Demo @Aspect @Component @Order(10) public class BidAuthorityProxy { /** * 扫描指定包下的类中使用@EnableRoleAu ...

  4. java aop xml配置_spring AOP使用 xml配置

    目前有一个业务层的类,我想要监控业务层这个类里面的某些方法.把其中的一些操作记录到一个log表中,但是又不想侵入代码,所以采用切面来完成 一.首先记录log的类: packagecom.aoptest ...

  5. 计算机硬件性能检测报告,硬件性能实际测试结果_平板电脑评测-中关村在线

    ·硬件性能实际测试结果 除了采用7吋的屏幕之外,海信M170采用的Android3.2系统也是该机的一大亮点,除了对系统运行速度的进一步优化之外,相比之前的系统还加入了应用程序分辨率自动适配的功能,这 ...

  6. java手机cpu测试_CPU性能检测下载

    测试你的CPU速度和性能,这个简单而强大的工具. 用于教育目的这个进程也可以比较不同的方法,看看它们之间的关系. 有了这个性能测试工具,你可以通过测试手机CPU: 测试使用的Java代码被称为整数AL ...

  7. java after方法_spring AOP的After增强实现方法实例分析

    本文实例讲述了spring AOP的After增强实现方法.分享给大家供大家参考,具体如下: 一 配置 xmlns:xsi="http://www.w3.org/2001/XMLSchema ...

  8. java切面获取异常日志_spring aop 配置切面,记录系统异常存入log日志

    -----------2016-10-19 14:09:48------------远程请求URL[http://localhost:8080/httpser/api/testAspect.actio ...

  9. spring aop实例讲解_Spring框架核心知识点

    文章内容输出来源:拉勾教育Java高薪训练营 前言: 由于工作需要提升自身技术能力,在各方比较下,报名了拉勾教育的java高薪训练营,目前已经学了半个月啦,来说说自身学习的感受吧: 课程内容有广度更有 ...

最新文章

  1. UIButton 的不同设置和UITextField 的默认值(修改默认值)
  2. 【Python】轻量级分布式任务调度系统-RQ
  3. python生成二维码、动态二维码 和 而二维码解析
  4. 篝火怎么做_上世纪的“Dyatlov事件”到底是怎么一回事儿,真是未知力量吗?...
  5. Cesium Vue开发环境搭建
  6. 利用python进行数据分析_资料 | 利用Python进行数据分析
  7. r语言 python 书_推荐关于R的几本书
  8. 回望2019,展望2020
  9. php获取td数据,记一次用PHP做爬虫获取全市高考报考数据
  10. Linux Python 安装第三方库时报错 package wheel is not installed
  11. HDU 5411 CRB and Puzzle(矩阵快速幂+可达矩阵)
  12. Python-docx库设置word页眉页脚
  13. Android 10.0 关机界面全屏显示(UI全屏显示)
  14. python的十句名言_让人恍然大悟的十句名言(双语版)
  15. Oracle 变量绑定与变量窥视合集系列五
  16. java sencha_Sencha Cmd 5 Java 8错误
  17. 【数据科学】使用Python建立你的数据处理肌肉记忆
  18. 浅析java.lang.IllegalArgumentException: URI is not absolute
  19. 嘿,妈妈,我是设计师
  20. java文件写入不覆盖_java写入文件不覆盖写入_Java写入文件–用Java写入文件的4种方法...

热门文章

  1. linux journalctl 命令 查询systemd init 系统日志
  2. linux touch命令 新建文件 更新文件时间
  3. MFC中CArray类原理及其应用
  4. Android中用Application类实现全局变量
  5. (八)OpenStack---M版---双节点搭建---Cinder安装和配置
  6. 深入浅出:Linux设备驱动之字符设备驱动
  7. 计算机发现概述教案,计算机网络概述教案
  8. Design Pattern - Adapter(C#)
  9. html 点击一行变色,elementui点击table每一行会变色,当有固定列的时候,
  10. android蓝牙python,Android蓝牙连接问题