原文连接: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接口的访问信息,包括:接口的调用次数、正常调用次数、异常调用次数、接口的平均访问时间、最大访问时间、最小访问时间,如图所示:
思路:
-
定义一个拦截器,记录方法的开始时间和结束时间
-
定义一个全局的异常处理器,记录防范访问发生异常
-
定义ThreadLocal,保存方法的访问信息。
-
为了访问多线程并发访问影响记录的准确性,用队列把计算串行化。
-
定义结果输出界面
下面基于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