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的实现。
构建高并发应用系统,首先从缓存开始,请访问正确读取与设置缓存方法了解如何正确设置缓存。
相关推荐
常用拦截 拦截器HandlerInterceptor 拦截器MethodInterceptor 添加依赖 创建启动类 创建拦截器类 创建控制器 监控control请求耗时,提高性能
该项目包含helloworld(快速入门)、web(ssh项目快速搭建)、aop(切面编程)、data-redis(redis缓存)、quartz(集群任务实现)、shiro(权限管理)、oauth2(四种认证模式)、shign(接口参数防篡改重放)、encoder(用户...
Spring是一个开源的Java应用框架,它为企业级Java应用... - AOP允许将横切关注点(cross-cutting concerns)从业务逻辑中分离出来,例如日志记录、性能监控、事务管理等。 - Spring框架通过AOP支持将切面逻辑与业务逻
性能监控:监控整个系统的性能,SQL监控,SQL防火墙,URL监控,SPRING监控,SESSION监控等 10. 接口测试:POST or GET 方式检测系统接口,参数加密,json返回结果,计算服务器响应时间 11. 发送邮件:单发,群发...
它采用MVC(Model-View-Controller,模型-视图-控制器)的架构模式,将应用程序分为模型层、视图层和控制器层,提供了处理请求、渲染视图和管理流程的功能。 3. MyBatis框架:MyBatis是一个持久层框架,用于与数据库...
- Spring Boot 基础知识,包括SpringBoot起步、配置详解、aop、filter、拦截器、监听、启动器、全局异常处理、外部Tomcat启动、HTTPS、监控 等。 - springboot-data - Spring Boot 数据库操作,包括SpringJDBC、...
- chapter9-1-3:[Spring Cloud构建微服务架构(三)断路器](http://blog.didispace.com/springcloud3/) - chapter9-1-4:[Spring Cloud构建微服务架构(四)分布式配置中心]...
Spring Boot中使用过滤器和拦截器 Spring Boot整合MyBatis通用Mapper和PageHelper 深入学习Spring Boot自动装配 深入学习Spring Boot中的SpringApplication Spring Boot配合Hibernate Validator参数校验 自定义...
4.容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能 5.容器提供了众多的辅助类,能加快应用的开发 6.spring对于主流的应用框架提供了集成支持,如hibernate,JPA,Struts等 7.spring属于低侵入...
性能监控:监控整个系统的性能,SQL监控,SQL防火墙,URL监控,SPRING监控,SESSION监控等 10. 接口测试:POST or GET 方式检测系统接口,参数加密,json返回结果,计算服务器响应时间 11. 发送邮件:单发,群发...
开机Spring Boot整合MyBatis数据源Druid监控PageHelper分页Hibernate验证器集成杰克逊日期时间格式化swagger2 api管理自定义过滤器,避免XSS攻击和SQL注入攻击自定义AOP配合redis解决重复提交问题自定义拦截器,拦截...
Spring Boot MicroService模板技术栈代码规范配置管理测试... 单元测试 组件测试 api测试 测试覆盖率 合同测试 整合测试 性能测试 测试覆盖率应用层 DDD策略 DIP(弹簧容器) AOP(SpringAOP) ORM(mybatis) Asnyc
Spring-AOP Spring-Aspects Messaging Spring-Messaging WEB Spring-Web Spring-Webmvc Spring-WebSocket Spring-Webmvc-Portlet 数据访问/集成(Data...
涵盖使用Spring Boot 进行Java EE 开发的绝大数应用场景,包含:Web 开发、数据访问、安全控制、批处理、异步消息、系统集成、开发与部署、应用监控、分布式系统开发等。 第一部分 点睛Spring 4.x 第1 章 Spring ...
Spring(IOC DI AOP 声明式事务处理) SpringMVC(支持Restful风格) Hibernate Validator(参数校验) Mybatis(最少配置方案) shiro权限控制,结合ajax实现了异步认证与异步授权,同时实现了细粒度的权限动态...
spring-dubbo-service微服务一,服务模块1,服务器配置:servlet,侦听器,拦截器,过滤器,aop,定时任务mybatis配置集成,多数据源;级联查询一对一对一对多注解配置及xml配置方式;分页处理:传递Page参数或继承...
涵盖使用Spring Boot 进行Java EE 开发的绝大数应用场景,包含:Web 开发、数据访问、安全控制、批处理、异步消息、系统集成、开发与部署、应用监控、分布式系统开发等。 第一部分 点睛Spring 4.x 第1 章 Spring ...