如何在 Spring Boot 中分析复杂的 http 请求处理



我有复杂的@RestController方法,像这样:

@PostMapping("{id}")
@PreAuthorize("hasRole('ADMIN')")
@Transactional
public Response handleRequest(@PathVariable("id") long id, @RequestBody @Valid Request request) {
    return service.handleRequest(id, request);
}
我们的请求处理

非常慢,因此我们想检查在特定请求处理任务上花费了多少时间。不幸的是,很多事情都是在我的方法之外完成的,例如:

  • 反序列化请求
  • 验证
  • 权限检查
  • 开始和结束事务
  • 序列化响应

有没有办法简单地测量所有这些部件?也许是一组接收跟踪消息的记录器,以便我可以在每个步骤结束时提取时间戳?

我现在看到的唯一方法是更改该方法以接受HttpServletRequest和HttpServletResponse,并在方法主体中执行这些部分。但这样我就会失去很多Spring Boot的好处。

您还可以

检查 tuto 是否为执行器添加自定义指标,但这似乎有点复杂(但您必须编写自己的指标 bean 并将其注入到代码中,覆盖 objectMapper 进行映射等...... )

或者可以激活Jackson,Spring-Security,javax.validation上的日志记录信息,以检查每个操作的日志中的时间,但不是很精确

你真正需要的是Java线程分析器,它会告诉你到底出了什么问题,为此你可以使用任何APM工具,我最喜欢的是GLOWROOT.我已经在类似的场景中使用它来测量API的性能并识别慢速跟踪,这将清楚地告诉你哪个方法需要时间,你可以看到从方法调用到所有方法的整个跟踪在内部调用,甚至可以识别慢查询(如果有(。希望这有帮助

网站: https://glowroot.org/

示例跟踪:

https://demo.glowroot.org/transaction/thread-profile?transaction-type=Web&transaction-name=%2Fhot-sauces

没有必要更改方法来期望 HttpServletRequest。您可以使用AspectJ

使用它,您可以收集在每种方法上花费的时间并分析其中的数据。

创建方法计时注释

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodTiming {
}

在您的请求中,创建一个映射,该映射将保留所有方法及其所花费的时间:

  public class Request {
  private Map<String, Long> methodTimings = new TreeMap<String, Long>();
  public void addMethodTiming(String classAndMethodName, long executionTimeMillis) {
        Long value = methodTimings.get(classAndMethodName);
        if (value != null) {
            executionTimeMillis += value;
        }
        methodTimings.put(classAndMethodName, executionTimeMillis);
    }
  }

比,创建将处理它的 Aspect 类:

@Aspect
@Component
public class MethodTimingAspect {
private static final String DOT = ".";
@Around("@annotation(MethodTiming)")
public Object timeAround(ProceedingJoinPoint joinPoint) throws Throwable  {
    Object result = null;
    StopWatch watch = new StopWatch();
    try {
        watch.start();
        result = joinPoint.proceed();
    } finally {
        watch.stop();
        long executionTime = watch.getLastTaskTimeMillis();
        String className = joinPoint.getTarget().getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        String classAndMethodName = className + DOT + methodName;
        Object[] methodArgs = joinPoint.getArgs();
        if (methodArgs != null) {
            for (Object arg : methodArgs) {
                if (arg instanceof Request) {
                    // inject time back into Request
                    Request request = (Request) arg;
                    request.addMethodTiming(classAndMethodName, executionTime);
                    break;
                }
            }
        }
    }
    return result;
}

最后,只需在您希望测量的方法上添加@MethodTiming:

@MethodTiming
public Request handleRequest(Request request) {
// handle the Request
return request
}

您的请求对象将在处理后具有类似

"methodTimings": {
    "RequestService.handleRequest": 2610,
    "AnotherRequestService.anotherMethod": 1351
}

最新更新