← 返回文章列表

backend项目架构设计 - 执行时间分析

分类:计算机/架构/backend项目架构设计

做一个复杂的数据计算需求,执行过程被拆分为几十个Service(服务)和Calculator(计算器)类,且在其中又调用了十几个基础数据的Service类。计算逻辑开发好后,结构清楚,但执行时间很长,需要做性能优化。那么,应该优化哪些地方,即哪些地方是性能瓶颈?这需要对执行时间做统计和分析。

我们利用Spring的AOP功能实现对方法做执行时间计时。

例如,对于Service,做如下切面类:

@Aspect
@Component
@Slf4j
public class ServiceTimingAspect {
    /**
     * 切点
     * <p>切点为{@code com.company.project}包下任意层级的{@code service}包中的所有类的所有方法。</p>
     */
    @Pointcut("execution(* com.company.project..service..*(..))")
    private void pointcut() {}

    /**
     * 环绕通知,执行并计时
     * @param joinPoint 连接点
     * @return 切点的返回值
     * @throws Throwable 切点的抛出对象
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 执行并计时
        var startTime = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        var endTime = System.currentTimeMillis();
        var duration = endTime - startTime;

        // 方法签名
        var methodSignature = joinPoint.toString();
        methodSignature = methodSignature.substring("execution(".length(), methodSignature.length() - 1);
        logger.debug("方法签名{}耗时: {}ms", methodSignature, duration);

        // 实参列表
        var args = Arrays.toString(joinPoint.getArgs());
        args = args.substring(1, args.length() - 1);
        args = args.replace("\n", "\\n").replace("\t", "\\t");
        // 方法调用
        var methodCall = methodSignature.substring(0, methodSignature.indexOf("(") + 1) + args  + ")";
        logger.debug("方法调用{}耗时: {}ms", methodCall, duration);

        return result;
    }
}

通过配置logback-spring.xml将切面类的日志同时输出到控制台和专门的计时日志文件:

<!-- 输出到计时日志文件的Appender -->
<appender name="TIMING_LOG_FILE" class="ch.qos.logback.core.FileAppender">
    <file>log/timing.log</file>
    <encoder>
        <pattern>${FILE_LOG_PATTERN}</pattern>
    </encoder>
</appender>

<!-- 切面类的Logger -->
<logger name="切面类所在的包名" level="DEBUG" additivity="false">
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="TIMING_LOG_FILE"/>
</logger>

编写TimingLogAnalyzer类,通过main()方法执行,读取计时日志文件,统计每个方法签名、方法调用的执行次数、总耗时、最小耗时、最大耗时、平均耗时,然后将统计结果输出到Excel文件中。

接下来,就可以在Excel文件中通过排序、统计图等手段,分析出性能瓶颈的所在,进而针对性地做性能优化。

功能增强: