链路追踪最佳实践
本页面提供了一些关于链路追踪的通用最佳实践。
Span 和资源属性
链路追踪是由 Span 构建的,Span 表示工作单元,例如对上游服务的调用或来自上游服务的调用。Span 主要由 Span 属性和资源属性构成。Span 还具有层次结构,父 Span 可以有子 Span 或同级 Span。
在下面的截图中,屏幕左侧 (1) 显示了查询结果列表。右侧 (2) 列出了构成所选链路追踪的每个 Span。
Span 属性是为 Span 提供上下文的键/值对。例如,如果 Span 处理通过 HTTP 调用另一个服务,属性可以包括 HTTP URL(可能是 Span 属性键 http.url
)和返回的 HTTP 状态码(作为 Span 属性 http.status_code
)。Span 属性可以包含各种非空类型。
与 Span 属性不同,资源属性是描述 Span 如何被收集的上下文的键/值对。通常,这些属性描述了创建 Span 的进程。这可以是一组关于 Kubernetes 集群的资源属性,在这种情况下,你可能会看到资源属性,例如:k8s.namespace
、k8s.container_name
和 k8s.cluster
。它们还可以包括用于为链路追踪插桩的库信息,或任何其他基础设施信息。
更多信息,请阅读 OpenTelemetry 规范中的属性和资源部分。
Span 和资源属性的命名约定
OpenTelemetry 项目定义了许多属性的语义约定,这有助于你确定哪些属性最重要,应该包含在 Span 中。这些约定为描述不同类型的实体提供了通用词汇,有助于确保你的数据一致且有意义。
命名属性时,使用一致的嵌套命名空间,确保属性键对于观察链路追踪 Span 的任何人来说都很清晰,并且 Span 可以共享通用属性。以上面我们的例子为例,属性的 http
前缀是命名空间,url
和 status_code
是该命名空间中的键。属性也可以嵌套,例如 http.url.protocol
可以是 HTTP
或 HTTPS
,而 http.url.path
可以是 /api/v1/query
。
关于语义命名约定的更多详细信息,请参阅 OpenTelemetry 作者建议 和 OpenTelemetry 语义约定 文档。
一些第三方库提供自动化插桩功能,当包含在源代码中时,会自动生成 Span 和 Span 属性。
更多关于为应用进行分布式追踪插桩的信息,请参阅分布式追踪插桩文档。
Span 应该有多少属性?
你的 Span 中包含多少属性取决于你。没有硬性规定。尽量将属性数量保持在最低限度,因为每个属性都会增加链路追踪系统的开销。在 Grafana Cloud 中,这会导致更高的链路追踪成本。
只包含与 Span 表示的操作相关的属性。例如,如果你正在追踪一个 HTTP 请求,你可能会包含请求方法、URL 和响应状态码等属性。
如果你不确定是否包含某个属性,最好谨慎行事并将其省略。如果稍后需要,可以随时添加更多属性。
通常,请考虑以下准则
- 不要在 Span 中包含指标或日志作为属性。
- 不要使用冗余属性。
- 确定要添加哪些属性时,请考虑应用程序的服务流,以及仅在当前 Span 的上下文中执行。
OpenTelemetry 项目没有指定一个 Span 可以拥有的最大属性数量。但是,每个 Span 的默认属性数量限制是 128 个条目,因此你可能需要调整。属性值和名称的字符长度也有默认限制。
确定在哪里添加 Span
在插桩时,确定你需要在一个链路追踪中观察到的最小工作单元,使其有价值,以确保不过度(或不足)插桩。
为任何具有相对重要持续时间的工作创建一个新的 Span,可以让你通过观察链路追踪立即看到在处理进入你的应用或系统的请求时,大量时间花费在哪里。
例如,为调用另一个服务(无论是否插桩)添加一个 Span,该调用可能需要未知的时间才能完成,因此能够区分这项工作可以显示出服务何时耗时超出预期。
为循环中可能调用许多其他函数的工作添加一个 Span 是一个很好的信号,可以显示该循环需要多长时间(你可以添加一个 Span 属性来计数循环运行了多少次,以确定持续时间是否可接受)。然而,为该循环中的每个方法或函数调用添加一个 Span 可能就没有价值,因为它可能会产生数百或数千个无用 Span。
Span 长度
虽然 Span(以及它们所属的链路追踪)的长度有一些(较高)默认限制,但可以通过这些配置进行调整。包含大量 Span 和/或长时间运行的 Span 的链路追踪可能会影响存储后查询它们所需的时间。Cloud Traces 用户应联系 Grafana 支持修改覆盖项。
对于长时间运行的 Span 和链路追踪,了解这对请求的影响的最佳方法是发送一些测试用例,查看性能表现,并评估链路追踪的大小。
然后,你可以修改 Tempo 的配置,或者确定如何重新设计生成链路追踪的方式。
你可以考虑通过以下几种方式分解 Span
- 分解查询
- 例如,如果一个复杂的 SQL 查询涉及多个操作(例如,使用 JOIN、子查询或 UNION),请考虑为每个重要操作创建单独的 Span。
- 改进长时间运行 Span 的粒度
- 对于长时间运行的操作,你可以为每个预设的执行时间间隔创建一个新的 Span。
注意
这需要在你的应用程序代码中进行基于时间的跟踪,实现起来更复杂。
- 对于长时间运行的操作,你可以为每个预设的执行时间间隔创建一个新的 Span。
- 使用 Span Link
- 如果数据流遇到瓶颈,对该数据的进一步操作可能会在稍后批量处理,此时使用Span Link可以帮助将链路追踪限制在可接受的时间范围内,同时与处理相同数据的其他链路追踪共享上下文。这也可以提高链路追踪的可读性。