[
6月13日
-
8分钟阅读
[
拯救
使用OpenTelemetry、Spring Cloud Sleuth、Kafka和Jaeger进行分布式跟踪
微服务中的分布式跟踪实施步骤指南
分布式跟踪可以让你深入了解某项服务作为分布式软件系统中的一部分是如何执行的。它跟踪并记录请求从其出发点到目的地以及它们所经过的系统。
在这篇文章中,我们将使用OpenTelemetry、Spring Cloud Sleuth、Kafka和Jaeger在三个Spring Boot微服务中实现分布式跟踪。
首先让我们看一下分布式追踪中的一些基本术语。
跨度。代表系统中的一个工作单元。跨度可以相互嵌套,以模拟工作的分解。例如,一个跨度可以是调用一个REST端点,另一个子跨度可以是该端点调用另一个,以此类推,在不同的服务中。
跟踪。所有共享同一根跨度的跨度的集合,或者更简单地说,所有跨度都是作为原始请求的直接结果而创建。跨度的层次结构(每个跨度都有自己的父跨度和根跨度)可以用来形成有向无环图,显示请求在各个组件中的路径。
开放式遥测
OpenTelemetry,也被称为OTel,是一个厂商中立的开源Observability框架,用于仪表、生成、收集和导出遥测数据,如跟踪、度量和日志。作为云原生计算基金会(CNCF)的一个孵化项目,OTel旨在提供统一的厂商无关的库和API集--主要用于收集数据并将其传输到某个地方。OTel正在成为生成和管理遥测数据的世界标准,并且正在被广泛采用。
Spring Cloud Sleuth
Sleuth是一个由Spring Cloud团队管理和维护的项目,旨在将分布式跟踪功能集成到Spring Boot应用程序中。它被捆绑在一个典型的Spring Starter ,所以只要把它作为一个依赖项加入,自动配置就能处理整个应用的所有集成和仪表。下面是一些开箱即用的Sleuth工具。
- 在Spring MVC控制器(REST端点)收到的请求
- 通过Kafka或MQ等消息传递技术的请求
- 通过
RestTemplate,WebClient等方式发出的请求。
Sleuth添加了一个拦截器,以确保所有的跟踪信息都在请求中传递。每次调用时,都会创建一个新的Span。它在收到响应时被关闭。
Sleuth能够追踪你的请求和信息,这样你就可以将该通信与相应的日志条目联系起来。你还可以将追踪信息导出到外部系统,以可视化延迟。
Jaeger
Jaeger最初是由Uber的团队建立的,然后在2015年开源。它在2017年被接受为云原生孵化项目,并在2019年毕业。作为CNCF的一部分,Jaeger是一个公认的云原生架构的项目。它的源代码主要是用Go编写的。Jaeger的架构包括。
- 仪器库
- 采集器
- 查询服务和网络UI
- 数据库存储
与Jaeger类似,Zipkin也在其架构中提供了相同的组件集。虽然Zipkin是一个较早的项目,但Jaeger有一个更现代和可扩展的设计。在这个例子中,我们选择Jaeger作为后端。
追踪系统设计
让我们来设计三个Spring Boot微服务。
customer-service-bff:使用backend for frontend模式,这个服务位于UI和后端之间。它被UI网络应用调用,而UI网络应用又通过REST API调用后端客户服务。customer-service流程:一个简单的客户CRUD服务。除了在CRUD操作时将数据持久化到数据库外,它还在创建、更新或删除客户记录时向Kafka发布事件。order-serviceKafka:在Kafka主题上监听,消费客户创建/更新/删除的事件。

这三个微服务被设计成。
- 通过REST API(
customer-service-bff和customer-service)进行通信 - 通过Kafka的事件驱动的pub/sub进行通信 (
customer-service和order-service)
这是为了观察OpenTelemetry与Spring Cloud Sleuth的结合,如何处理代码的自动仪表,并生成和传输跟踪数据。上面的虚线捕捉了追踪数据的路径,这些数据由微服务输出,通过OTLP(OpenTelemetry Line Protocol)到达OpenTelemetry Collector,而Collector反过来处理并将追踪数据输出到后端Jaeger,以进行存储和查询。
使用monorepo,我们的项目结构如下。
步骤1:添加POM依赖项
这是使用OTel和Spring Cloud Sleuth实现分布式跟踪的关键。我们的目标是不需要手动检测我们的代码,所以我们依靠这些依赖项来完成它们的设计工作--自动检测我们的代码,此外还有跟踪实现,将遥测数据导出到OTel采集器等。
spring-cloud-dependencies:Spring Cloud的依赖性spring-cloud-sleuth-otel-dependencies:Spring Cloud Sleuth OpenTelemetry依赖项spring-cloud-starter-sleuth:Sleuth通过spring-cloud-sleuth-brave模块中的桥与OpenZipkin Brave追踪器集成。由于我们不使用Zipkin做后端,我们必须从spring-cloud-starter-sleuth依赖关系中排除spring-cloud-sleuth-brave,而加入spring-cloud-sleuth-otel-autoconfigure依赖关系。这就把基于Brave的默认跟踪实现替换为基于OpenTelemetry的实现。opentelemetry-exporter-otlp-trace:这是Spring Cloud Sleuth OTel中的组件,它将追踪发送到OpenTelemetry收集器。
第2步:OpenTelemetry配置
OpenTelemetry采集器端点
对于每个微服务,我们需要在application.yml 中添加以下配置(见下面部分的示例片段)。spring.sleuth.otel.exporter.otlp.endpoint 主要是配置 OTel 收集器端点。它告诉输出器(在我们的例子中是 Sleuth)通过 OTLP 将跟踪数据发送到指定的收集器端点 [http://otel-collector:4317](http://otel-collector/:4317.).注意端点 URL 中的otel-collector 来自 docker-compose 服务的otel-collector 图像。
追踪数据概率采样
spring.sleuth.otel.config.trace-id-ratio-based 属性定义了追踪数据的采样概率。它根据给取样器的分数对追踪数据进行采样。概率采样允许OpenTelemetry追踪用户通过使用随机采样技术来降低跨度收集成本。如果比例小于1.0,一些痕迹将不会被导出。在这个例子中,我们将把抽样配置为1.0,即100%。
有关其他 OTel Spring Cloud Sleuth 属性,请参见常用应用程序属性。
OpenTelemetry 配置文件
我们需要在项目根部有一个 OTel 配置文件otel-config.yaml 。其内容如下。这个配置文件定义了 OTel 接收器、处理器和输出器的行为。正如我们所看到的,我们将接收器定义为监听gRPC和HTTP,处理器使用批处理,出口器为jaeger和日志。
第3步:用docker-compose把所有东西串起来
让我们看看我们需要旋转的docker容器,以便运行这三个微服务,并观察它们的分布式跟踪,前三个微服务在上面的部分有解释。
customer-service-bffcustomer-serviceorder-servicepostgres-customer:数据库,用于customer-servicepostgres-order:数据库用于order-servicejaeger-all-in-one: 运行所有Jaeger后端组件和UI的单一镜像otel-collector:OpenTelemetry追踪的引擎,它接收、处理并向后端输出追踪数据zookeeper追踪:追踪Kafka集群中的节点状态,维护Kafka主题和消息的列表kafka:pub/sub事件流处理平台
运行docker-compose up -d ,调出所有九个容器。

第4步:追踪数据的行动
快乐的路径
现在,让我们启动我们的customer-service-bff ,即流程的入口,创建一个新的客户。
启动Jaeger用户界面。 [http://localhost:16686/](http://localhost:16686/,),通过服务搜索customer-service-bff ,点击Find Traces 按钮,这里是我们看到的创建客户跟踪:它跨越了三个服务,总共跨越了六个,持续时间82.35ms。

除了跟踪时间线视图(上面的截图),Jaeger还提供了一个图形视图(在右上方的下拉菜单中选择Trace Graph )。
三个微服务在docker中的日志输出显示了相同的跟踪ID,用红色突出显示,并根据它们的应用名称有不同的span id(应用名称和它们相应的span id用匹配的颜色突出显示)。在customer-service ,相同的span id从REST API请求传递到Kafka发布器请求。
错误情况
让我们在docker中暂停我们的customer-service PostgreSQL数据库,并重复从customer-service-bff 创建客户的流程。我们得到了500 internal server error ,正如预期的那样。在Jaeger中检查,我们看到下面的跟踪,异常堆栈跟踪抱怨SocketTimeoutException ,也是预期的。
识别长期运行的跨度
Jaeger用户界面允许我们搜索超过指定最大持续时间的跟踪。例如,我们可以搜索所有超过1000ms的痕迹。然后,我们就可以深入研究这些长时间运行的痕迹,以调查其根本原因。
总结
在这个故事中,我们从OpenTelemetry、Spring Cloud Sleuth和Jaeger的角度解读了分布式追踪,验证了REST API调用和Kafka pub/sub中的分布式追踪的自动工具。我希望这个故事能让你更好地了解这些追踪框架和工具,尤其是OpenTelemetry,以及它是如何从根本上改变我们在分布式系统中做观察的。
这个故事的源代码可以在我的GitHub repo中找到。
编码愉快!
参考文献
storiesfromtheherd.com/unpacking-o…