Spring-AOP实践 - 统计访问时间

简介:

其中,遇到的问题:

1.少包aspectjweaver

添加依赖后才可以使用@Aspect

2.环绕通知加入多个point

刚开使用&&连接多个point,傻傻的看不到调用,忽然看到要用||才对

3.监听时间工具StopWatch每次只能启动一个,一定要关闭后才能启动下一个。

而我想要测试controller->service->repository各个阶段用时显然很不适应。因为使用同一个stopwatch对象来保存时间,而stopwatch每次只能记录一个时间段作为总时间的一部分,不存在时间嵌套关系(这个以后可以考虑想想别的方案)。controller开始后执行部分验证逻辑或是其他,然后调用service,这时候service启动定时会失败,因为controller的计时器还没关,因此需要先关掉controller的计时器。这样会导致controller的计时部分仅仅是调用service之前的时间,service返回值之后,controller再进行某些处理的时间并没有统计。显然,我需要一个卡表的时间统计设计,即进入controller开始计时,调用service后开始service计时,以此类推,最后获得controller总时间Tc,service总时间Ts,repository总时间Tr.所以时间统计应该如图1:

                

               图一                                                                 图二

这样的话,我应该分别获得Tc,Ts,Tr的时间,然后计算百分比或者别的统计工作。也就是说,log仅仅是log,记录而已,想要得到一个统计结果还需要针对数据二次开发,这就需要将时间存储起来或者进行日志挖掘,然而这统计的工作会越来越复杂,最终我都不知会加多少任务进去。

事实上,我们这个页面耗费时间主要是多个webservice调用产生的io耗时,也就是说其实统计一个层面的时间就差不多了。那么,忽略service返回后controller的其他时间,仅仅计算controller开始到调用service的时间为Tc,service开始到调用repository的时间为Ts,如图2,这样利用StopWatch就很容获取时间统计结果。

4.线程不安全

我居然使用单例的一个成员变量做状态存储,真是。待改进

 

1.搭建项目

采用spring-boot-aop的框架来搭建。最终结构图如下:

1.1在idea中,new project-》maven

1.2初始化pom,添加aop,web,test依赖:

  

1.3创建启动入口:com.test.spring.aop.Application.java

  

1.4发现我们需要一个service:

创建com.test.spring.aop.domain.service.IHelloService

创建com.test.spring.aop.domain.service.impl.HelloService

  

1.5发现我们需要一个属性配置文件:

创建application.yml

  View Code

 

1.6这时候可以测试一下启动看看了

直接启动:运行com.test.spring.aop.Application#main()即可。

命令行启动:mvn spring-boot:run

命令行bug启动:mvn spring-boot:run -Drun.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"

 

1.7创建测试com.test.spring.aop.ApplicationTest

  View Code

 

2.AOP - HelloWorld

2.1创建com.test.spring.aop.monitor.ServiceMonitor

  View Code

2.2测试

测试水平还没开始,所以只能手动测试了,运行com.test.spring.aop.ApplicationTest可以看到在HelloService调用前后插入了日志。

 

3.时间统计

最终,生成类图如下:

最终获得的时间统计如下:

复制代码
2016-07-16 21:25:09.361  INFO 16824 --- [nio-8080-exec-4] com.test.spring.aop.monitor.UserMonitor  : StopWatch 'controller': running time (millis) = 3218
-----------------------------------------
ms     %     Task name
-----------------------------------------
01001  031%  List com.test.spring.aop.domain.service.IUserService.getAll()
02000  062%  Object com.test.spring.aop.domain.repository.IConnector.getSthFromRemote()
00217  007%  List com.test.spring.aop.domain.repository.IUserDao.getAll()
复制代码

 

3.1需要设计一个controller

创建com.test.spring.aop.web.UserController:

  View Code

3.2发现需要一个service

创建com.test.spring.aop.domain.service.IUserService

  View Code

创建com.test.spring.aop.domain.service.impl.UserService

  View Code

3.3发现需要一个repository

创建com.test.spring.aop.domain.repository.IUserDao

  View Code

创建com.test.spring.aop.domain.repository.impl.UserDao

  View Code

3.3.1临时添加一个远程调用

创建com.test.spring.aop.domain.repository.IConnector

  View Code

创建com.test.spring.aop.domain.repository.impl.Connector

  View Code

 

3.4发现需要一个实体类

创建com.test.spring.aop.domain.entiry.User

  View Code

3.5完成

以上就基本把一个web接口搞定了,可以运行测试一下,使用浏览器或者postman访问localhost:8080/user/all就可以获得user列表了。

 

3.6加入user时间统计

创建com.test.spring.aop.monitor.UserMonitor

  View Code

这里有几点问题:

1.StopWatch不能重复创建,如果想统计每次controller访问时间,必须在访问前初始化,访问后废除。

2.这里的时间统计仅仅针对controller入口的统计,也就是说,如果别的调用了service,这个时间是不统计的,因为StopWatch没有初始化。

3.StopWatch的prettyPrint是一个很好的东东

4.如果想要向开始那样,完全统计每层耗时,这个设计需要重新设计。当前有这样的想法。controller切点初始化三个或者多个StopWatch;service、repository等分别建立切点并计时;controller执行完毕之后的切点汇总统计数据,销毁各个StopWatch。这样就做到了每个层都统计了。然而问题来了,如果一个controller调用了多个service,显然需要统计所有service耗时和单个service耗时。

 

Boo! Big Bug!!!

今早起来接着看,忽然想起spring bean管理是单例的,而且必须是单例的才可以使用共同的成员变量,但问题来了,spring的controller是多线程的。也就是说,这个切面进入不是排队的,第一个请求过程中第二个请求也是可以进来的。那么,共享成员变量实在是愚蠢的决定。然而选择创建一个类来管理对应一个请求的计时器也不好的,如果并发量超级大岂不是每个人都会产生一个类?

因此,这个计时器必须是一个过程量,可以在指定区间(即controller执行期间)生存,而后销毁。对应的,显然是每个并发请求会至少产生一个类。也就是说,我需要在controller请求的时候new 一个StopWatch的id,然后在接下来的一系列调用中都使用这个id对应的计时器,最后销毁。如果几百万个并发量,那么就会产生几百万个类实例。懵逼。

 

最终选择

通过一个实例来保存一次请求状态太消耗性能,而如果不通过一个类实例保存状态就无法汇总所有的时间。所以这个方案不合适。那么,剩下日志和传入数据库。当下就先记录日志好了。记录想要记录的节点的时间:

  View Code

 

压力测试

1.建立test case

首先,测试controller:创建com.test.spring.aop.web.UserControllerTest

  View Code

不知道为啥,我的new Thread并没有并发执行,而是按顺序执行的。也就是说,虽然我new 10 个Thread,但还是一个接一个的运行。

 

改进
于是,我想采用线程池:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
    public  void  testUsersConcurrent()  throws   Exception{
 
        ThreadPoolExecutor executor =  new  ThreadPoolExecutor( 5 , 10 , 200 , TimeUnit.MILLISECONDS, new  ArrayBlockingQueue<Runnable>( 5 ));
        //不能超过15
        for  ( int  i =  0 ; i <  15 ; i++) {
            executor.execute(()->{
                try  {
                    mockMvc.perform(MockMvcRequestBuilders.get( "/user/all" )).andExpect(MockMvcResultMatchers.status().isOk());
                catch  (Exception e) {
                    e.printStackTrace();
                }
            });
        }
 
        //等待其他线程执行,方便查看控制台打印结果
        Thread.sleep( 100000 );
    }

  

这下就可以看到controller是多线程并发的了,因为瞬间就打印了10条controller访问日志,正好是线程池的最大容量:

  View Code

 

2.采用Apache 的ab进行并发访问

2.1下载Apache

参考:使用Apache Server 的ab进行web请求压力测试

2.2测试

  View Code

 

可以看到服务端是多线程的:

  View Code

 本文转自Ryan.Miao博客园博客,原文链接:http://www.cnblogs.com/woshimrf/p/5677337.html,如需转载请自行联系原作者

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
缓存 Java API
【云原生】Spring Cloud Gateway的底层原理与实践方法探究
【云原生】Spring Cloud Gateway的底层原理与实践方法探究
|
2月前
|
安全 Java 数据库
后端进阶之路——万字总结Spring Security与数据库集成实践(五)
后端进阶之路——万字总结Spring Security与数据库集成实践(五)
|
3月前
|
Java 数据库连接 Spring
从零开始,探索Spring框架的魅力与实践
从零开始,探索Spring框架的魅力与实践
|
3月前
|
缓存 NoSQL Java
Spring Cache 缓存原理与 Redis 实践
Spring Cache 缓存原理与 Redis 实践
141 0
|
7月前
|
消息中间件 弹性计算 Java
Rocketmq-spring入门与实践
本场景带您体验如何在 Spring 生态中优雅地使用 Apache RocketMQ,感受最受欢迎业务开发框架与最受欢迎消息平台结合的魅力。
401 0
|
3月前
|
前端开发 安全 Java
Spring Boot项目中VO层设计:选择继承或组合的灵活实践
Spring Boot项目中VO层设计:选择继承或组合的灵活实践
92 0
|
6天前
|
监控 Java 数据库连接
Spring高手之路17——动态代理的艺术与实践
本文深入分析了JDK和CGLIB两种动态代理技术在Spring框架中的应用。讨论了动态代理的基础概念,通过实例展示了如何实现和应用这两种方法,并比较了它们的性能差异及适用场景。进一步,探讨了在动态代理中实现熔断限流和日志监控的策略,以及如何利用动态代理优化Spring应用的设计和功能。
14 6
Spring高手之路17——动态代理的艺术与实践
|
2月前
|
XML Java 数据格式
使用Spring AOP添加统计时间的功能
使用Spring AOP添加统计时间的功能
|
3月前
|
存储 安全 Java
Spring Security中Token存储与会话管理:解析与实践
Spring Security中Token存储与会话管理:解析与实践
89 0
|
4月前
|
Java Spring
spring boot aop 实践---记录日志
spring boot aop 实践---记录日志
28 0