You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

非RESTful SpringBoot应用性能测试:如何获取各服务耗时拆解数据?

嘿,这个问题我之前帮不少做消息队列驱动的Spring Boot项目解决过——非REST架构确实没法直接用常规的Web请求链路追踪,但还是有不少简便的方案能拿到各服务的耗时拆解,给你整理几个实用的:

方案1:Spring Boot Micrometer + 自定义AOP埋点

这是最贴合Spring Boot生态的方案,不需要引入太多额外依赖(Micrometer是Spring Boot Actuator的默认依赖),能轻松实现服务方法的耗时统计,还能对接Prometheus+Grafana做可视化监控。

步骤大概是这样:

  • 自定义一个用于标记需要统计耗时的注解,比如@ServiceTimed
  • 写一个AOP切面,环绕被标记的方法,记录方法执行的开始/结束时间,将耗时数据上报到Micrometer的MeterRegistry
  • 通过Actuator的/metrics端点查看数据,或者集成监控工具展示

示例代码:

// 自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceTimed {}

// AOP切面
@Aspect
@Component
public class ServiceTimingAspect {
    private final MeterRegistry meterRegistry;

    public ServiceTimingAspect(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }

    @Around("@annotation(com.yourpackage.ServiceTimed)")
    public Object recordServiceTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        try {
            return joinPoint.proceed();
        } finally {
            long durationMs = System.currentTimeMillis() - startTime;
            String serviceMethod = joinPoint.getSignature().toShortString();
            // 注册计时器并记录耗时
            Timer.builder("service.processing.duration")
                    .tag("method", serviceMethod)
                    .register(meterRegistry)
                    .record(durationMs, TimeUnit.MILLISECONDS);
        }
    }
}

之后只需要在你想统计的服务方法上加上@ServiceTimed注解,启动项目后访问/actuator/metrics/service.processing.duration就能看到各方法的耗时分布(平均值、百分位数等)。

方案2:分布式追踪工具(OpenTelemetry/Jaeger)

如果你的项目以后可能扩展成分布式系统,或者想要更直观的链路可视化,用OpenTelemetry+Jaeger是个不错的选择——它能从消息消费的入口开始,追踪每个服务方法的调用链路,清晰展示每个步骤的耗时。

核心思路是:

  • 在消息消费方法中创建一个根Span(代表整个消息处理链路)
  • 在每个服务方法中创建子Span,标记该步骤的执行
  • 配置OpenTelemetry将追踪数据导出到Jaeger,在Jaeger UI中查看完整链路的耗时拆解

示例代码:

@Service
public class QueueMessageConsumer {
    private final Tracer tracer;
    private final OrderProcessingService orderService;

    public QueueMessageConsumer(Tracer tracer, OrderProcessingService orderService) {
        this.tracer = tracer;
        this.orderService = orderService;
    }

    @JmsListener(destination = "order.queue")
    public void consumeOrderMessage(Message message) throws JMSException {
        String messageId = message.getJMSMessageID();
        // 创建根Span,标记消息消费入口
        Span rootSpan = tracer.spanBuilder("message.consume.order")
                .setAttribute("message.id", messageId)
                .startSpan();
        
        try (Scope scope = tracer.withSpan(rootSpan)) {
            // 调用各个服务步骤
            orderService.validateOrder(message.getBody(String.class));
            orderService.processPayment();
            orderService.notifyUser();
        } finally {
            rootSpan.end();
        }
    }
}

// 服务方法中创建子Span
@Service
public class OrderProcessingService {
    private final Tracer tracer;

    public OrderProcessingService(Tracer tracer) {
        this.tracer = tracer;
    }

    public void validateOrder(String orderJson) {
        Span span = tracer.spanBuilder("order.validate").startSpan();
        try (Scope scope = tracer.withSpan(span)) {
            // 订单校验逻辑
        } finally {
            span.end();
        }
    }
}

配置好OpenTelemetry和Jaeger后,你就能在Jaeger的UI里看到每个消息处理链路的完整时序图,每个服务步骤的耗时一目了然。

方案3:日志埋点(快速排查用)

如果只是临时需要排查某个消息的处理耗时,不想引入额外依赖,直接用日志埋点最方便。可以结合SLF4J的MDC(映射诊断上下文)来关联同一个消息的所有日志,方便后续筛选分析。

示例代码:

@Service
public class PaymentService {
    private static final Logger logger = LoggerFactory.getLogger(PaymentService.class);

    public void processPayment(String messageId) {
        MDC.put("messageId", messageId);
        long startTime = System.currentTimeMillis();
        try {
            // 支付处理逻辑
            logger.info("Payment processing started");
            // ...业务代码
        } finally {
            long durationMs = System.currentTimeMillis() - startTime;
            logger.info("Payment processing completed in {} ms", durationMs);
            MDC.remove("messageId");
        }
    }
}

在消息消费方法中把消息ID传入服务方法,这样所有日志都会带上messageId标记,你可以通过日志工具(比如ELK)筛选出同一个消息的所有日志,快速计算各步骤的耗时。


内容的提问来源于stack exchange,提问作者Snowman ROTMG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 13:27:46