领先一步
VMware 提供培训和认证,助您快速提升。
了解更多我受到 OpenZipkin 贡献者以及 Spring Cloud Sleuth 和 Zipkin 贡献者 Adrian Cole 的 精彩演讲介绍 Zipkin 的启发,撰写了这篇博文。演讲中包含许多精彩内容,所以阅读完本文后,请务必观看!
技术的进步和云计算的兴起使得轻松构建和部署服务变得更加容易。云计算使我们能够自动化解决构建新服务带来的痛苦(从几天或几周(喘气!)到几分钟!)。速度的提升反过来又使我们能够更加敏捷,并考虑更小的、可独立部署的服务批次。新服务的激增使得推理系统范围和请求特定的性能特征变得复杂。
当应用程序的所有功能都位于一个单体中 - 我们将此类应用程序称为一个大型、不间断的可部署文件,例如 .war
或 .ear
- 推理出现问题的位置就容易得多。是否存在内存泄漏?它就在单体中。某个组件是否无法正确处理请求?它也在单体中。消息是否丢失?同样,可能也在单体中。分布式改变了一切。
系统在负载和规模下表现不同。系统行为的规范通常会偏离系统的实际行为,而实际行为本身也可能在不同的上下文中发生变化。在请求遍历系统时对其进行上下文化非常重要。能够讨论特定请求的性质并能够理解该特定请求相对于过去一分钟、一小时、一天(或任何!)其他有用间隔中类似请求的一般行为也很重要,这提供了具有统计意义的样本。上下文帮助我们确定请求是否异常以及是否值得关注。在建立“正常”的基线之前,无法追踪系统中的错误。什么算长?对于某些系统,可能是微秒,对于其他系统,可能是秒或分钟!
在这篇文章中,我们将了解支持分布式追踪的 Spring Cloud Sleuth 如何帮助我们建立这种上下文,并帮助我们更好地理解系统的实际行为,而不仅仅是其指定的行为。
从理论上讲,追踪很简单。当请求在一个系统中从一个组件流向另一个组件时,穿过入口和出口点,**追踪器**会在可能的地方添加逻辑以延续在发出第一个请求时生成的唯一的**追踪 ID**。当请求在其旅程中到达某个组件时,会为该组件分配一个新的**跨度 ID**并将其添加到追踪中。追踪表示请求的整个旅程,跨度表示沿途的每个单独的跳跃,每个请求。跨度可能包含**标签**或元数据,这些元数据可用于稍后对请求进行上下文化。跨度通常包含常见的标签,例如开始时间戳和结束时间戳,尽管将语义相关的标签(例如业务实体 ID)与跨度关联起来很容易。
Spring Cloud Sleuth(org.springframework.cloud
:spring-cloud-starter-sleuth
)一旦添加到 CLASSPATH 中,就会自动检测常见的通信通道
RestTemplate
发出的请求等。Spring Cloud Sleuth 为您设置了有用的日志格式,用于打印追踪 ID 和跨度 ID。假设您在 spring.application.name
为 my-service-id
的微服务中运行启用了 Spring Cloud Sleuth 的代码,您将在微服务的日志中看到如下内容
2016-02-11 17:12:45.404 INFO [my-service-id,73b62c0f90d11e06,73b62c0f90d11e06,false] 85184 --- [nio-8080-exec-1] com.example.MySimpleComponentMakingARequest : ...
在该示例中,my-service-id
是 spring.application.name
,73b62c0f90d11e06
是追踪 ID,73b62c0f90d11e06
是跨度 ID。此信息非常有用。您可以将日志发布到 Elasticsearch 和 Splunk 等日志分析和处理工具。有多种方法可以将数据传输到那里。例如,Logstash 是一种日志发布器,它会写入 ElasticSearch。Cloud Foundry 会自动将服务所有实例的日志聚合到统一日志中 通过名为 Loggregator 的工具,然后可以将其转发到任何与 Syslog 兼容的服务,包括 Splunk 或 PaperTrail 等工具。无论您采用哪种方法,如果所有日志和追踪信息都存储在一个可供查询和分析的位置,您都可以进行有趣的查询。
Spring Cloud Sleuth 还通过简单地注入 SpanAccessor
使此信息可用于任何了解 Spring Cloud Sleuth 的 Spring 应用程序。您还可以使用它来检测 Spring Cloud 未检测到的自己的组件,以便它们可以延续追踪信息。当然,每个追踪器都会有所不同,但 Spring Cloud Sleuth 本身的代码(例如:TraceFeignClientAutoConfiguration
)暗示了典型追踪器的运作方式
...
@Autowired
private SpanAccessor spanAccessor;
...
Span span = this.spanAccessor.getCurrentSpan();
...
template.header(Span.TRACE_ID_NAME, Span.toHex(span.getTraceId()));
setHeader(template, Span.SPAN_NAME_NAME, span.getName() );
setHeader(template, Span.SPAN_ID_NAME, Span.toHex(span.getSpanId()));
...
哪些请求应该被追踪?理想情况下,您需要足够的数据来查看反映实时操作流量的趋势。但是,您不希望压垮日志记录和分析基础设施。某些组织可能只保留每千个请求、每十个请求或每百万个请求的请求!默认情况下,阈值为 10% 或 .1,但您可以通过指定采样百分比来覆盖它
spring.sleuth.sampler.percentage = 0.2
或者,您可以注册自己的 Sampler
bean 定义并以编程方式决定哪些请求应该被采样。您可以对要追踪哪些内容做出更明智的选择,例如,忽略成功的请求,或者检查某个组件是否处于错误状态,或者任何其他内容。作为参数提供的 Span
表示较大追踪中当前进行中的请求的跨度。如果您愿意,可以进行有趣且特定于请求类型的采样。例如,您可以决定只采样 HTTP 状态代码为 500 的请求。例如,以下 Sampler
将追踪大约一半的请求
@Bean
Sampler customSampler() {
return new Sampler() {
@Override
public boolean isSampled(Span span) {
return Math.random() > .5 ;
}
};
}
确保为您的应用程序和基础设施设定切合实际的期望。您的应用程序的用法模式很可能需要更灵敏或更不灵敏的东西来检测趋势和模式。这旨在作为操作数据;大多数组织不会将此数据存储超过几天,或者在上限情况下,最多一周。
数据收集是一个开始,但目标是理解数据,而不仅仅是收集它。为了理解全局概况,我们需要超越单个事件。
为此,我们将使用 OpenZipkin 项目。OpenZipkin 是 Zipkin 的完全开源版本,Zipkin 是一个起源于 Twitter 的 2010 年的项目,并且基于 Google Dapper 论文。
以前,Zipkin 的开源版本与 Twitter 内部使用的版本以不同的速度发展。OpenZipkin 代表了这些努力的同步:OpenZipkin就是 Zipkin,当我们在本文中提到 Zipkin 时,指的是 OpenZipkin 中反映的版本。
Zipkin 提供了一个 REST API,客户端可以直接与之交互。Zipkin 甚至支持此 REST API 的基于 Spring Boot 的实现。使用它就像直接使用 Zipkin 的 @EnableZipkinServer
一样简单。Zipkin 服务器通过 SpanStore
将写入委派给持久层。目前,开箱即用地支持使用 MySQL 或内存中的 SpanStore
。作为 REST 的替代方案,我们还可以通过 Spring Cloud Stream 绑定程序(如 RabbitMQ 或 Apache Kafka)将消息发布到 Zipkin 服务器。我们将使用此选项和 org.springframework.cloud
:spring-cloud-sleuth-zipkin-stream
的 @EnableZipkinStreamServer
,将传入的基于 Spring Cloud Stream 的 Sleuth Span
适配为 Zipkin 的 Span
,然后使用 SpanStore
持久化它们。您可以使用任何您喜欢的 Spring Cloud Stream 绑定,但在本例中,我们将使用 Spring Cloud Stream RabbitMQ(org.springframework.cloud
:spring-cloud-starter-stream-rabbitmq
)。
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.sleuth.zipkin.stream.EnableZipkinStreamServer;
@EnableZipkinStreamServer
@SpringBootApplication
public class ZipkinQueryServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ZipkinQueryServiceApplication.class, args);
}
}
在 application.properties
中指定端口,以将 Zipkin 服务器固定到众所周知的端口,以便 UI 稍后可以与其通信
server.port=9411
我的示例代码使用了基于 Spring Cloud Netflix 的 Eureka 服务注册表(位于 eureka-servie
中)进行服务注册和发现,所以接下来先启动它。
我们的微服务(message-client
和 message-service
)是典型的 Spring Cloud 微服务。我仅仅添加了 org.springframework.cloud
:spring-cloud-sleuth-stream
和相应的 Spring Cloud Stream 绑定器,以便将它们的 Sleuth 跟踪信息以带外方式发布到 Zipkin 进行分析。
Zipkin Web UI 使分析和查询 Zipkin 数据变得容易。您可以运行我的示例中的 Zipkin Web 构建脚本,或者直接从 Zipkin 项目的构建中获取最新版本,然后运行它。
java -jar lib/zipkin-web-all.jar -zipkin.web.port=:9412 -zipkin.web.rootUrl=/ -zipkin.web.query.dest=localhost:9411
当 message-service
接收请求时,它会通过 Spring Cloud Stream 绑定器将回复消息发送回 message-client
,然后客户端接收并使用 Spring Integration 消息端点进行日志记录。这是一个人为编造的调用序列,用于演示 Spring Cloud Sleuth 的部分功能。
启动 UI,然后查找所有最近的跟踪信息。您可以按最新、最长等排序,以便更细粒度地控制查看的结果。
当我点击其中一个返回的跟踪信息时,会看到如下所示的 UI。
每个单独的 Span 也包含与其关联的特定请求的相关信息(**标签**)。您可以点击单个 Span 查看此详细信息。
对于基于 Spring 的工作负载,分布式跟踪再简单不过了!但是,跟踪的本质是所有服务的跨领域关注点,无论它们使用哪种技术栈实现。 OpenTracing 计划 旨在为多种语言和平台标准化现代跟踪的词汇和概念。OpenTracing API 拥有多家非常大型组织的支持,其中包括 Google Dapper 论文的原始作者之一。该计划定义了语言绑定;JavaScript、Python、Go 等语言已经有相应的实现。我们将保持 Spring Cloud Sleuth 在概念上与该计划兼容,并对其进行跟踪。预计(但并非暗示)绑定通常会将 Zipkin 作为其后端。
这篇博文旨在概述分布式跟踪中的概念和支持技术。我们了解了 Spring Cloud Sleuth 以及它如何与 Zipkin 协同工作。Zipkin 本身拥有一个有趣的支持生态系统。如果您真的想了解像 Spring Cloud Sleuth、Zipkin、Apache 的 HTrace 等分布式跟踪工具的模型来源,请查看原始的 Google Dapper 论文。您应该查看Adrian Cole 对 Zipkin 更大生态系统的介绍。当然,这篇博文的代码也已上线。最后,您可以使用Spring Initializr,并将 Spring Cloud Sleuth Stream 和 Zipkin Stream Server 添加到您的 Maven 构建中,以开始使用。