APM 最佳实践:从业者指南中的注意事项与禁忌

0 阅读14分钟

作者:来自 Elastic Observability Team

应用性能管理(Application performance management - APM)是定期跟踪、衡量和分析软件应用程序性能和可用性的做法。APM 可以帮助你在复杂的 microservices 环境中获得可见性,而这种复杂性可能会让 site reliability engineering ( SRE )团队应接不暇。由此产生的洞察力可以带来最佳的用户体验,并实现预期的业务成果。这个过程虽然复杂,但目标很明确:确保应用程序顺利运行,满足用户和业务的期望。

清晰地了解应用程序的运行情况以及主动进行 APM 实践,对于维护高性能的软件应用程序至关重要。APM 不应该是事后的想法,它应该从一开始就被考虑进去。当主动实施时,它可以通过将监控组件直接嵌入应用程序中,整合到软件运行的方式里。

什么是应用性能管理?

应用性能管理(Application performance management)包括对应用程序后端和前端性能的持续监控、分析和管理。应用监控正在不断扩展和演进,但 APM 策略不应孤立制定。将多个利益相关者、业务专家、应用程序开发人员和运维团队纳入进来至关重要。成功的 APM 策略不仅仅关注 uptime 或服务器健康状况,而是在问题影响到用户之前就关注应用程序的服务水平目标(service level objectives - SLOs )。

现代 APM 实施方式包括对你的应用程序进行检测,以收集三种类型的 telemetry 数据:traces(请求流程)、metrics(聚合指标)和 logs(离散事件)。挑战不仅仅是收集数据,而是要在不影响性能的前提下收集正确的数据。

了解更多关于 observability metrics 的内容

检测有多种方式,但最有效的策略是将自动检测(针对框架和库)与手动检测(针对业务逻辑)相结合。使用 OpenTelemetry agent 的自动检测可以以最少的代码更改,覆盖你 80% 的 observability 需求:

`

1.  # Auto-instrumentation handles this automatically
2.  @app.route('/api/orders')
3.  def create_order():
4.      # Add manual span only for critical business logic
5.      with tracer.start_as_current_span("order.validation") as span:
6.          span.set_attribute("order.value", order_total)
7.          if not validate_order(order_data):
8.              span.set_status(Status(StatusCode.ERROR))
9.              return 400

`AI写代码
  • 建议:从自动检测开始,然后为关键业务操作添加手动 span。
  • 禁忌:不要对每个函数调用都进行手动检测 —— 这会带来性能开销和噪音。
  • 陷阱:过度检测可能会增加 15%–20% 的延迟。通过基准性能对比来监控你的监控。

组织或企业在制定 APM 策略时需要考虑的一些组件包括:

  • 性能监控,包括评估延迟、服务水平目标( SLOs )、响应时间、吞吐量和请求量

  • 错误跟踪,包括异常、崩溃和失败的 API 调用

  • 基础设施监控,包括支持应用程序的服务器、容器和 cloud 环境的健康状况与资源使用情况

  • 用户体验指标,包括加载时间、会话性能、点击路径以及浏览器或设备信息(需要记住的是,即使系统指标看起来正常,用户仍可能遇到性能问题)

现在观看

高效 APM 的关键原则

高效应用性能管理的核心原则包括端到端可见性(从用户浏览器到数据库)、实时监控与洞察,以及以用户和业务目标为中心的上下文洞察。APM 可以通过持续优化和性能提升来增强应用的可扩展性。

  • 建议:实现基于 SLO 的实时仪表盘与告警,而不是依赖随意设定的阈值。
  • 禁忌:不要仅依赖周期性性能评估或 CPU / memory 告警 —— 应该监控用户体验指标。
  • 陷阱:来自底层系统指标的告警疲劳。应聚焦能反映真实问题的用户相关 SLO。

制定 APM 策略时,可参考以下关键原则:

  1. 主动监控:在问题影响用户之前就进行预防,通过设置告警并快速响应异常。但要避免告警疲劳,将自动化告警与人工监督结合,聚焦结果而非单纯系统指标。

  2. 实时洞察不仅仅记录问题,而是基于实时数据和仪表盘快速做出决策,优先关注最关键的业务事务。利用 telemetry 数据(logs、metrics 和 traces)来解析性能洞察。

  3. 端到端可见性:监控整个环境中的应用程序、完整用户流程及从前端到后端的所有层级。

  4. 以用户为中心的方法:从最终用户的角度优先考虑性能和体验,同时兼顾关键业务目标。

  5. 真实用户监控:工作不应在交付到用户手中时就停止。通过监控用户体验,依据他们的反馈进行迭代和优化。

  6. 持续改进:利用洞察信息持续优化,定期发现和解决未报告的问题。问题应动态应对,而不是等周期性评估时才处理。

  7. 上下文传递:确保 trace 上下文贯穿整个请求路径,尤其是跨服务边界时。

    `
    
    1.  # Outgoing request - inject context
    2.  headers = {}
    3.  propagate.inject(headers)
    4.  response = requests.post('http://service-b/process', headers=headers)
    
    `AI写代码
    
  8. 抽样策略:使用智能抽样在可见性与性能之间取得平衡:

    • 对高流量服务使用 1%–10% 的基于头部的抽样( head-based sampling )

    • 对错误和慢请求使用 100% 的基于尾部的抽样( tail-based sampling )

    • 监控检测开销 —— 目标是性能影响低于 5%

APM 实施最佳实践

合适的 APM 解决方案应以最小的检测成本支持你的技术栈。OpenTelemetry 已成为行业标准,提供可跨语言使用的、与供应商无关的检测方案:

`

1.  @RestController
2.  public class OrderController {

4.      @PostMapping("/orders")
5.      public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
6.          // Auto-instrumentation captures this endpoint automatically
7.          // Add custom business context
8.          Span.current().setAttributes(Attributes.of(
9.              stringKey("order.value"), String.valueOf(request.getTotal()),
10.              stringKey("user.tier"), request.getUserTier()
11.          ));

13.          return ResponseEntity.ok(processOrder(request));
14.      }
15.  }

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)
  • 建议:在生产环境中实施抽样策略,并监控检测开销。
  • 禁忌:不要对高流量服务使用 100% 抽样 —— 这会影响性能并导致存储成本激增。
  • 陷阱:基于头部的抽样可能会漏掉关键的错误 trace。使用基于尾部的抽样可以在减少数据量的同时捕获所有错误。

正确做法如下:

  • 选择合适的 APM 解决方案:合适的 APM 工具应与应用架构和组织需求相匹配。该解决方案应为组织提供监控、跟踪、衡量和分析软件应用所需的工具和能力。企业可以使用 OpenTelemetry 这个开源的 observability 框架,对应用进行检测并收集 telemetry 数据(tracesmetricslogs)。
  • 管理基数以控制成本:高基数属性会使指标难以使用且成本高昂:
`

1.  # Good - bounded cardinality
2.  span.set_attribute("user.tier", user.subscription_tier)  # 3-5 values
3.  span.set_attribute("http.status_code", response.status_code)  # ~10 values

5.  # Bad - unbounded cardinality  
6.  span.set_attribute("user.id", user.id)  # Millions of values
7.  span.set_attribute("request.timestamp", now())  # Infinite values

`AI写代码

基于 SLOs 设置智能告警,而不是使用随意的阈值。利用错误预算来决定何时通知相关人员:

`

1.  slos:
2.    - name: checkout_availability
3.      target: 99.9%
4.      window: 7d
5.    - name: checkout_latency  
6.      target: 95%  # 95% of requests under 500ms
7.      window: 7d

`AI写代码
  • 培训团队并促进协作。APM 策略影响广泛的利益相关者,不仅仅是开发人员。务必让 IT 团队和其他业务相关人员参与跨部门协作。通过将 APM 融入组织架构,共同合作。确保制定清晰的目标和 KPI,与业务需求和用户体验保持一致。
  • 审查和评估。APM 策略会随着应用和业务需求的变化不断发展和调整。

APM 中的监控策略

成功的应用性能管理策略的关键之一是考虑如何以及何时使用不同的监控方法。结合多种监控策略至关重要,因为应用的不同组件,如用户体验或基础设施,需要针对性的方法来有效检测和解决问题。多样化的策略可确保全面覆盖、更快分析、更稳定的应用性能以及更满意的最终用户。

需要考虑的各种监控方法包括:

  • 实时监控:以亚秒级粒度持续跟踪系统实时性能。除了技术指标外,还要针对业务逻辑实现自定义指标:
`

1.  order_processing_duration = Histogram(
2.      "order_processing_seconds",
3.      "Time to process orders", 
4.      ["payment_method", "order_size"]
5.  )

7.  with order_processing_duration.labels(
8.      payment_method=payment.method,
9.      order_size=get_size_bucket(order.total)
10.  ).time():
11.      process_order(order)

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)
  • 合成监控:模拟用户交互,提前发现问题,防止真实用户受影响。对外部依赖尤为关键:
`

1.  // Synthetic check for critical user flow
2.  const syntheticCheck = async () => {
3.      const span = tracer.startSpan('synthetic.checkout_flow');
4.      try {
5.          await loginUser();
6.          await addItemToCart();
7.          await completePurchase();
8.          span.setStatus({code: SpanStatusCode.OK});
9.      } catch (error) {
10.          span.recordException(error);
11.          span.setStatus({code: SpanStatusCode.ERROR});
12.          throw error;
13.      } finally {
14.          span.end();
15.      }
16.  };

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)
  • 深入诊断与性能分析:帮助排查复杂的性能瓶颈,包括第三方插件或工具。通过应用性能分析,你可以更深入地挖掘数据,分析其功能表现。
  • 分布式追踪:微服务架构的关键。需要在异步边界仔细处理上下文传递:
`

1.  # Event-driven systems - propagate context through messages
2.  def publish_order_event(order_data):
3.      headers = {}
4.      propagate.inject(headers)

6.      message = {
7.          'data': order_data,
8.          'trace_headers': headers  # Preserve trace context
9.      }
10.      kafka_producer.send('order-events', message)

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

APM 数据分析与洞察

监控和收集数据只是开始。企业需要懂得如何解读应用性能管理数据,以便进行调优和决策。

识别趋势和模式有助于团队主动发现问题。利用关联分析将用户投诉与后端性能关联起来。下面是使用 ES|QL ( Elastic 查询语言 )的示例:

`

1.  FROM traces-apm*
2.  | WHERE user.id == "user_12345" 
3.    AND @timestamp >= "2024-06-06T09:00:00" 
4.    AND @timestamp <= "2024-06-06T10:00:00"
5.  | EVAL duration_ms = transaction.duration.us / 1000
6.  | KEEP trace.id, duration_ms, transaction.name, service.name, transaction.result
7.  | WHERE duration_ms > 2000
8.  | SORT duration_ms DESC
9.  | LIMIT 10

`AI写代码

检测瓶颈:APM 揭示了常见的性能反模式,如下面代码中的 n+1 问题。使用 APM 来优化代码:

`

1.  # N+1 query problem detected by APM
2.  def get_user_orders_slow(user_id):
3.      user = User.query.get(user_id)
4.      orders = []
5.      for order_id in user.order_ids:  # Each iteration = 1 DB query
6.          orders.append(Order.query.get(order_id))
7.      return orders

9.  # Optimized after APM analysis
10.  def get_user_orders_fast(user_id):
11.      return Order.query.filter(Order.user_id == user_id).all()  # Single query

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

关联指标并将用户投诉与后端性能数据(包括历史数据)联系起来,可以揭示系统不同部分的交互方式。这有助于团队准确诊断根本原因,了解性能问题的全面影响。

自动化根因分析,利用基于 AI / machine learning 的工具如 AIOps,可以通过精准定位问题源头,加速诊断和解决,减少停机时间,释放资源。

重要的是使用数据的整体视图来指导未来决策。数据越多,利用价值越大。

  • 建议:使用分布式追踪来识别导致性能下降的具体服务和操作。
  • 禁忌:不要认为相关性就意味着因果关系 —— 要用代码级性能分析数据进行验证。
  • 陷阱:遗留系统在追踪中常常表现为黑盒。应使用日志关联和合成 span 来保持可见性。

高级实施模式

复杂的生产环境带来独特挑战,需要采用高级实施策略。本节介绍处理多语言架构、遗留系统集成和复杂关联分析的实用方法。

多语言环境中的上下文传递:在不同语言和框架之间维护 trace 上下文,需要特别关注传递机制:

`

1.  // Java - Auto-propagation with Spring Cloud
2.  @PostMapping("/orders")
3.  public ResponseEntity<Order> createOrder(@RequestBody OrderRequest request) {
4.      Span.current().setAttributes(Attributes.of(
5.          stringKey("order.type"), request.getOrderType(),
6.          longKey("order.value"), request.getTotalValue()));

8.      // OpenFeign automatically propagates context to downstream services
9.      return paymentClient.processPayment(request.getPaymentData());}

`AI写代码
`

1.  // Go - Manual context extraction and propagation
2.  func processHandler(w http.ResponseWriter, r *http.Request) {
3.      ctx := otel.GetTextMapPropagator().Extract(r.Context(), 
4.                                                propagation.HeaderCarrier(r.Header))
5.      ctx, span := tracer.Start(ctx, "process_payment")
6.      defer span.End()
7.      // Continue with trace context maintained}

`AI写代码

遗留系统集成:为无法直接进行检测的系统创建 observability 桥接:

`

1.  # Synthetic spans with correlation IDs for mainframe calls
2.  with tracer.start_as_current_span("mainframe.account_lookup") as span:
3.      correlation_id = format(span.get_span_context().trace_id, '032x')

5.      logger.info("CICS call started", extra={
6.          "correlation_id": correlation_id,
7.          "trace_id": span.get_span_context().trace_id
8.      })

10.      result = call_mainframe_service(account_data, correlation_id)
11.      span.set_attribute("account.status", result.status)

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

使用 ES|QL 进行高级追踪分析:利用 Elastic 查询语言将用户投诉与后端性能关联起来:

`

1.  -- Find slow requests during complaint timeframe
2.  FROM traces-apm*
3.  | WHERE user.id == "user_12345" AND @timestamp >= "2024-06-06T09:00:00"
4.  | EVAL duration_ms = transaction.duration.us / 1000
5.  | WHERE duration_ms > 2000
6.  | STATS avg_duration = AVG(duration_ms) BY service.name, transaction.name
7.  | SORT avg_duration DESC

9.  -- Correlate errors across service boundaries
10.  FROM traces-apm*
11.  | WHERE trace.id == "44b3c2c06e15d444a770b87daab45c0a"
12.  | EVAL is_error = CASE(transaction.result == "error", 1, 0)
13.  | STATS error_rate = SUM(is_error) / COUNT(*) * 100 BY service.name
14.  | WHERE error_rate > 0

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)

事件驱动架构模式:通过消息头显式传递上下文,以支持异步处理:

`

1.  # Producer - inject context into message
2.  headers = {}
3.  propagate.inject(headers)
4.  message = {
5.      'data': order_data,
6.      'trace_headers': headers  # Preserve trace context
7.  }
8.  await kafka_producer.send('order-events', message)

10.  # Consumer - extract and continue trace
11.  trace_headers = message.get('trace_headers', {})
12.  context = propagate.extract(trace_headers)
13.  with tracer.start_as_current_span("order.process", context=context):
14.      await process_order(message['data'])

`AI写代码![](https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png)
  • 建议:使用 ES|QL进行传统仪表盘无法处理的复杂追踪分析。
  • 禁忌:不要直接对遗留系统进行检测 —— 应使用关联 ID 和合成 span。
  • 陷阱:消息队列和异步处理会中断追踪上下文,除非通过消息头显式传递。
  • 关键洞察:完美的检测并不总是可能。通过战略性使用关联 ID、合成 span 和智能查询,即使在复杂混合环境中也能实现全面的可观测性。

使用 Elastic Observability 进行性能优化的 APM

Elastic Observability 通过提供统一的可观测性,将应用性能数据与日志、指标和追踪整合在一个强大的平台上,使实施应用性能管理策略变得无缝。使用 Elastic 的 OpenTelemetry 发行版(EDOT)收集数据,可以快速轻松地开始采集 APM 数据。

开发人员可以设置异常告警,利用分布式追踪优化特定服务或事务,通过负载均衡和缓存减少延迟,使用 Elastic 提升性能稳定性

通过代码分析,团队可以识别性能热点、低效代码路径、内存泄漏或资源密集型操作,这些都会拖慢应用速度。企业可以创建自定义仪表盘来跟踪 KPI,最终支持更好的业务成果。

探索 Elastic Observability Labs 获取更多技术可观测性内容

附加 APM 资源:

Elastic 连续第三年被 Gartner® 评为 2023 年 APM 和可观测性魔力象限中的 Visionary

本文所述功能的发布与时间完全由 Elastic 自行决定。任何当前不可用的功能可能不会按时或根本不会发布。

原文:APM best practices: Dos and don’ts guide for practitioners | Elastic Blog