`
baggioback
  • 浏览: 70593 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

性能监控

 
阅读更多

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

Java代码  收藏代码
  1. 2013-07-07 19:19:50,440 WARN  [main] [aop.PerformanceInterceptor]   
  2. |-144 ms; [HelloService.hellpAop]  
  3. |+---10 ms; [AnotherService.childMehhod]  
  4. |+---21 ms; [AnotherService.childMehhod3]  
  5. |+---+---8 ms; [HelloService.parentMehtod]  
  6. |+---12 ms; [AnotherService.childMehhod2]  

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

step1.构造拦截器。

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

 

Java代码  收藏代码
  1. public Object invoke(MethodInvocation invocation) throws Throwable {  
  2.        try {    
  3.         String name = extractLogName(invocation);  
  4.         //记录开始时间  
  5.            start(name);    
  6.            return invocation.proceed();    
  7.        } finally {   
  8.         //记录方法结束时间  
  9.            stop();    
  10.        }    
  11. }  

 

 

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

 

step2.构造数据结构

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

 

Java代码  收藏代码
  1. /** 
  2.      * 保存监控信息变量 
  3.      * @author donlianli@126.com 
  4.      */  
  5.     private static class StackData {  
  6.         /** 
  7.          * 记录根根节点 
  8.          */  
  9.         public StackEntry root;  
  10.         /** 
  11.          * 当前正在调用方法节点 
  12.          */  
  13.         public StackEntry currentEntry;  
  14.         /** 
  15.          * 堆栈树高度 
  16.          */  
  17.         public int level;  
  18.     }  
  19.       
  20.     /** 
  21.      * aop方法性能统计实体 
  22.      * @author donlianli@126.com 
  23.      */  
  24.     private static class StackEntry {  
  25.         public String logName ;  
  26.         public long beginTime;  
  27.         public long endTime;  
  28.         /** 
  29.          * 节点所处高度 
  30.          */  
  31.         public int level;  
  32.         /** 
  33.          * 调用的子方法 
  34.          */  
  35.         public List<StackEntry> child;  
  36.         /** 
  37.          * 上级节点 
  38.          */  
  39.         public StackEntry parent ;  
  40.         public StackEntry(String logName, long currentTimeMillis) {  
  41.             this.logName = logName;  
  42.             this.beginTime = currentTimeMillis;  
  43.             this.child = new ArrayList<StackEntry>(3);  
  44.         }  
  45.     }  

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

 

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

 

Java代码  收藏代码
  1. public static void start(String logName) {    
  2.         StackData data = dataHolder.get();    
  3.         StackEntry currentEntry = new StackEntry(logName, System.currentTimeMillis());    
  4.         if (data == null) {    
  5.             data = new StackData();    
  6.             data.root = currentEntry;    
  7.             data.level = 1;  
  8.             dataHolder.set(data);    
  9.         } else {    
  10.             StackEntry parent = data.currentEntry;    
  11.             currentEntry.parent=parent;    
  12.             parent.child.add(currentEntry);    
  13.         }    
  14.         data.currentEntry = currentEntry;    
  15.         currentEntry.level=data.level;  
  16.         data.level++;    
  17.     }  
  18.     public static void stop() {  
  19.         StackData data = dataHolder.get();    
  20.         StackEntry self = data.currentEntry;  
  21.         self.endTime = System.currentTimeMillis();  
  22.         data.currentEntry = self.parent;  
  23.         data.level--;  
  24.         printStack(data);  
  25.     }  
  26.        /** 
  27.      * 此处还可以进行改进,可以将超时的数据放入一个有界队列 
  28.      * 里,在另一个线程进行打印。 
  29.      * @param data 
  30.      */  
  31.     private static void printStack(StackData data) {  
  32.         if(logger.isWarnEnabled()){  
  33.             StringBuilder sb = new StringBuilder("\r\n");  
  34.             StackEntry root = data.root;  
  35.             appendNode(root,sb);  
  36.             logger.warn(sb.toString());  
  37.         }  
  38.     }  
  39.   
  40.     private static void appendNode(StackEntry entry, StringBuilder sb) {  
  41.         long totalTime = entry.endTime-entry.beginTime ;  
  42.         if(entry.level ==1){  
  43.             sb.append("|-");  
  44.         }  
  45.         sb.append(totalTime);  
  46.         sb.append(" ms; [");  
  47.         sb.append(entry.logName);  
  48.         sb.append("]");  
  49.               
  50.         for(StackEntry cnode : entry.child){  
  51.             sb.append("\r\n|");  
  52.             for(int i=0,l=entry.level;i<l;i++){  
  53.                 sb.append("+---");  
  54.             }  
  55.             appendNode(cnode,sb);  
  56.         }  
  57.           
  58.     }    

 

等等,还有需求?

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

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

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

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

Java代码  收藏代码
  1.       /** 
  2.  * 性能监控开关 
  3.  * 可以在运行时动态设置开关 
  4.  */  
  5. private volatile boolean switchOn = true;  
  6. /** 
  7.  * 方法执行阈值 
  8.  */  
  9. private volatile int threshold = 100;  
  10. public Object invoke(MethodInvocation invocation) throws Throwable {  
  11.     if(switchOn){  
  12.         String name = extractLogName(invocation);  
  13.         try {    
  14.                 start(name);    
  15.             return invocation.proceed();    
  16.         } finally {    
  17.                 stop(threshold);    
  18.         }    
  19.     }  
  20.     else {  
  21.          return invocation.proceed();    
  22.     }  
  23. }  

 打印日志阈值:

Java代码  收藏代码
  1.       /** 
  2.  * @param threshold 打印日志的阈值 
  3.  */  
  4. public static void stop(int threshold) {  
  5.     StackData data = dataHolder.get();    
  6.     StackEntry self = data.currentEntry;  
  7.     self.endTime = System.currentTimeMillis();  
  8.     data.currentEntry = self.parent;  
  9.     data.level--;  
  10.     if(data.root == self && (self.endTime -self.beginTime) > threshold){  
  11.         printStack(data);  
  12.     }  
  13. }  

 

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

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

 spring怎么配置?拦截器怎么配置?下载附件,自己看吧,lib包都在里面。

分享到:
评论

相关推荐

    oracle性能监控sql 监控当前会话 执行的sql及io等信息

    Oracle性能监控SQL——监控当前会话执行的SQL及IO等信息 Oracle性能监控是数据库管理员的重要职责之一,通过监控数据库的性能,可以及时发现问题,避免数据库的宕机和性能下降。本文将介绍一些常用的Oracle性能监控...

    微服务架构的应用性能监控.pdf

    微服务架构的应用性能监控 微服务架构的应用性能监控是指在微服务架构下的应用系统中监控和优化性能的过程。微服务架构是指将一个大型应用程序拆分成多个小型、独立的服务,以提高系统的灵活性、可靠性和可扩展性。...

    VMware ESXi性能监控方法-ESXTOP Technical Deep Dive

    本文将深入探讨ESXi性能监控的高级技巧,特别是在VMworld 2018上发布的ESXTOP技术深度解析。 首先,ESXTOP是一个命令行工具,它提供实时的性能数据,可以用来监控虚拟机和ESXi主机本身的性能。使用ESXTOP,系统管理...

    记一次记一次Linux性能监控

    【Linux性能监控详解】 在运维和开发过程中,对Linux系统的性能监控是确保服务稳定性和效率的关键环节。本文将深入探讨Linux性能监控的重点,包括CPU、内存、IO和网络子系统的监控,以及如何针对不同类型的應用进行...

    SQLServer性能监控指标说明

    SQL Server 性能监控指标说明 SQL Server 的性能监控指标是数据库管理员和开发者对数据库性能进行监控和优化的重要依据。以下是 SQL Server 性能监控指标的说明: 配置硬件 在 SQL Server 中,配置硬件是性能监控...

    网络实验之设备性能监控

    在计算机网络领域,设备性能监控是一项至关重要的任务,它能够帮助我们实时了解网络中的设备运行状态,及时发现并解决问题,确保网络服务的稳定性和高效性。本实验“网络实验之设备性能监控”聚焦于通过代码实现对...

    java虚拟机性能监控

    ### Java虚拟机性能监控 #### 一、引言 在当今快速发展的信息技术领域,Java作为最流行的编程语言之一,其虚拟机(Java Virtual Machine, JVM)的性能优化和监控成为了确保应用程序高效稳定运行的关键因素。Java...

    Windows服务器性能监控

    ### Windows服务器性能监控 #### 一、概述 随着信息技术的发展,服务器已经成为企业信息化的重要组成部分。为了确保业务的正常运行,对服务器性能进行监控显得尤为重要。本文将详细介绍如何对Windows服务器进行...

    MySQL数据库性能监控与诊断

    ### MySQL数据库性能监控与诊断详解 在IT领域,特别是对于依赖于数据库的现代应用程序而言,数据库性能监控与诊断是确保系统稳定性和响应速度的关键环节。本文将深入探讨MySQL数据库性能监控与诊断的相关知识点,...

    BPC应用性能监控平台实施方案.docx

    BPC 应用性能监控平台实施方案 BPC 应用性能监控平台实施方案是为了提高业务性能和监控能力而设计的解决方案。该方案旨在通过实施 BPC 应用性能监控平台来提高企业的业务效率和竞争力。 第 1 章 前言 在现代企业...

    经典 Linux系统性能监控 中文版

    《经典 Linux系统性能监控 中文版》是一本深入浅出的Linux性能监控指南,它针对的是那些希望理解和优化Linux系统性能的技术人员。该书虽然基于较为传统的概念和技术,但其内容扎实,阐述清晰,覆盖了从基础到进阶的...

    linux性能监控脚本

    在Linux系统管理中,性能监控是一项至关重要的任务,它能够帮助我们及时发现并解决系统可能出现的问题,优化系统资源的利用。这些脚本是经典工具,适用于监控Linux系统的不同性能指标,如流量、磁盘大小、CPU负载和...

    涨见识!JVM性能监控与调优实战 一线大厂大牛讲师的JVM优化案例与解决方案课程

    JVM性能监控与调优实战课程,作为整篇课程的重中之重,非常值得同学们参考学习。课程前端讲解了JVM的性能监控和调优的概述,对调优的的方法和工具进行讲解学习,让同学们掌握方法,理解知识。课程的中间阶段我们进行...

    cat性能监控demo资源

    在IT行业中,性能监控是确保系统稳定性和效率的关键环节,特别是在大规模应用如美团点评这样的平台上。本资源"cat性能监控demo资源"旨在提供一个关于如何使用Cat(Coding Analysis and Testing,编码分析与测试)...

    C#写的计算机性能监控程序

    在IT领域,性能监控是确保系统稳定运行的关键环节。在这个案例中,我们关注的是一个用C#编程语言编写的计算机性能监控程序。该程序能够帮助用户实时了解CPU、内存、网络以及各个进程的使用状况,这对于管理和优化...

    前端性能监控系统

    在IT领域,前端性能监控系统是提升用户体验和优化网站或应用程序不可或缺的一部分。它允许开发者实时追踪和分析用户在浏览器端的交互,以便发现并解决可能导致性能下降的问题。在这个"前端性能监控系统"中,我们可以...

Global site tag (gtag.js) - Google Analytics