如何记录应用的接口访问信息(调用次数,最长时间、最短时间、平均时间等等)

文章转载于:https://blog.csdn.net/goldenfish1919/article/details/78094090

已经重写了,代码在这:https://github.com/xjs1919/ezprofiler,主要使用以下方式实现:

(1)通过实现BeanPostProcessor,做Controller扫描

(2)通过EnableProfiler这个注解,加载内部的Controller

(3)通过自定义HandlerMapping,添加对内部Controller的访问

使用起来很简单,只需要2步:

(1)添加依赖:

  1. <dependency>
  2. <groupId>com.github.xjs </groupId>
  3. <artifactId>ezprofiler-spring-boot-starter </artifactId>
  4. <version>0.0.1-SNAPSHOT </version>
  5. </dependency>

(2)引入配置类打开ezprofiler:

  1. @EnableProfiler
  2. @Configuration
  3. public class EzProfilerConfigure {
  4. }

(3)自定义一些参数,可以省略

  1. ezprofiler.basepackage=com.test //设置controller所在的包
  2. ezprofiler.enableBasic=true//启用basic认证
  3. ezprofiler.username=xjs
  4. ezprofiler.password=123456
  5. #ezprofiler.url=/my/profiler//接口的url

打开浏览器访问就ok了!

---------------------------以下是第一版-------------------------------

扫描二维码关注公众号,回复: 2202486 查看本文章

背景:

有时候为了对系统进行优化,我们需要找出系统中访问时间长的那些方法,本文就来演示下,如何实现这个功能。最终实现的效果是访问一个url,列出当前系统中所有api接口的访问信息,包括:接口的调用次数、正常调用次数、异常调用次数、接口的平均访问时间、最大访问时间、最小访问时间,如图所示:


思路:

(1)定义一个拦截器,记录方法的开始时间和结束时间

(2)定义一个全局的异常处理器,记录防范访问发生异常

(3)定义ThreadLocal,保存方法的访问信息。

(4)为了访问多线程并发访问影响记录的准确性,用队列把计算串行化。

(5)定义结果输出界面

下面基于SpringBoot来实现一下。

(1)定义拦截器

  1. @Service
  2. public class AccessInterceptor implements HandlerInterceptor {
  3. @Override
  4. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  5. if(handler instanceof HandlerMethod){
  6. HandlerMethod hm = (HandlerMethod)handler;
  7. AccessInfo mi = new AccessInfo();
  8. mi.setHm(hm);
  9. mi.setStartAt(System.currentTimeMillis());
  10. mi.setUri(request.getRequestURI());
  11. AccessHolder.accessStart(mi); //记录方法开始
  12. }
  13. return true;
  14. }
  15. @Override
  16. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView model)
  17. throws Exception {
  18. //可以向view中写入数据
  19. }
  20. @Override
  21. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception)
  22. throws Exception {
  23. if(handler instanceof HandlerMethod){
  24. AccessHolder.accessEnd(); //记录方法结束
  25. }
  26. }
  27. }

(2)定义异常处理器

  1. @ControllerAdvice
  2. @ResponseBody
  3. public class GlobalExceptionHandler {
  4. @ExceptionHandler(value=Exception.class)
  5. public Result<String> allExceptionHandler(HttpServletRequest request, Exception exception) throws Exception{
  6. AccessHolder.accessError(); //记录方法异常
  7. if(exception instanceof BizException){
  8. exception.printStackTrace();
  9. BizException biz = (BizException)exception;
  10. return Result.error(biz.getCodeMsg());
  11. } else {
  12. exception.printStackTrace();
  13. return Result.error(CodeMsg.SERVER_ERROR);
  14. }
  15. }
  16. }

(3)定义ThreadLocal

  1. @Service
  2. public class AccessHolder {
  3. private static ThreadLocal<AccessInfo> accessHolder = new ThreadLocal<AccessInfo>(); //记录单次访问信息
  4. private static ConcurrentHashMap<Class<?>, ControllerAccessInfo> map = new ConcurrentHashMap<Class<?>, ControllerAccessInfo>(); //记录所有的访问信息
  5. private static WorkingService<AccessRequest> workingService = new WorkingService<AccessRequest>(); //工作队列,串行化
  6. static {
  7. workingService.start();
  8. }
  9. public static void accessStart(AccessInfo mai) {
  10. accessHolder.set(mai);
  11. }
  12. public static AccessInfo getAccessInfo() {
  13. return accessHolder.get();
  14. }
  15. public static void accessError() {
  16. AccessInfo ai = getAccessInfo() ;
  17. if(ai == null) {
  18. return;
  19. }
  20. ai.setOccurError( true);
  21. }
  22. public static void accessEnd() {
  23. AccessInfo ai = getAccessInfo();
  24. if(ai == null) {
  25. return;
  26. }
  27. ai.setEndAt(System.currentTimeMillis());
  28. accessHolder.set( null);
  29. workingService.execute( new AccessRequest(ai), new LazyExecutable<AccessRequest>() {
  30. @Override
  31. public void lazyExecute(AccessRequest request) {
  32. addAccessInfo(request.getAi());
  33. }
  34. });
  35. }
  36. private static void addAccessInfo(AccessInfo ai) {
  37. HandlerMethod hm = ai.getHm();
  38. Class<?> controllerClazz = hm.getBeanType();
  39. if(!AccessAble.class.isAssignableFrom(controllerClazz)) {
  40. return;
  41. }
  42. Method method = hm.getMethod();
  43. long startAt = ai.getStartAt();
  44. long endAt = ai.getEndAt();
  45. boolean occurError = ai.isOccurError();
  46. long useTime = endAt - startAt;
  47. String uri = ai.getUri();
  48. ControllerAccessInfo cai = map.get(controllerClazz);
  49. if(cai == null) {
  50. cai = new ControllerAccessInfo();
  51. cai.setControllerClazz(controllerClazz);
  52. map.put(controllerClazz, cai);
  53. }
  54. List<MethodAccessInfo> mais = cai.getMethodInfos();
  55. if(mais == null) {
  56. mais = new ArrayList<MethodAccessInfo>();
  57. cai.setMethodInfos(mais);
  58. }
  59. MethodAccessInfo mai = getMethodAccessInfo(mais, method);
  60. if(mai == null) {
  61. mai = new MethodAccessInfo();
  62. mai.setMethod(method.getName());
  63. mai.setUri(uri);
  64. mai.setInvokeCount( 1);
  65. if(occurError) {
  66. mai.setErrorCount( 1);
  67. } else {
  68. mai.setSuccessCount( 1);
  69. }
  70. mai.setMinMillSecond(useTime);
  71. mai.setMaxMillSecond(useTime);
  72. mai.setAvgMillSecond(useTime);
  73. mais.add(mai);
  74. } else {
  75. if(useTime < mai.getMinMillSecond()) {
  76. mai.setMinMillSecond(useTime);
  77. }
  78. if(useTime > mai.getMaxMillSecond()) {
  79. mai.setMaxMillSecond(useTime);
  80. }
  81. mai.setInvokeCount(mai.getInvokeCount() + 1);
  82. if(occurError) {
  83. mai.setErrorCount(mai.getErrorCount()+ 1);
  84. } else {
  85. mai.setSuccessCount(mai.getSuccessCount()+ 1);
  86. }
  87. mai.setAvgMillSecond((mai.getAvgMillSecond()+useTime)/ 2);
  88. }
  89. }
  90. private static MethodAccessInfo getMethodAccessInfo(List<MethodAccessInfo> mais, Method method) {
  91. for(MethodAccessInfo mai : mais) {
  92. if(method.getName().equals(mai.getMethod())) {
  93. return mai;
  94. }
  95. }
  96. return null;
  97. }
  98. public static Map<String, Object> getAllAccessInfo() {
  99. Map<String, Object> result = new HashMap<String, Object>();
  100. for(Map.Entry<Class<?>, ControllerAccessInfo> entry : map.entrySet()) {
  101. ControllerAccessInfo cai = entry.getValue();
  102. result.put(cai.getControllerClazz().getSimpleName(), cai.getMethodInfos());
  103. }
  104. return result;
  105. }
  106. }

(4)利用SpringBoot的EndPoint把结果输出出去

  1. public class AccessEndPoint implements Endpoint<Map<String, Object>> {
  2. public String getId() {
  3. return "access";
  4. }
  5. public boolean isEnabled() {
  6. return true;
  7. }
  8. public boolean isSensitive() {
  9. return false;
  10. }
  11. public Map<String, Object> invoke() {
  12. return AccessHolder.getAllAccessInfo();
  13. }
  14. }
  1. @Configuration
  2. public class EndPointAutoConfig {
  3. @Bean
  4. public AccessEndPoint myEndPoint() {
  5. return new AccessEndPoint();
  6. }
  7. }
完整的代码见: https://github.com/xjs1919/ezprofiler

猜你喜欢

转载自blog.csdn.net/zty1317313805/article/details/81028838
今日推荐