作者简介:艾阳坤,Apache RocketMQ PMC Member/Committer,CNCF OpenTelemetry Member,CNCF Envoy contributor。
在分布式系统中,多个服务之间的交互涉及到复杂的网络通信和数据传输,其中每个服务可能由不同的团队或组织负责维护和开发。因此,在这样的环境下,当一个请求被发出并经过多个服务的处理后,如果出现了问题或错误,很难快速定位到根因。分布式全链路追踪技术则可以帮助我们解决这个问题,它能够跟踪和记录请求在系统中的传输过程,并提供详细的性能和日志信息,使得开发人员能够快速诊断和定位问题。对于分布式系统的可靠性、性能和可维护性起到了非常重要的作用。
RocketMQ 5.0 与分布式全链路追踪
Apache RocketMQ 5.0 版本作为近几年来最大的一次迭代,在整个可观测性上也进行了诸多改进。其中,支持标准化的分布式全链路追踪就是一个重要的特性。
RocketMQ 5.0 可观测
而由 Google、Microsoft、Uber 和 LightStep 联合发起的 CNCF OpenTelemetry 作为 OpenTracing 和 OpenCensus 的官方继任者,已经成为可观测领域的事实标准,RocketMQ 的分布式全链路追踪也围绕 OpenTelemetry 进行展开。
分布式链路追踪系统的起源可以追溯到 2007 年 Google 发布的《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》[1]论文。这篇论文详细介绍了 Google 内部使用的链路追踪系统 Dapper,其中使用的 span 概念被广泛采用,并成为后来开源链路追踪系统中的基础概念之一。
Dapper Trace Tree
在 Dapper 中,每个请求或事务被追踪时都会创建一个 span,记录整个请求或事务处理过程中的各个组件和操作的时间和状态信息。这些 span 可以嵌套,形成一个树形结构,用于表示整个请求或事务处理过程中各个组件之间的依赖关系和调用关系。后来,很多开源链路追踪系统,如 Zipkin 和 OpenTracing,也采用了类似的 span 概念来描述分布式系统中的链路追踪信息。现在,合并了 OpenTracing 和 OpenCensus 的 CNCF OpenTelemetry 自然也一样采用了 span 概念,并在此基础上进行了进一步发展。
OpenTelemetry 为 messaging 相关的 span 定义了一组语义约定(semantic convention)[2],旨在制定一套与特定消息系统无关的 specification,而 OpenTelmetry 自身的开发其实也都是由 specification 驱动进行展开。
Specification Driven Development
Messaging Span 定义
Specifaition 中描述了 messaging span 的拓扑关系,包括代表消息发送、接收和处理的不同 span 之间的父子和链接关系。关于具体的定义可以参考:Semantic Conventions of Messaging[3]。对应到 RocketMQ 中,有三种不同的 span:
Span | Description |
send | 消息的发送过程。span 以一次发送行为开始,成功或者失败/抛异常结束。消息发送的内部重试会被记录成多条 span。 |
receive | 消费者中接收消息的长轮询过程,与长轮询的生命周期保持一致。 |
process | 对应 PushConsumer 里 MessageListener 中对消息的处理过程,span 以进入 MessageListener 为开始,离开 MessageListener 为结束。 |
特别地,默认情况下,receive span 是不启用的。在 receive span 启用和不启用的两种情况下,span 之间的组织关系是不同的:
启用 receive span 前后的 span 关系
在没有启用 receive span 的情况下,process span 会作为 send span 的 child;而当 receive span 启用的情况下,process span 会作为 receive span 的 child,同时 link 到 send span。
Messaging Attributes 定义
语义约定中规定了随 span 携带的通用属性的统一名称,这包括但不限于:
- messaging.message.id: 消息的唯一标识符。
- messaging.destination:消息发送的目的地,通常是一个队列或主题名称。
- messaging.operation:对消息的操作类型,例如发送、接收、确认等。
具体可以查看 Messaging Attributes 的部分[4]。
特别地,不同的消息系统可能会有自己特定的行为和属性,RocketMQ 也和 Kafka 以及 RabbitMQ 一起,将自己特有的属性推进了社区规范中[5],这包括:
Attribute | Type | Description |
messaging.rocketmq.namespace | string | RocketMQ 资源命名空间,暂未启用 |
messaging.rocketmq.client_group | string | RocketMQ producer/consumer 负载均衡组,5.0 只对 consumer 生效 |
messaging.rocketmq.client_id | string | 客户端唯一标识符 |
messaging.rocketmq.message.delivery_timestamp | int | 定时消息定时时间,只对 5.0 生效 |
messaging.rocketmq.message.delay_time_level | int | 定时消息定时级别,只对 4.0 生效 |
messaging.rocketmq.message.group | string | 顺序消息分组,只对 5.0 生效 |
messaging.rocketmq.message.type | string | 消息类型,可能为 normal/fifo/delay/transaction,只对 5.0 生效 |
messaging.rocketmq.message.tag | string | 消息 tag |
messaging.rocketmq.message.keys | string[] | 消息 keys,可以有多个 |
messaging.rocketmq.consumption_model | string | 消息消费模型,可能为 clustering/broadcasting,5.0 broadcasting 被废弃 |
快速开始
在 OpenTelemetry 中有两种不同的方式可以为应用程序添加可观测信息:
- Automatic Instrumentation:无需编写任何代码,只需进行简单的配置即可自动生成可观测信息,包括应用程序中使用的类库和框架,这样可以更方便地获取基本的性能和行为数据。
- Manual Instrumentation:需要编写代码来创建和管理可观测数据,并通过 exporter 导出到指定的目标。这样可以更灵活自由地控制用户想要观测的逻辑和功能。
在 Java 类库中,前者是一种更为常见的使用形式。RocketMQ 5.0 客户端的 trace 也依托于 automatic instrumentation 进行实现。在 Java 程序中,automatic instrumentation 的表现形式为挂载 Java agent。在过去的一年里,我们将基于 RocketMQ 5.0 客户端的 instrumentation[6]推入了 OpenTelemetry 官方社区。现在,只需要在 Java 程序运行时挂载上 OpenTelemetry agent,即可实现对应用程序透明的分布式全链路追踪。
除此之外,Automatic Instrumentation 和 Manual Instrumentation 并不冲突,Automatic Instrumentation 中所使用的关键对象会被注册成全局对象,在 Manual Instrumentation 的使用方式中也可以非常方便的获取。实现两个 Instrumentation 共用一套配置,非常灵活和方便。
首先准备好 RocketMQ 5.0 Java 客户端,可以参考 example[7]进行消息的收发。关于 RocketMQ 5.0 的更多细节,欢迎大家参考和关注 rocketmq-clients 仓库[8]和 RocketMQ 官网[9]。
然后准备好 OpenTelemetry agent jar,可以从 OpenTelemetry 官方下载最新 agent[10],在应用程序启动时增加 -javaagent:yourpath/opentelemetry-javaagent.jar 即可。
可以通过设置 OTEL_EXPORTER_OTLP_ENDPOINT 环境变量来设置 OpenTelemetry collector 的接入点。
默认情况下,按照 OpenTelemetry 中关于 messaging 的规范,只有 send 和 process 的 span 会被启用,receive 的 span 是默认不启用的,如果想要启用 receive span,需要手动设置 -Dotel.instrumentation.messaging.experimental.receive-telemetry.enabled=true。
场景最佳实践
目前,主流的云服务供应商都为 OpenTelemetry 提供了良好的支持,阿里云上的 SLS 和 ARMS 两款可观测产品都提供了基于 OpenTelemetry 的分布式全链路追踪服务。
为了更好地展示分布式全链路追踪的过程,这里提供了一个代码示例:rocketmq-opentelemetry[11] 。在这个代码示例中,会启动三个不同的进程,涉及三种不同类库和业务逻辑之间的相互调用,展示了一个在分布式环境较复杂中间件之间进行交互的典型案例。
请求首先会从 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。
剩余60%,完整内容请点击下方链接查看:
https://developer.aliyun.com/article/1181611?utm_content=g_1000369982
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。