`
donlianli
  • 浏览: 339219 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
Group-logo
Elasticsearch...
浏览量:217965
社区版块
存档分类
最新评论

Spring aop 性能监控器

阅读更多

       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<StackEntry> child;
		/**
		 * 上级节点
		 */
		public StackEntry parent ;
		public StackEntry(String logName, long currentTimeMillis) {
			this.logName = logName;
			this.beginTime = currentTimeMillis;
			this.child = new ArrayList<StackEntry>(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<l;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怎么配置?拦截器怎么配置?下载附件,自己看吧,lib包都在里面。

 

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

 

 

构建高并发应用系统,首先从缓存开始,请访问正确读取与设置缓存方法了解如何正确设置缓存。

 

 
对这类话题感兴趣?欢迎发送邮件至donlianli@126.com
关于我:邯郸人,擅长Java,Javascript,Extjs,oracle sql。
更多我之前的文章,可以访问 我的空间
11
3
分享到:
评论
7 楼 lv12312 2013-07-08  
你这个对程序肯定有影响,买个jprofiler,搞定一切
6 楼 zhukewen_java 2013-07-08  
我表示经常通过目测来查出性能瓶颈
5 楼 MrLee23 2013-07-08  
你这个在加一个内存在方法或对象使用前后有个记录就好了,这样还可以监控内存的使用情况
4 楼 a5728238 2013-07-08  
如果不使用spring那你这个不就费了吗。。。
没感觉这个工具有什么用
3 楼 kentkwan 2013-07-08  
那不是spring的功劳 java中的动态代理就可以实现你说的功能了
2 楼 留下的祝福 2013-07-08  
不错,要是能远程监控就好了
1 楼 iamzhongyong 2013-07-08  
完全可以在BTrace中进行封装。对程序没有任何侵入

相关推荐

    spring aop jar 包

    `aop-jar`这个压缩包可能包含了Spring AOP的实现类、接口、以及其他相关的辅助类,例如动态代理类、切点匹配器等。在项目中,我们通常会将这个jar包引入到类路径下,以便使用Spring AOP的功能。 总的来说,Spring ...

    Spring AOP实现机制

    - **性能监控**:记录方法执行时间,分析系统性能瓶颈。 ### 6. 性能对比 JDK动态代理由于基于接口,对非接口类无法处理,而CGLIB则无此限制,但CGLIB的性能相对较低。在实际应用中,应根据具体需求和性能要求选择...

    spring aop 五个依赖jar

    在Spring AOP中,你可以选择使用AspectJ的编译时或运行时织入,以实现更细粒度的控制和更高的性能。 2. **aspectjweaver.jar**:AspectJ Weaver是AspectJ框架的一部分,主要负责在运行时对类进行字节码操作,实现切...

    spring aop用到jar包.rar

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点,如日志、事务管理、性能监控等。在使用Spring AOP时,通常需要引入特定的jar包来支持其功能。...

    spring aop demo 两种实现方式

    这为日志、事务管理、性能监控等提供了方便。本示例提供了一种通过注解和配置文件两种方式实现Spring AOP的方法。 首先,我们来详细讲解通过注解实现Spring AOP。在Spring中,我们可以使用`@Aspect`注解来定义一个...

    springAOP 4个jar包

    Spring AOP是Spring框架的重要组成部分,它提供了面向切面编程的能力,使得开发者可以在不修改源...在实际应用中,Spring AOP常用于日志记录、事务管理、性能监控等多种场景,极大地提高了代码的可维护性和可复用性。

    spring aop所需jar包

    - 性能监控:在关键方法周围添加计时器,以跟踪性能瓶颈。 - 事务管理:确保数据库操作在正确的情况下提交或回滚,无需在业务代码中显式处理。 6. **配置和使用**: - XML配置:在Spring配置文件中定义切面、...

    Spring AOP框架实现的结构分析

    横切关注点是非功能性的、横切性模块,如安全性管理、事务管理、性能监控等。方面是对一个横切关注点的模块化,将原本散落在各处的、用于实现这个关注点的代码归整到一处。连接点是程序执行过程中的一点,如字段访问...

    Spring AOP需要jar包.rar

    Spring AOP(面向切面编程)是Spring框架的重要组成部分,它提供了一种模块化和声明式的方式来处理系统中的交叉关注点,如日志、事务管理、性能监控等。在Java应用中,AOP通过代理模式实现,使得我们可以将横切关注...

    spring AOP切面编程

    Spring AOP(Aspect Oriented Programming,面向切面编程)是Spring框架的重要组成部分,它扩展了传统的面向对象编程,使得开发者可以方便地实现横切关注点,如日志、事务管理、性能监控等。在Spring中,AOP通过代理...

    springaop

    标题 "springaop" 暗示我们关注的是Spring框架中的AOP(面向切面编程)模块。在Spring框架中,AOP是一种强大的工具,它允许程序员定义“切面”,这些切面可以封装横切关注点,如日志、事务管理、性能监控等,将它们...

    Spring aop代码

    4. 性能监控:计算方法的执行时间,分析系统性能瓶颈。 压缩包中的"aop"文件可能是包含这些示例代码的目录,其中可能有Java源代码文件、配置文件以及测试类。测试代码可能展示了如何声明和使用切面,如何定义切入点...

    Spring AOP 拦截器 Advisor

    Spring AOP 拦截器 Advisor 是 Spring 框架中的一个重要概念,它与切面编程密切相关,用于实现细粒度的控制和增强应用程序的行为。在 Spring AOP 中,Advisor 是一个组合了通知(Advice)和切入点(Pointcut)的对象...

    Spring使用AOP的三个jar包

    AOP是一种编程范式,它允许程序员定义“切面”,这些切面可以封装关注点,如日志、事务管理、性能监控等,与业务逻辑解耦。要使用Spring的AOP功能,我们需要引入特定的库,这正是标题中提到的"Spring使用AOP的三个...

    spring aop支持jdk1.7的jar

    在Java应用程序中,AOP常用于日志记录、事务管理、性能监控等场景,它通过定义切面(Aspect)、通知(Advice)、连接点(Join Point)和切入点(Pointcut)等概念,实现了代码的解耦。 标题提到的"spring aop支持...

    spring aop简单应用示例

    在IT行业中,Spring AOP(面向切面编程)是一种强大的工具,它允许程序员在不修改原有业务代码的情况下,实现如日志记录、性能监控、事务管理等横切关注点的功能。本示例将深入探讨Spring AOP的基础知识,以及如何在...

    springboot spring aop 拦截器注解方式实现脱敏

    虽然在这个特定的例子中,`application.properties`可能没有直接与拦截器相关,但我们可以在这里配置一些全局属性,比如日志级别,这对于调试和监控拦截器的行为非常有用。例如: ```properties logging.level.org....

    mybatis 拦截器 + spring aop切面 + spring事务+ 反射工具类

    Spring AOP是Spring框架的一个重要特性,它实现了面向切面编程,允许开发者定义“切面”,即关注点的模块化,比如日志记录、性能监控、安全控制等。切面通过通知(advises)来增强其他对象的行为。Spring支持多种...

Global site tag (gtag.js) - Google Analytics