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

原文连接:https://blog.csdn.net/goldenfish1919/article/details/78094090

使用:
(1)下载源码,执行maven clean install安装依赖

(2)引用依赖:

	<dependency>
        <groupId>com.github.xjs</groupId>
	    <artifactId>ezprofiler-spring-boot-starter</artifactId>
	    <version>0.0.1-SNAPSHOT</version>
    </dependency> 

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

@EnableProfiler
@Configuration
public class EzProfilerConfigure {
 
}

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

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

打开浏览器访问就ok了!

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

背景:

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

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

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

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

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

  5. 定义结果输出界面

下面基于SpringBoot来实现一下。

(1)定义拦截器

@Service
public class AccessInterceptor implements HandlerInterceptor  {
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		if(handler instanceof HandlerMethod){
			HandlerMethod hm = (HandlerMethod)handler;
			AccessInfo mi = new AccessInfo();
			mi.setHm(hm);
			mi.setStartAt(System.currentTimeMillis());
			mi.setUri(request.getRequestURI());
			AccessHolder.accessStart(mi);//记录方法开始
		}
		return true;
	}
	
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView model)
			throws Exception {
		//可以向view中写入数据
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception)
			throws Exception {
		if(handler instanceof HandlerMethod){
			AccessHolder.accessEnd();//记录方法结束
		}
	}
}

(2)定义异常处理器

@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
    @ExceptionHandler(value=Exception.class)  
    public Result<String> allExceptionHandler(HttpServletRequest request, Exception exception) throws Exception{  
        AccessHolder.accessError();//记录方法异常
        if(exception instanceof BizException){
        	exception.printStackTrace();
        	BizException biz = (BizException)exception;
        	return Result.error(biz.getCodeMsg());
        }else {
        	exception.printStackTrace();
        	return Result.error(CodeMsg.SERVER_ERROR);
        }
    }  
}

(3)定义ThreadLocal

@Service
public class AccessHolder {
	
	private static ThreadLocal<AccessInfo> accessHolder = new ThreadLocal<AccessInfo>();//记录单次访问信息
 
	private static ConcurrentHashMap<Class<?>, ControllerAccessInfo> map = new ConcurrentHashMap<Class<?>, ControllerAccessInfo>();//记录所有的访问信息
 
	private static WorkingService<AccessRequest> workingService = new WorkingService<AccessRequest>();//工作队列,串行化
	
	static {
		workingService.start();
	}
	
	public static void accessStart(AccessInfo mai) {
		accessHolder.set(mai);
	}
	
	public static AccessInfo getAccessInfo() {
		return accessHolder.get();
	}
	
	public static void accessError() {
		AccessInfo ai = getAccessInfo() ;
		if(ai == null) {
			return;
		}
		ai.setOccurError(true);
	}
	
	public static void accessEnd() {
		AccessInfo ai = getAccessInfo();
		if(ai == null) {
			return;
		}
		ai.setEndAt(System.currentTimeMillis());
		accessHolder.set(null);
		workingService.execute(new AccessRequest(ai), new LazyExecutable<AccessRequest>() {
			@Override
			public void lazyExecute(AccessRequest request) {
				addAccessInfo(request.getAi());
			}
		});
	}
	
	private static void addAccessInfo(AccessInfo ai) {
		HandlerMethod hm = ai.getHm();
		Class<?> controllerClazz = hm.getBeanType();
		if(!AccessAble.class.isAssignableFrom(controllerClazz)) {
			return;
		}
		Method method = hm.getMethod();
		long startAt = ai.getStartAt();
		long endAt = ai.getEndAt();
		boolean occurError = ai.isOccurError();
		long useTime = endAt - startAt;
		String uri = ai.getUri();
		ControllerAccessInfo cai = map.get(controllerClazz);
		if(cai == null) {
			cai = new ControllerAccessInfo();
			cai.setControllerClazz(controllerClazz);
			map.put(controllerClazz, cai);
		}
		List<MethodAccessInfo> mais = cai.getMethodInfos();
		if(mais == null) {
			mais = new ArrayList<MethodAccessInfo>();
			cai.setMethodInfos(mais);
		}
		MethodAccessInfo mai = getMethodAccessInfo(mais, method);
		if(mai == null) {
			mai = new MethodAccessInfo();
			mai.setMethod(method.getName());
			mai.setUri(uri);
			mai.setInvokeCount(1);
			if(occurError) {
				mai.setErrorCount(1);
			}else {
				mai.setSuccessCount(1);
			}
			mai.setMinMillSecond(useTime);
			mai.setMaxMillSecond(useTime);
			mai.setAvgMillSecond(useTime);
			mais.add(mai);
		}else {
			if(useTime < mai.getMinMillSecond()) {
				mai.setMinMillSecond(useTime);
			}
			if(useTime > mai.getMaxMillSecond()) {
				mai.setMaxMillSecond(useTime);
			}
			mai.setInvokeCount(mai.getInvokeCount() + 1);
			if(occurError) {
				mai.setErrorCount(mai.getErrorCount()+1);
			}else {
				mai.setSuccessCount(mai.getSuccessCount()+1);
			}
			mai.setAvgMillSecond((mai.getAvgMillSecond()+useTime)/2);
		}
	}
	
	private static MethodAccessInfo getMethodAccessInfo(List<MethodAccessInfo> mais, Method method) {
		for(MethodAccessInfo mai : mais) {
			if(method.getName().equals(mai.getMethod())) {
				return mai;
			}
		}
		return null;
	}
	
	public static Map<String, Object> getAllAccessInfo() {
		Map<String, Object> result = new HashMap<String, Object>();
		for(Map.Entry<Class<?>, ControllerAccessInfo> entry : map.entrySet()) {
			ControllerAccessInfo cai = entry.getValue();
			result.put(cai.getControllerClazz().getSimpleName(), cai.getMethodInfos());
		}
		return result;
	}
}

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

public class AccessEndPoint implements Endpoint<Map<String, Object>> {
 
    public String getId() {
        return "access";
    }
    
    public boolean isEnabled() {
        return true;
    }
    
    public boolean isSensitive() {
        return false;
    }
    
    public Map<String, Object> invoke() {
    	return AccessHolder.getAllAccessInfo();
    }
}
@Configuration
public class EndPointAutoConfig {
    @Bean
    public AccessEndPoint myEndPoint() {
        return new AccessEndPoint();
    }
}

完整的代码见:https://github.com/xjs1919/ezprofiler

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值