OpenTelemetrySpringCloudSleuth
11月23日 碧落盟投稿 前言
分布式跟踪可让您深入了解特定服务在分布式软件系统中作为整体的一部分是如何执行的。它跟踪和记录从起点到目的地的请求以及它们经过的系统。
在本文中,我们将使用OpenTelemetry、SpringCloudSleuth、Kafka和Jaeger在三个SpringBoot微服务中实现分布式跟踪。
我们先来看看分布式追踪中的一些基本术语。
跨度:表示系统内的单个工作单元。跨度可以相互嵌套以模拟工作的分解。例如,一个跨度可能正在调用一个REST端点,然后另一个子跨度可能是该端点调用另一个,等等在不同的服务中。
Trace:所有共享相同根跨度的跨度集合,或者更简单地说,将所有跨度创建为原始请求的直接结果。跨度的层次结构(每个跨度在根跨度旁边都有自己的父跨度)可用于形成有向无环图,显示请求在通过各种组件时的路径。
OpenTelemetry
OpenTelemetry,也简称为OTel,是一个供应商中立的开源Observability框架,用于检测、生成、收集和导出遥测数据,例如跟踪、指标和日志。作为云原生计算基金会(CNCF)的孵化项目,OTel旨在提供与供应商无关的统一库和API集主要用于收集数据并将其传输到某处。OTel正在成为生成和管理遥测数据的世界标准,并被广泛采用。
SpringCloudSleuth
Sleuth是一个由SpringCloud团队管理和维护的项目,旨在将分布式跟踪功能集成到SpringBoot应用程序中。它作为一个典型SpringStarter的。以下是一些开箱即用的Sleuth工具:
在SpringMVC控制器(REST端点)收到的请求通过Kafka或MQ等消息传递技术的请求用RestTemplate,WebClient等发出的请求
Sleuth添加了一个拦截器,以确保在请求中传递所有跟踪信息。每次调用时,都会创建一个新的Span。它在收到响应后关闭。
Sleuth能够跟踪您的请求和消息,以便您可以将该通信与相应的日志条目相关联。您还可以将跟踪信息导出到外部系统以可视化延迟。
Jaeger
Jaeger最初由Uber的团队构建,然后于2015年开源。它于2017年被接受为云原生孵化项目,并于2019年毕业。作为CNCF的一部分,Jaeger是云原生架构中公认的项目。它的源代码主要是用Go编写的。Jaeger的架构包括:
InstrumentationLibrariesCollectors查询服务和网页界面数据库存储
与Jaeger类似,Zipkin在其架构中也提供了相同的组件集。尽管Zipkin是一个较老的项目,但Jaeger具有更现代和可扩展的设计。对于此示例,我们选择Jaeger作为后端。
追踪系统设计
让我们设计三个SpringBoot微服务:
customerservicebff:使用backendforfrontend模式,此服务位于UI和后端之间。它由UIWeb应用程序调用,而后者又通过RESTAPI调用调用后端客户服务。customerservice:一个简单的客户CRUD服务。除了在CRUD操作时将数据持久化到其数据库之外,它还在创建、更新或删除客户记录时将事件发布到Kafka。orderservice:监听Kafka主题,使用客户创建更新删除的事件。
这三个微服务旨在:
通过RESTAPI(customerservicebff和customerservice)进行通信通过Kafka(customerservice和orderservice)通过事件驱动的pubsub进行通信
这是为了观察OpenTelemetry如何结合SpringCloudSleuth处理代码的自动检测以及生成和传输跟踪数据。上面的虚线捕获了微服务导出的跟踪数据的路径,通过OTLP(OpenTelemetryProtocol)传输到OpenTelemetryCollector,收集器依次处理并将跟踪数据导出到后端Jaeger进行存储和查询。
使用monorepo,我们的项目结构如下:
第1步:添加POM依赖项
这是使用OTel和SpringCloudSleuth实现分布式跟踪的关键。我们的目标是不必手动检测我们的代码,因此我们依靠这些依赖项来完成它们设计的工作自动检测我们的代码,除了跟踪实现、将遥测数据导出到OTel收集器等。
dependencyManagementdependenciesdependencygroupIdorg。springframework。cloudgroupIdspringclouddependenciesartifactIdversion{springcloud。version}versiontypepomtypescopebimportbscopedependencydependencygroupIdorg。springframework。cloudgroupIdspringcloudsleuthoteldependenciesartifactIdversion{springcloudsleuthotel。version}versionscopebimportbscopetypepomtypedependencydependenciesdependencyManagementdependenciesdependencygroupIdorg。springframework。cloudgroupIdspringcloudstartersleuthartifactIdexclusionsexclusiongroupIdorg。springframework。cloudgroupIdspringcloudsleuthbraveartifactIdexclusionexclusionsdependencydependencygroupIdorg。springframework。cloudgroupIdspringcloudsleuthotelautoconfigureartifactIddependencydependencygroupIdio。opentelemetrygroupIdopentelemetryexporterotlptraceartifactIddependencydependencies
springclouddependencies:SpringCloud依赖springcloudsleuthoteldependencies:SpringCloudSleuthOpenTelemetry依赖项springcloudstartersleuth:Sleuth通过模块中可用的桥与OpenZipkinBrave跟踪器集成springcloudsleuthbrave。由于我们将在示例中使用OpenTelemetry跟踪器,因此我们springcloudsleuthbrave从springcloudstartersleuth依赖项中排除,而是添加springcloudsleuthotelautoconfigure依赖项。这将基于Brave的默认跟踪实现替换为基于OpenTelemetry的实现。opentelemetryexporterotlptrace:这是SpringCloudSleuthOTel中向OpenTelemetryCollector发送跟踪的组件。
第2步:OpenTelemetry配置
OpenTelemetry收集器端点
对于每个微服务,我们需要在其中添加以下配置application。yml(请参阅下面部分中的示例片段)。spring。sleuth。otel。exporter。otlp。endpoint主要是配置OTelCollector端点。它告诉导出器,在我们的例子中是Sleuth,通过OTLP将跟踪数据发送到指定的收集器端点http:otelcollector:4317。注意otelcollector端点URL来自otelcollector图像的dockercompose服务。
跟踪数据概率抽样
spring。sleuth。otel。config。traceidratiobased属性定义了跟踪数据的采样概率。它根据提供给采样器的分数对一部分迹线进行采样。概率抽样允许OpenTelemetry跟踪用户通过使用随机抽样技术降低跨度收集成本。如果该比率小于1。0,则某些迹线将不会被导出。对于此示例,我们将采样配置为1。0、100。
有关其他OTelSpringCloudSleuth属性,请参阅常见应用程序属性。
spring:application:name:customerservicesleuth:otel:config:traceidratiobased:1。0exporter:otlp:endpoint:http:fontiotelcollector:4317ifontfontfont
OpenTelemetry配置文件
我们需要项目根目录下的OTel配置文件otelconfig。yaml。内容如下。此配置文件定义了OTel接收器、处理器和导出器的行为。正如我们所看到的,我们定义了我们的接收器来监听gRPC和HTTP,处理器使用批处理和导出器作为jaeger和日志记录。
extensions:memoryballast:sizemib:512zpages:endpoint:0。0。0。0:55679receivers:otlp:protocols:grpc:http:processors:batch:exporters:logging:logLevel:debugjaeger:endpoint:jaegerallinone:14250tls:insecure:btruebservice:pipelines:traces:receivers:〔otlp〕processors:〔batch〕exporters:〔logging,jaeger〕extensions:〔memoryballast,zpages〕
第3步:dockercompose将所有内容串在一起
让我们看看我们需要启动哪些docker容器来运行这三个微服务并观察它们的分布式跟踪,前三个微服务在上面的部分中进行了解释。
customerservicebffcustomerserviceorderservicepostgrescustomer:数据库customerservicepostgresorder:数据库orderservicejaegerallinone:运行所有Jaeger后端组件和UI的单个图像otelcollector:OpenTelemetry追踪引擎,接收、处理、导出追踪数据到后端zookeeper:跟踪Kafka集群中节点的状态,维护Kafka主题和消息列表kafka:发布订阅事件流处理平台
services:customerservicebff:image:customerservicebff:0。0。1SNAPSHOTports:font8080:8080fontfontdependson:zookeeperkafkacustomerservice:image:customerservice:0。0。1SNAPSHOTports:fontfont8081:8081fontfontdependson:zookeeperkafkapostgrescustomerenvironment:SPRINGDATASOURCEJDBCURLjdbc:postgresql:fontfontipostgrescustomer:5432customerserviceifontfontSPRINGDATASOURCEUSERNAMEpostgresSPRINGDATASOURCEPASSWORDpostgresSPRINGJPAHIBERNATEDDLAUTOupdateorderservice:image:orderservice:0。0。1SNAPSHOTports:fontfont8082:8082fontfontdependson:zookeeperkafkapostgresorderenvironment:SPRINGDATASOURCEJDBCURLjdbc:postgresql:fontfontipostgresorder:5432orderserviceifontfontSPRINGDATASOURCEUSERNAMEpostgresSPRINGDATASOURCEPASSWORDpostgresSPRINGJPAHIBERNATEDDLAUTOupdatepostgrescustomer:image:postgresports:fontfont5432:5432fontfontenvironment:POSTGRESPASSWORDpostgresPOSTGRESUSERpostgresPOSTGRESDBcustomerservicepostgresorder:image:postgresports:fontfont5431:5431fontfontenvironment:POSTGRESPASSWORDpostgresPOSTGRESUSERpostgresPOSTGRESDBorderservicejaegerallinone:image:jaegertracingallinone:latestports:fontfont16686:16686fontfontfontfont14268fontfontfontfont14250fontfontotelcollector:image:otelopentelemetrycollector:0。47。0command:〔fontfontconfigetcotelcollectorconfig。yamlfontfont〕volumes:。otelconfig。yaml:etcotelcollectorconfig。yamlports:fontfont1888:1888fontfontpprofextensionfontfont13133:13133fontfonthealthcheckextensionfontfont4317fontfontOTLPgRPCreceiverfontfont55670:55679fontfontzpagesextensiondependson:jaegerallinonezookeeper:image:confluentinccpzookeeper:latestenvironment:ZOOKEEPERCLIENTPORT:2181ZOOKEEPERTICKTIME:2000ports:22181:2181kafka:image:confluentinccpkafka:latestdependson:zookeeperports:29092:29092environment:KAFKABROKERID:1KAFKAZOOKEEPERCONNECT:zookeeper:2181KAFKAADVERTISEDLISTENERS:PLAINTEXT:fontfontikafka:9092,PLAINTEXTHOST:localhost:29092ifontfontKAFKALISTENERSECURITYPROTOCOLMAP:PLAINTEXT:PLAINTEXT,PLAINTEXTHOST:PLAINTEXTKAFKAINTERBROKERLISTENERNAME:PLAINTEXTKAFKAOFFSETSTOPICREPLICATIONFACTOR:1font
运行dockercomposeupd以调出所有九个容器:
第4步:追踪数据在行动
快乐之路
现在,让我们启动customerservicebff流程的入口点,以创建新客户。
启动JaegerUI,http:localhost:16686〔urlhttps:link。zhihu。com?targethttp3Alocalhost3A166862C〕按〔url〕服务搜索customerservicebff,单击FindTraces按钮,这是我们看到的创建客户跟踪:它跨越三个服务,总共跨越六个,持续时间82。35毫秒。
除了TraceTimeline视图(上面的屏幕截图),Jaeger还提供了一个图形视图(TraceGraph在右上角的下拉菜单中选择):
三个微服务在docker中的日志输出显示相同的跟踪id,以红色突出显示,并根据其应用程序名称显示不同的跨度id(应用程序名称及其对应的跨度id以匹配的颜色突出显示)。在的情况下customerservice,相同的spanid从RESTAPI请求传递到Kafka发布者请求。
错误场景
customerservice让我们在docker中暂停我们的PostgreSQL数据库,然后重复从customerservicebff。500internalservererror正如预期的那样,我们得到了。检查Jaeger,我们看到以下跟踪,异常堆栈跟踪抱怨SocketTimeoutException,再次如预期的那样。
识别长期运行的跨度
JaegerUI允许我们搜索超过指定最大持续时间的跟踪。例如,我们可以搜索所有耗时超过1000毫秒的跟踪。然后,我们可以深入研究长期运行的跟踪以调查其根本原因。
总结
在这个故事中,我们从OpenTelemetry、SpringCloudSleuth和Jaeger的角度解压了分布式跟踪,验证了RESTAPI调用和Kafkapubsub中分布式跟踪的自动检测。我希望这个故事能让你更好地理解这些跟踪框架和工具,尤其是OpenTelemetry,以及它如何从根本上改变我们在分布式系统中进行可观察性的方式。