RocketMQ 5.0 × OpenTelemetry 分布式全链路追踪最佳实践

在分布式系统中,多个服务之间的交互涉及到复杂的网络通信和数据传输,其中每个服务可能由不同的团队或组织负责维护和开发。因此,在这样的环境下,当一个请求被发出并经过多个服务的处理后,如果出现了问题或错误,很难快速定位到问题的根源。全链路追踪技术可以帮助我们解决这个问题,它能够跟踪和记录请求在系统中的传输过程,并提供详细的性能和日志信息,使得开发人员能够快速诊断和定位问题。这种技术对于分布式系统的可靠性、性能和可维护性都非常重要。

RocketMQ 5.0 与分布式全链路追踪

Apache RocketMQ 5.0 版本作为近几年来最大的一次迭代,在整个可观测性上也进行了诸多改进。其中,支持标准化的分布式全链路追踪就是一个重要的特性。

RocketMQ 5.0 可观测
 

分布式链路追踪系统的起源可以追溯到 2007 年 Google 发布的《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》论文。这篇论文详细介绍了 Google 内部使用的链路追踪系统 Dapper,其中使用的 span 概念被广泛采用,并成为后来开源链路追踪系统中的基础概念之一。

Dapper Trace Tree
 

在 Dapper 中,每个请求或事务被追踪时都会创建一个 span,记录整个请求或事务处理过程中的各个组件和操作的时间和状态信息。这些 span 可以嵌套,形成一个树形结构,用于表示整个请求或事务处理过程中各个组件之间的依赖关系和调用关系。后来,很多开源链路追踪系统,如 Zipkin 和 OpenTracing,也采用了类似的 span 概念来描述分布式系统中的链路追踪信息。现在,合并了 OpenTracing 和 OpenCensus 的 CNCF OpenTelemetry 自然也一样采用了 span 概念,并在此基础上进行了进一步发展。

OpenTelemetry 为 messaging 相关的 span 定义了一组语义约定(semantic convention),旨在制定一套与特定消息系统无关的 specification,而 OpenTelmetry 的开发其实也都是由 specification 驱动进行展开。

Specification Driven Development
 

语义约定中规定了随 span 携带的通用属性的统一名称,这包括但不限于:

  • messaging.message.id: 消息的唯一标识符。
  • messaging.destination:消息发送的目的地,通常是一个队列或主题名称。
  • messaging.operation:对消息的操作类型,例如发送、接收、确认等。

特别地,不同的消息系统可能会有自己特定的行为和属性,RocketMQ 也和 Kafka 以及 RabbitMQ 一起,将自己特有的属性推进了社区规范中,这包括但不限于:

  • messaging.rocketmq.client_id: RocketMQ 客户端唯一标识符。
  • messaging.rocketmq.message.type: RocketMQ 消息类型。
  • messaging.rocketmq.message.tag: RocketMQ 消息 tag,作为消息 topic 之外的二级分类。
  • messaging.rocketmq.message.keys: 除了 message id 之外的另外一种标记消息的方式。

此外,这些约定还描述了 messaging span 的拓扑关系,包括代表消息发送、接收和处理的不同 Span 之间的父子和链接关系。关于具体的定义可以参考:《Semantic Conventions of Messaging》

在 OpenTelemetry 中有两种不同的方式可以为应用程序添加可观测信息:

  • Automatic Instrumentation:无需编写任何代码,只需进行简单的配置即可自动生成可观测信息,包括应用程序中使用的类库和框架,这样可以更方便地获取基本的性能和行为数据。
  • Manual Instrumentation:需要编写代码来创建和管理可观测数据,并通过 exporter 导出到指定的目标。这样可以更灵活自由地控制用户想要观测的逻辑和功能。

在 Java 类库中,前者是一种更为常见的使用形式。RocketMQ 5.0 客户端的 trace 也依托于 automatic instrumentation 进行实现。在 Java 程序中,automatic instrumentation 的表现形式为挂载 Java agent。在过去的一年里,我们将基于 RocketMQ 5.0 客户端的 instrumentation 推入了 OpenTelemetry 官方社区。现在,只需要在 Java 程序运行时挂载上 OpenTelemetry agent,即可实现对应用程序透明的分布式全链路追踪。

除此之外,Automatic Instrumentation 和 Manual Instrumentation 并不冲突,Automatic Instrumentation 中所使用的关键对象会被注册成全局对象,在 Manual Instrumentation 的使用方式中也可以非常方便的获取。实现两个 Instrumentation 共用一套配置,非常灵活和方便。

云上最佳实践

阿里云上的两款可观测产品都提供了基于 OpenTelemetry 的分布式全链路追踪服务。在准备接入之前,首先准备好 RocketMQ 5.0 Java 客户端,可以参考 example 进行消息的收发。

引入 RocketMQ 5.0 客户端
 

关于 RocketMQ 5.0 的更多细节,欢迎大家参考和关注 rocketmq-clients 仓库RocketMQ 官网

然后准备好 OpenTelemetry agent jar,可以从 OpenTelemetry 官方下载最新 agent

默认情况下,按照 OpenTelemetry 中关于 messaging 的规范,只有以下两种情况下的 span 会默认启用:

  • 消息发送(send,对应 RocketMQ 中的消息发送)。
  • 消息处理(process,对应 RocketMQ 中 PushConsumer 中的 MessageListener 的处理过程)。

如果想要启用消息接收(receive,对应消费者中的长轮询),需要在启动时通过设置 -Dotel.instrumentation.messaging.experimental.receive-telemetry.enabled=true 来手动启用。需要注意的是,这两种情况下消息 span 之间的拓扑关系是不同的。

Receive Span Suppressed/Enabled
 

为了更好地展示分布式全链路追踪的过程,我提供了一个代码示例:rocketmq-opentelemetry。在这个代码示例中,会启动三个不同的进程,涉及三种不同类库和业务逻辑之间的相互调用,展示了一个在分布式环境较复杂中间件之间进行交互的典型案例。

请求首先会从 gRPC 客户端发往 gRPC 服务端,在 gRPC 服务端收到请求之后,会向 RocketMQ 5.0 的 Producer 往服务端发送一条消息,然后再回复对应的 response 给客户端。在 RocketMQ 5.0 的 PushConsumer 接受到消息之后,会在 MessageListener 中使用 Apache HttpClient 往淘宝网发送一条 GET 请求。

示例代码链路
 

特别地,gRPC 客户端在发起具体的调用是在一个上游业务 span 的生命周期之内进行的,这个 span 我们称之为 ExampleUpstreamSpan,RocketMQ 5.0 PushConsumer 在收到消息之后,也会在 MessageListener 里执行其他的业务操作,也会有对应的 span,我们称之为 ExampleDownstreamSpan。那么默认在 receive span 没有启用的情况下,按照开始时间的顺序,会先后存在 7 个 span。分别是:

  • ExampleUpstreamSpan
  • gRPC 客户端请求 span。
  • gRPC 服务端响应 span。
  • RocketMQ 5.0 Producer 的 send span。
  • RocketMQ 5.0 Producer 的 process span。
  • HTTP 请求 span。
  • ExampleDownstreamSpan

目前,主流的云服务供应商都为 OpenTelemetry 提供了良好的支持。阿里云上的 SLS 和 ARMS 也已经完成了对 OpenTelemetry 完善的接入。

RocketMQ 5.0 对接 SLS trace 服务

首先在阿里云日志服务中创建 trace 服务。然后获取接入点,项目和实例名称等信息。

SLS Trace 服务接入配置
 

在补充好信息之后,稍等一会就可以看到对应的 trace 信息已经被上传到 SLS trace 服务中:

SLS Trace 服务分布式全链路追踪展示
 

trace 服务其实是将相关数据存储到日志中,因此这些数据也可以通过 SLS 的 SQL 语法查询得到。

通过 trace 数据,我们可以很方便知道用户的操作系统环境,Java 版本等一系列基础信息。消息的发送延时,失败与否,消息是否准时投递到了客户端,以及客户端本地消费耗时,消费失败与否等一系列有效信息,可以帮助我们十分有效地进行问题排查。

RocketMQ 5.0 对接应用实时监控服务(ARMS)

首先进入应用实时监控服务 ARMS 控制台,点击接入中心中的 OpenTelemetry,选择 java 应用程序下的自动探测,获取启动参数并修改至自己的 java 应用程序。

ARMS 使用 OpenTelemetry 接入示例
 

配置好参数之后,启动自己的相关应用程序,稍等一会儿,就可以在 ARMS Trace Explorer 里看到对应的数据了。

Trace Explorer
 

还可以查看 span 之间的时序关系。

ARMS Trace explorer 分布式全链路追踪展示
 

具体地,可以点进每个 span 查看详细的 attributes/resources/events 等信息。如下图左侧是 send span 的属性,右侧是 process span 的属性。

attributes/resources/events 等信息
 

除此之外,ARMS 还支持通过使用 OpenTelemetry Collector 转发的形式来收集应用程序的 Trace 数据,具体也可以参考通过 OpenTelemetry 接入 AMRS

趋势与思考

随着现代应用程序架构的不断演进,可观测性的重要性日益凸显。它不仅可以帮助我们快速发现和解决系统中的问题,提高应用程序的可靠性和性能,还是实现 DevOps 的关键部分。在可观测领域,像 DataDog 这样专注于可观测领域的公司已经脱颖而出,成为了业界的明星。

除了传统的可观测技术,近年来涌现了一些新兴技术,如 eBPF(Extended Berkeley Packet Filter)和 Service Mesh 也为可观测领域提供了一些新的思路:

eBPF 可以在内核层面运行,通过动态注入代码来监控系统的行为。它被广泛应用于实时网络和系统性能监控、安全审计和调试等任务。Service Mesh 是一种可以在应用程序和网络层面上提供可观测性的技术,通过在应用程序之间注入代理层实现流量管理、安全和可观测性等功能。代理层可以收集和报告有关流量的各种指标和元数据,从而帮助我们了解系统中各个组件的行为和性能。

在可观测领域,RocketMQ 也在不断探索和摸索更加领先的可观测手段,以帮助开发者和客户更快更省心地发现系统中的隐患。随着技术的不断发展,可观测性将成为应用程序架构不可或缺的一部分,为我们提供更加可靠和高效的服务。