1、介绍
在微服务为我们提供了模块分,低耦合的高效开发和DevOPS中,具体业务中当一个请求中,请求了多个服务单元,如果请求出现了错误或异常,很难去定位是哪个服务出了问题,这时就需要链路追踪。可能你会想在业务系统中请求中埋点,或写日志,但是这种都需要在业务代码中来写,而且耦合在代码中,不具备微服务的扩张性后后期的易维护行。
1.1、Jaeger概念
受Dapper和OpenZipkin启发的Jaeger是由Uber Technologies作为开源发布的分布式跟踪系统。它用于监视和诊断基于微服务的分布式系统,包括:
分布式上下文传播
分布式交易监控
根本原因分析
服务依赖性分析性能/延迟优化
1.2、特性
1.2.1、高扩展性
Jaeger后端的设计没有单点故障,可以根据业务需求进行扩展。例如,Uber上任何给定的Jaeger安装通常每天要处理数十亿个跨度。
1.2.2、原生支持OpenTracing
Jaeger后端,Web UI和工具库已完全设计为支持OpenTracing标准。
通过跨度引用将迹线表示为有向无环图(不仅是树)
支持强类型的跨度标签和结构化日志通过行李
支持通用的分布式上下文传播机制
1.2.3、多存储后端
Jaeger支持两个流行的开源NoSQL数据库作为跟踪存储后端:Cassandra 3.4+和Elasticsearch 5.x / 6.x / 7.x。正在进行使用其他数据库的社区实验,例如ScyllaDB,InfluxDB,Amazon DynamoDB。Jaeger还附带了一个简单的内存存储区,用于测试设置。
1.2.4、现代化的UI
Jaeger Web UI是使用流行的开源框架(如React)以Javascript实现的。v1.0中发布了几项性能改进,以允许UI有效处理大量数据,并显示具有成千上万个跨度的跟踪(例如,我们尝试了具有80,000个跨度的跟踪)。
1.2.5、云原生部署
Jaeger后端作为Docker映像的集合进行分发。这些二进制文件支持各种配置方法,包括命令行选项,环境变量和多种格式(yaml,toml等)的配置文件。Kubernetes模板和Helm图表有助于将其部署到Kubernetes集群。
1.2.6、可观察性
默认情况下,所有Jaeger后端组件都公开Prometheus指标(也支持其他指标后端)。使用结构化日志库zap将日志写到标准输出。
1.2.7、安全
Jaeger的第三方安全审核可在https://github.com/jaegertracing/security-audits中获得。有关Jaeger中可用安全机制的摘要,请参见问题#1718。
1.2.8、与Zipkin的向后兼容性
尽管我们建议使用OpenTracing API来对应用程序进行检测并绑定到Jaeger客户端库,以从其他地方无法获得的高级功能中受益,但是如果您的组织已经使用Zipkin库对检测进行了投资,则不必重写所有代码。Jaeger通过在HTTP上接受Zipkin格式(Thrift或JSON v1 / v2)的跨度来提供与Zipkin的向后兼容性。从Zipkin后端切换只是将流量从Zipkin库路由到Jaeger后端的问题。
1.3、jaeger架构:
(详见官网:)
2、下载安装编译jaeger
2.1、直接下载编译好的、适用于不同系统的jaeger
下载地址:https://www.jaegertracing.io/download/
下载对应平台的压缩包后,后解压、配置path路径即可。
2.2、本地编译
安装msys2环境或者cygwin环境
编译需要msys2环境或者cygwin环境。最好在虚拟机的纯linux环境中来执行编译。
安装yarn、dep、cross-env
npm install yarn -y
npm install dep -y
npm install cross-env -y
npm install --global cross-env
yarn global add cross-env
安装yarn:npm install -global yarn -y
安装nodejs
要求版本20以上,请从官网Index of /dist/v22.6.0/下载。
下载代码
下载指定branch或者tag的分支代码
git clone --recurse-submodules GitHub - jaegertracing/jaeger: CNCF Jaeger, a Distributed Tracing Platform
如果没能成功下载子项目 jaeger-ui,需要在 jaeger 项目的根目录下执行如下命令:
git clone --recurse-submodules https://github.com/jaegertracing/jaeger-ui
项目编译
依赖工具安装
make install-tools
可执行文件编译
make build-binaries-linux
jaeger-query 运行
jaeger_query --query.port=${serverport} --query.static-files=${jaeger-ui-filepath} --query.base-path=/jaeger --es.server-urls=http://${ip}:${port} --es.username=${es_user} --es.password=${es_pwd}
query.port:服务监听端口
query.static-files: 静态文件的路径,一般为index.html 所在的文件夹
query.base-path:浏览器中URL的前缀,参数中的 /jaeger就是后面URL中的 /jaeger,http://127.0.0.1:32765/jaeger/search,--query.base-path=/xxx/jaeger 这样的话访问默认IP端口就会跳转到 http://127.0.0.1:32765/xxx/jaeger/search
3、测试用的All in One多功能一体的启动方式
多功能一体是一个为快速本地测试而设计的可执行文件。它包括Jaeger-UI、Jaeger收集器、Jaeger-query和Jaeger-agent,以及一个内存存储组件。
3.1、启动方式
(1)docker启动(仅能用于测试环境)
启动多功能一体的最简单方法是使用发布到DockerHub的预构建映像(单个命令行)。
docker run --rm --name jaeger \
-e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
-p 6831:6831/udp \
-p 6832:6832/udp \
-p 5778:5778 \
-p 16686:16686 \
-p 4317:4317 \
-p 4318:4318 \
-p 14250:14250 \
-p 14268:14268 \
-p 14269:14269 \
-p 9411:9411 \
jaegertracing/all-in-one:1.59
(2)可执行文件启动(仅能用于测试环境)
jaeger-all-in-one --collector.zipkin.host-port=:9411
4、Kubernetes启动方式
Please see Kubernetes Operator: https://github.com/jaegertracing/jaeger-operator
5、生产环境启动方式
特别注意:all-in-one的方式将所有的数据存储在计算机内存中,给计算机内存带来巨大负担,同时,当all-in-one关闭后,所有的jaeger数据也会全部清空,因此,不建议在生产环境中使用all-in-one的方式;
5.1、组件说明
(1)jaeger-all-in-one(只能测试的时候使用!)
专为快速本地测试而设计。它通过内存存储组件启动Jaeger UI、collector收集器、query查询和agent代理。
(2)jaeger-agent代理(已弃用!)
从Jaeger客户端接收跨度并转发给收集器。设计为作为sidecar或host agent运行。jaeger代理已弃用,不再建议使用,请参阅https://github.com/jaegertracing/jaeger/issues/4739。
建议直接使用jaeger-collector收集器。
(3)jaeger-collector收集器
从代理或直接从客户端接收跨度,并将其保存在持久存储中。
这些代理可以是SpringBoot的项目。
(4)jaeger-ingester(针对kafka,这里不适用!)
收集器的替代品;从Kafka主题读取跨度并将其保存到存储中。
(5)jaeger-query查询
提供Jaeger UI和API,用于从存储中检索跟踪。
5.2、安装配置cassandra数据库(也可以选择配置es)
如何安装、配置cassandra数据,详见Cassandra安装部署相关文章。
在这里,Cassandra安装部署在hyper-v虚拟机中。
启动cassandra数据库的方式:cassandra -R
访问cassandra数据看得方式:cqlsh <IP> <端口>
5.3、在cassandra数据中建立对应的数据库和数据表
1、创建建表脚本create.sh
官方提供的create.sh内容地址:
jaeger/plugin/storage/cassandra/schema at main · jaegertracing/jaeger · GitHub
create.sh内容:
#!/usr/bin/env bash
function usage {
>&2 echo "Error: $1"
>&2 echo ""
>&2 echo "Usage: MODE=(prod|test) [PARAM=value ...] $0 [template-file] | cqlsh"
>&2 echo ""
>&2 echo "The following parameters can be set via environment:"
>&2 echo " MODE - prod or test. Test keyspace is usable on a single node cluster (no replication)"
>&2 echo " DATACENTER - datacenter name for network topology used in prod (optional in MODE=test)"
>&2 echo " TRACE_TTL - time to live for trace data, in seconds (default: 172800, 2 days)"
>&2 echo " DEPENDENCIES_TTL - time to live for dependencies data, in seconds (default: 0, no TTL)"
>&2 echo " KEYSPACE - keyspace (default: jaeger_v1_{datacenter})"
>&2 echo " REPLICATION_FACTOR - replication factor for prod (default: 2 for prod, 1 for test)"
>&2 echo " VERSION - Cassandra backend version, 3 or 4 (default: 4). Ignored if template is provided."
>&2 echo ""
>&2 echo "The template-file argument must be fully qualified path to a v00#.cql.tmpl template file."
>&2 echo "If omitted, the template file with the highest available version will be used."
exit 1
}
trace_ttl=${TRACE_TTL:-172800}
dependencies_ttl=${DEPENDENCIES_TTL:-0}
cas_version=${VERSION:-4}
template=$1
if [[ "$template" == "" ]]; then
case "$cas_version" in
3)
template=$(dirname $0)/v003.cql.tmpl
;;
4)
template=$(dirname $0)/v004.cql.tmpl
;;
*)
template=$(ls $(dirname $0)/*cql.tmpl | sort | tail -1)
;;
esac
fi
if [[ "$MODE" == "" ]]; then
usage "missing MODE parameter"
elif [[ "$MODE" == "prod" ]]; then
if [[ "$DATACENTER" == "" ]]; then usage "missing DATACENTER parameter for prod mode"; fi
datacenter=$DATACENTER
replication_factor=${REPLICATION_FACTOR:-2}
replication="{'class': 'NetworkTopologyStrategy', '$datacenter': '${replication_factor}' }"
elif [[ "$MODE" == "test" ]]; then
datacenter=${DATACENTER:-'test'}
replication_factor=${REPLICATION_FACTOR:-1}
replication="{'class': 'SimpleStrategy', 'replication_factor': '${replication_factor}'}"
else
usage "invalid MODE=$MODE, expecting 'prod' or 'test'"
fi
keyspace=${KEYSPACE:-"jaeger_v1_${datacenter}"}
if [[ $keyspace =~ [^a-zA-Z0-9_] ]]; then
usage "invalid characters in KEYSPACE=$keyspace parameter, please use letters, digits or underscores"
fi
if [ ! -z "$COMPACTION_WINDOW" ]; then
if echo "$COMPACTION_WINDOW" | grep -E -q '^[0-9]+[mhd]$'; then
compaction_window_size="$(echo "$COMPACTION_WINDOW" | sed 's/[mhd]//')"
compaction_window_unit="$(echo "$COMPACTION_WINDOW" | sed 's/[0-9]//g')"
else
usage "Invalid compaction window size format. Please use numeric value followed by 'm' for minutes, 'h' for hours, or 'd' for days."
fi
else
trace_ttl_minutes=$(( $trace_ttl / 60 ))
# Taking the ceiling of the result
compaction_window_size=$(( ($trace_ttl_minutes + 30 - 1) / 30 ))
compaction_window_unit="m"
fi
case "$compaction_window_unit" in
m) compaction_window_unit="MINUTES" ;;
h) compaction_window_unit="HOURS" ;;
d) compaction_window_unit="DAYS" ;;
esac
>&2 cat <<EOF
Using template file $template with parameters:
mode = $MODE
datacenter = $datacenter
keyspace = $keyspace
replication = ${replication}
trace_ttl = ${trace_ttl}
dependencies_ttl = ${dependencies_ttl}
compaction_window_size = ${compaction_window_size}
compaction_window_unit = ${compaction_window_unit}
EOF
# strip out comments, collapse multiple adjacent empty lines (cat -s), substitute variables
cat $template | sed \
-e 's/--.*$//g' \
-e 's/^\s*$//g' \
-e "s/\${keyspace}/${keyspace}/g" \
-e "s/\${replication}/${replication}/g" \
-e "s/\${trace_ttl}/${trace_ttl}/g" \
-e "s/\${dependencies_ttl}/${dependencies_ttl}/g" \
-e "s/\${compaction_window_size}/${compaction_window_size}/g" \
-e "s/\${compaction_window_unit}/${compaction_window_unit}/g" | cat -s
将create.sh转成unix符号:dos2unix ./create.sh
2、获取对应的表结构
参考文章:https://www.jianshu.com/p/41d423ab8cce
表结构地址:jaeger/plugin/storage/cassandra/schema at main · jaegertracing/jaeger · GitHub
本地存储的表结构位置:D:\Program\jaeger\cassandra表结构
需要注意的是,不同版本的jaeger使用的表结构不同。
当前版本比较高,因此使用v004.cql.tmpl来建表。
3、进入cassandra数据库,并建立对应的数据库和数据表
在控制台输入命令:cqlsh 192.168.77.31 9042
然后进入到数据库中。
(1)建立数据库jaeger_v1_prod:
CREATE KEYSPACE jaeger_v1_prod WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };
(2)使用数据库jaeger_v1_prod:
use jaeger_v1_prod
(3)建立数据表service_names、operation_names:
sudo MODE=prod DATACENTER=prod ./create.sh /mnt/share/v004.cql.tmpl | cqlsh 192.168.77.31 9042
其中,create.sh文件是官方提供的sh导入建表的脚本。
5.4、启动方式
实际部署中,如果不使用kafka可以仅仅开启两个应用:jaeger-collector收集器、jaeger-query查询
1、配置PATH系统环境变量
首先,需要配置PATH系统环境变量:
SPAN_STORAGE_TYPE=cassandra
SAMPLING_STORAGE_TYPE=cassandra
2、启动jaeger-collector收集器
直接在控制台执行命令:
jaeger-collector.exe --cassandra.keyspace jaeger_v1_prod --cassandra.servers 192.168.77.31 --cassandra.port 9042
3、启动jaeger-query查询
直接在控制台执行命令:
jaeger-query.exe --cassandra.keyspace jaeger_v1_prod --cassandra.servers 192.168.77.31 --cassandra.port 9042
*4、docker启动方式参考(这里不适用!)
docker run -d --name=jaeger-collector -p 9411:9411 -p 14250:14250 -p 14268:14268 -p 14269:14269 -e SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://1.2.1.5:9200 -e ES_INDEX_PREFIX=test jaegertracing/jaeger-collector:1.38
docker run -d --name=jaeger-agent -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778/tcp -p 5775:5775/udp -e REPORTER_GRPC_HOST_PORT=1.2.1.5:14250 jaegertracing/jaeger-agent:1.38
docker run -d --name=jaeger-query -p 16686:16686 -p 16687:16687 -e SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://1.2.1.5:9200 -e ES_INDEX_PREFIX=test jaegertracing/jaeger-query:1.38
Jaeger Spark依赖项 这是一个Spark作业,它从存储中收集跨度,分析服务之间的链接,并将其存储以供以后在UI中呈现
docker pull jaegertracing/spark-dependencies:latest
下面这个是执行一次的,会自动退出,每次更新需要执行一次
docker run -d --name=jaeger-spark -e STORAGE=elasticsearch -e ES_NODES=http://1.2.1.5:9200 -e ES_INDEX_PREFIX=test jaegertracing/spark-dependencies:latest
6、访问jaeger前端
直接访问:http://localhost:16686、http://localhost:16686/search,就可以打开jaeger页面。
7、jaeger相关端口说明
以下端口基本固定,不建议擅自改动。
Port | Protocol | Component | Function |
6831 | UDP | agent | accept jaeger.thrift over Thrift-compact protocol (used by most SDKs) |
6832 | UDP | agent | accept jaeger.thrift over Thrift-binary protocol (used by Node.js SDK) |
5775 | UDP | agent | (deprecated) accept zipkin.thrift over compact Thrift protocol (used by legacy clients only) |
5778 | HTTP | agent | serve configs (sampling, etc.) |
16686 | HTTP | query | serve frontend |
4317 | HTTP | collector | accept OpenTelemetry Protocol (OTLP) over gRPC |
4318 | HTTP | collector | accept OpenTelemetry Protocol (OTLP) over HTTP |
14268 | HTTP | collector | accept jaeger.thrift directly from clients |
14250 | HTTP | collector | accept model.proto |
9411 | HTTP | collector | Zipkin compatible endpoint (optional) |
6、SpringBoot集成jaeger
创建项目,准备实施 Jaeger 跟踪
我们从https://start.spring.io创建一个只有一个“Spring Web”依赖项的应用程序。
引入jeager依赖
生成并下载代码后,我们会将以下 Jaeger 依赖项添加到 pom 文件中,这将有助于在服务之间生成和传播跟踪。
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
<version>3.3.1</version>
</dependency>
编写controller
有了这个,让我们添加一个带有一些路径的控制器。
@RestController
@RequestMapping("/service")
public class Controller {
private static final Logger logger = LoggerFactory.getLogger(Controller.class);
private RestTemplate restTemplate;
@Value("${spring.application.name}")
private String applicationName;
public Controller(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@GetMapping("/path1")
public ResponseEntity path1() {
logger.info("Incoming request at {} for request /path1 ", applicationName);
String response = restTemplate.getForObject("http://localhost:8090/service/path2", String.class);
return ResponseEntity.ok("response from /path1 + " + response);
}
@GetMapping("/path2")
public ResponseEntity path2() {
logger.info("Incoming request at {} at /path2", applicationName);
return ResponseEntity.ok("response from /path2 ");
}
}
在这里,我们有两个端点:/path1和/path2。这里的想法是使用同一应用程序的两个实例,以便在固定端口 8090/path1调用另一个服务。/path2
*创建一个RestTemplate bean(不是必须的)
为了使跨度连接到相同的跟踪 ID,我们需要创建一个 RestTemplate bean 以允许 Jaeger 包含一个拦截器。然后,这有助于向传出请求添加跟踪,这将有助于跟踪整个请求。
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
配置yml
现在,让我们添加一些属性以允许应用程序将跟踪发送到 Jaeger 服务器。我们将通过 TCP 进行通信,因此请确保我们将跟踪发送到另一个 TCP 端口,即 14268
opentracing:
jaeger:
http-sender:
url: http://localhost:14268/api/traces
启动SpringBoot测试服务
让我们使用以下命令启动“服务器 1”。
java -jar
target/Distributed-Service-0.0.1-SNAPSHOT.jar
--spring.application.name=Service-1
--server.port=8080
然后在不同的终端上,运行与“服务 2”相同的应用程序的新实例,如下所示
java -jar
target/Distributed-Service-0.0.1-SNAPSHOT.jar
--spring.application.name=Service-2
--server.port=8090
测试微服务之间的服务调用
应用程序启动后,调用“Service 1”,/path1如下所示
curl -i http://localhost:8080/service/path1
让我们看一下“服务1”的日志。
INFO 69938 --- [nio-8080-exec-1] i.j.internal.reporters.LoggingReporter : Span reported: ed70bbaa2bd5b42f:c7c94163fc95fc1e:ed70bbaa2bd5b42f:1 - GET
跟踪的格式为 [Root Span ID, Current Span ID, Parent Span ID]。在这种情况下,由于“服务 1”是发起服务,因此父跨度 IDed70bbaa2bd5b42f也是根跨度 ID。
现在,让我们看一下“服务 2”的日志。
INFO 69885 --- [nio-8090-exec-1] i.j.internal.reporters.LoggingReporter : Span reported: ed70bbaa2bd5b42f:e9060cb1d5336c55:c7c94163fc95fc1e:1 - path2
从这里我们看到中间值是当前 span ID,父 span ID(即第三个值c7c94163fc95fc1e)是“Service 1”的 span ID。
查看jaeger前端展示的跟踪信息
直接访问:http://localhost:16686,就可以打开jaeger页面。
如果您打开 UI,您将看到以下内容:
当我们深入挖掘时,我们会看到每个跨度的更多细节。
在这里,根跨度 IDed70bbaa2bd5b42f跨越整个请求。其他两个跨度 ID 指的是单个服务。
7、分布式链路追踪的基本概念
分布式链路追踪是一种监控和分析分布式系统中请求流动的方法。它能够记录和分析一个请求在系统中经历的每一步操作,帮助开发者和运维人员了解系统的性能和行为。在微服务架构中,一个请求可能会跨越多个服务节点,而每个服务节点又可能依赖其他多个服务。分布式链路追踪通过生成一个唯一的跟踪ID(Trace ID),并在每个服务节点生成一个跨度(Span),记录每个操作的详细信息,从而形成完整的请求链路。
Span 和 Trace
Trace:表示一个完整的请求链路,从请求发起到请求完成,包含了所有相关的 Spans。
Span:表示 Trace 中的一个单独的操作单元,包含操作的开始时间、结束时间、操作名称、相关的元数据(如标签、日志)等信息。一个 Trace 由多个 Spans 组成,形成一个有向无环图(DAG)。
上下文传播
在分布式系统中,请求在不同服务节点之间传递时,需要传递跟踪信息以保持 Trace 的连续性。上下文传播是指将 Trace ID 和 Span ID 等信息通过 HTTP 头、消息队列等方式在服务之间传递,使得每个服务都能够关联到同一个 Trace。
采样策略
由于分布式系统中的请求量通常非常大,记录每一个请求的详细信息会带来巨大的存储和性能开销。采样策略通过对请求进行采样,只记录部分请求的跟踪信息,从而减少开销。常见的采样策略包括随机采样、基于请求类型的采样、基于错误率的采样等。
8、分布式链路追踪的工作原理
分布式链路追踪的核心工作原理包括以下几个步骤:
请求拦截:在请求进入系统时,生成一个唯一的 Trace ID,并为每个操作生成 Span ID。将这些跟踪信息注入到请求的上下文中。
上下文传播:在请求在服务之间传递时,将跟踪信息通过 HTTP 头、消息队列等方式传递,确保每个服务节点都能获取到跟踪信息。
数据收集:每个服务节点在处理请求时,记录当前 Span 的详细信息,包括开始时间、结束时间、操作名称、元数据等。
数据传输:将收集到的跟踪数据通过日志、消息队列等方式传输到集中式的跟踪存储系统中。
数据存储:将跟踪数据存储在分布式存储系统中,以便后续查询和分析。
数据展示:通过可视化工具展示跟踪数据,帮助用户分析和诊断系统行为。
9、常见的分布式链路追踪系统
Zipkin
Zipkin 是 Twitter 开源的一个分布式链路追踪系统,能够帮助用户收集和分析系统中的跟踪数据。Zipkin 的核心组件包括:
Zipkin Collector:负责收集跟踪数据并存储到后端存储系统中。
Zipkin Query:提供查询接口,允许用户通过 API 或 Web 界面查询跟踪数据。
Zipkin Web UI:提供可视化界面,帮助用户分析和展示跟踪数据。
Jaeger
Jaeger 是 Uber 开源的分布式链路追踪系统,设计初衷是解决微服务架构中的监控和故障排除问题。Jaeger 提供了与 Zipkin 类似的功能,并在可扩展性和性能上进行了优化。Jaeger 的核心组件包括:
Jaeger Agent:运行在主机上,负责收集应用程序发送的跟踪数据并转发给 Jaeger Collector。
Jaeger Collector:接收 Jaeger Agent 发送的跟踪数据并存储到后端存储系统中。
Jaeger Query:提供查询接口,允许用户通过 API 或 Web 界面查询跟踪数据。
Jaeger UI:提供可视化界面,帮助用户分析和展示跟踪数据。
OpenTelemetry
OpenTelemetry 是一个联合的开源项目,由 OpenTracing 和 OpenCensus 项目合并而来,旨在为分布式系统提供统一的监控、跟踪和日志收集工具。OpenTelemetry 提供了 SDK 和 API,支持多种编程语言,并与多种后端系统兼容。OpenTelemetry 的核心组件包括:
OpenTelemetry SDK:提供多种编程语言的 SDK,帮助开发者在应用程序中集成跟踪功能。
OpenTelemetry Collector:一个独立的服务,负责收集、处理和导出跟踪数据。
OpenTelemetry Protocol:统一的协议,支持多种后端系统,如 Jaeger、Zipkin、Prometheus 等。
10、分布式链路追踪的技术实现
数据收集
数据收集是分布式链路追踪的第一步,需要在每个服务节点中集成跟踪 SDK,以拦截和记录请求的详细信息。常见的集成方式包括:
拦截器:在 HTTP 请求的入口和出口处添加拦截器,生成和记录 Span 信息。
中间件:在应用程序中使用中间件,自动处理跟踪信息的生成和传播。
手动注入:在代码中手动添加跟踪信息的生成和记录逻辑。
数据传输
数据传输负责将收集到的跟踪数据发送到集中式的存储系统中。常见的传输方式包括:
日志文件:将跟踪数据写入日志文件,通过日志收集系统(如 ELK Stack)集中处理和存储。
消息队列:通过消息队列(如 Kafka、RabbitMQ)传输跟踪数据,确保数据的可靠传输和处理。
HTTP 请求:直接通过 HTTP 请求将跟踪数据发送到后端存储系统。
数据存储
数据存储负责将传输到的跟踪数据持久化,支持高效的查询和分析。常见的存储系统包括:
关系型数据库:如 MySQL、PostgreSQL,适用于小规模的跟踪数据存储。
分布式数据库:如 Cassandra、Elasticsearch,适用于大规模的跟踪数据存储,支持高并发和快速查询。
对象存储:如 Amazon S3、Google Cloud Storage,适用于冷数据存储,成本较低。
数据展示
数据展示通过可视化工具将跟踪数据展示给用户,帮助用户分析
和诊断系统行为。常见的可视化工具包括:
Zipkin Web UI:提供基本的跟踪数据查询和展示功能,支持时序图、依赖关系图等。
Jaeger UI:提供丰富的跟踪数据查询和展示功能,支持高级查询、时序图、依赖关系图等。
Grafana:通过插件支持展示跟踪数据,能够与其他监控数据(如指标、日志)集成,提供统一的监控视图。
11、分布式链路追踪的应用场景
性能优化
通过分布式链路追踪,开发者能够清晰地看到请求在系统中的每一步操作及其耗时,从而识别性能瓶颈。例如,某个服务响应时间过长,或某个数据库查询耗时过多,开发者可以据此进行优化,提升系统整体性能。
故障排除
在分布式系统中,定位故障原因往往非常困难。分布式链路追踪通过记录详细的请求流动信息,帮助运维人员快速定位故障点。例如,某个请求在某个服务节点出现错误,或某个服务依赖的外部接口不可用,运维人员可以通过跟踪数据迅速找到问题所在,进行修复。
依赖分析
分布式链路追踪能够展示系统中各个服务之间的依赖关系,帮助开发者和运维人员更好地理解系统架构。例如,通过依赖关系图可以看到某个服务依赖了哪些其他服务,这些服务又依赖了哪些外部系统,从而更好地进行系统设计和优化。