1. 什么是链路追踪

链路追踪是指在分布式系统中,将一次请求的处理过程进行记录并聚合展示的一种方法。目的是将一次分布式请求的调用情况集中在一处展示,如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等。这样就可以轻松了解一个请求在系统中的完整生命周期,包括经过的服务、调用的操作以及每个操作的延迟等。通过链路追踪,可以更好地理解系统的性能瓶颈、找出问题的根源以及优化系统的性能。
如下图就是一个简单的微服务中的调用过程,如果我们没有链路追踪,且每个服务都是一个多节点集群,想要搞清楚一个请求是怎么走的就非常困难。
链路追踪

2. 链路追踪的重要性

在分布式系统中,由于服务节点众多且相互之间存在复杂的依赖关系,所以一旦出现故障,排查起来往往非常困难。而链路追踪可以有效地帮助解决这个问题。具体是以下几个方面:

快速定位问题:当应用程序出现故障时,开发人员可以通过链路追踪来快速定位到故障的原因。通过查看元数据,可以确定故障发生的位置以及导致故障的请求数据,加速故障的排查过程。

优化程序性能:链路追踪可以帮助开发人员分析应用程序的性能瓶颈。通过观察数据在各个节点之间的流动情况,可以确定哪些节点的性能较差,并针对这些节点进行优化。

分析安全问题:通过观察数据在系统中的流动情况,可以发现潜在的安全漏洞和攻击路径,例如DDoS攻击、中间人攻击、SQL注入攻击等。有助于提高系统的安全性,并减少潜在的安全风险。

3. 链路追踪的实现

链路追踪的实现方式很多,你可以通过不同工具去实现。
如果你的微服务用的是Spring Cloud, 其实spring cloud已经有很完美的解决方案。
Spring Cloud 链路追踪通常使用 Spring Cloud Sleuth 来实现。Spring Cloud Sleuth 集成了 Zipkin 和 Brave 来提供链路追踪功能。
但是也有很多微服务没有用Spring Cloud,如果我们只需要简单的链路追踪,也可以自己手写一份实现。
手写也不复杂,但是需要实现的人考虑周全,把链路追踪写好一点。本篇及后续篇章主要介绍手写的不同实现方式。
链路追踪的核心是日志追踪,下面我们尝试用代码来实现日志追踪。

3.1 实现一个API接口

模拟一个登录接口API。 API包含如下实现

  • API接口参数包括request body;
  • 能接收可能包含trace id的header;
  • 读取当前线程名(用于线程结束前还原线程名);
  • 如果请求头中没有包含trace id, 自动生成一个;
  • 在请求开始的时候替换当前线程名,直到维持到请求结束前,用于修改日志文件的线程名,用它来做日志信息追踪;
  • 用一个for loop模拟登录过程的日志
package com.sandwich.logtracing.controller;import com.sandwich.logtracing.entity.ApiResponse;
import com.sandwich.logtracing.util.RandomStrUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;/*** @Author 公众号: IT三明治* @Date 2025/8/29* @Description: login demo controller*/
@Slf4j
@RestController
@RequestMapping("/test")
public class LoginController {@PostMapping("/login")public ApiResponse<String> login(@RequestBody LoginRequest loginRequest,@RequestHeader(value = "x-request-correlation-id", required = false) String traceId) {String currentThreadName = Thread.currentThread().getName();//if the request header don't have a trace id,then generate a random oneif (StringUtils.isBlank(traceId)) {traceId = RandomStrUtils.generateRandomString(15);}//replace current thread name with a trace idThread.currentThread().setName(traceId);log.info("previous thread name:{}", currentThreadName);for (int i=1; i<= 10; i++) {log.info("processing login for user {}, login step {} done", loginRequest.getUsername(), i);}log.info("user {} login success", loginRequest.getUsername());//restore thread name before api request endThread.currentThread().setName(currentThreadName);return ApiResponse.success("Sandwich login success", traceId);}@Datapublic static class LoginRequest {private String username;private String password;}
}

3.2 定义一个response entity

这个entity跟其他response不同之处在于它除了能支持一个泛型的data,还可以支持trace id返回

package com.sandwich.logtracing.entity;import lombok.Data;
import lombok.experimental.Accessors;/*** @Author 公众号: IT三明治* @Date 2025/8/29* @Description: api response entity*/
@Data
@Accessors(chain = true)
public class ApiResponse<T> {private int responseCode;private String message;private T data;private String traceId;public static  <T> ApiResponse<T> success(T data, String traceId) {return new ApiResponse<T>().setResponseCode(ResponseCode.SUCCESS.getCode()).setMessage(ResponseCode.SUCCESS.getMessage()).setData(data).setTraceId(traceId);}public static ApiResponse<String> success() {return new ApiResponse<String>().setResponseCode(ResponseCode.SUCCESS.getCode()).setMessage(ResponseCode.SUCCESS.getMessage());}public static  <T> ApiResponse<T> success(T data) {return new ApiResponse<T>().setResponseCode(ResponseCode.SUCCESS.getCode()).setMessage(ResponseCode.SUCCESS.getMessage()).setData(data);}}

准备一个枚举保存response code和对应的message, 这个demo我只用一个success的

package com.sandwich.logtracing.constant;import lombok.Getter;/*** @Author 公众号: IT三明治* @Date 2025/8/29* @Description:*/
@Getter
public enum ResponseCode {SUCCESS(200, "success"),FAIL(500, "internal error"),NOT_FOUND(404, "not found"),UNAUTHORIZED(401, "unauthorized"),FORBIDDEN(403, "forbidden"),NOT_ACCEPTABLE(406, "not acceptable"),REQUEST_TIMEOUT(408, "request timeout"),CONFLICT(409, "conflict"),UNSUPPORTED_MEDIA_TYPE(415, "unsupported media type"),TOO_MANY_REQUESTS(429, "too many requests");private final int code;private final String message;ResponseCode(int code, String message) {this.code = code;this.message = message;}
}

3.3 用shell写一个api请求(login.shell)

为了更好地展示api的所有信息,我选择用shell完成api请求,shell需要完成的功能如下:

  • 自动生成trace id
  • 自动组装api request,包括请求数据类型,header, payload
  • 用python格式化返回的json结构体
#!/bin/bash# Define the API endpoint
API_URL="http://localhost:8080/test/login"function generate_random_string() {# 使用openssl生成随机字符串(如果已安装)if command -v openssl &> /dev/null; thenopenssl rand -base64 20 | tr -dc 'a-zA-Z0-9' | fold -w 15 | head -n 1else# 使用系统方法生成local chars="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"local result=""result=$(printf "%s" "${chars:$((RANDOM % ${#chars})):1}"{1..15} | tr -d '\n')echo "$result"fi
}function normalLogin() {# 生成15位随机字符串作为traceIdtraceId=$(generate_random_string)response=$(curl -X POST $API_URL \-H "Content-Type: application/json" \-H "x-request-correlation-id: $traceId" \-d '{"username": "Sandwich", "password": "test"}')echo "Response from login API:"echo "$response" | python -m json.tool
}normalLogin

4. 测试验证

  • 启动项目
  • 执行请求
Administrator@USER-20230930SH MINGW64 /d/git/java/log-tracing/shell (master)
$ ./login.sh% Total    % Received % Xferd  Average Speed   Time    Time     Time  CurrentDload  Upload   Total   Spent    Left  Speed
100   144    0   100  100    44    493    217 --:--:-- --:--:-- --:--:--   712
Response from login API:
{"responseCode": 200,"message": "success","data": "Sandwich login success","traceId": "wwDJbM12XdX562A"
}
  • 用trace id追踪日志信息

5. 总结

这就是一个最小的链路追踪过程,非常简单,我连日志管理文件都没有配置,只用了springboot 默认的日志系统。

无疑它是非常不完善的,请关注我,下期我再逐步优化它。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/94943.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/94943.shtml
英文地址,请注明出处:http://en.pswp.cn/web/94943.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零开始的python学习——常量与变量

ʕ • ᴥ • ʔ づ♡ど &#x1f389; 欢迎点赞支持&#x1f389; 个人主页&#xff1a;励志不掉头发的内向程序员&#xff1b; 专栏主页&#xff1a;python学习专栏&#xff1b; 文章目录 前言 一、常量和表达式 二、变量类型 2.1、什么是变量 2.2、变量语法 &#xff08;1&a…

基于51单片机环境监测设计 光照 PM2.5粉尘 温湿度 2.4G无线通信

1 系统功能介绍 本设计是一套 基于51单片机的环境监测系统&#xff0c;能够实时采集环境光照、PM2.5、温湿度等参数&#xff0c;并通过 2.4G无线模块 NRF24L01 实现数据传输。系统具备本地显示与报警功能&#xff0c;可通过按键设置各类阈值和时间&#xff0c;方便用户进行环境…

【Flask】测试平台开发,产品管理实现添加功能-第五篇

概述在前面的几篇开发文章中&#xff0c;我们只是让数据在界面上进行了展示&#xff0c;但是没有添加按钮的功能&#xff0c;接下来我们需要开发一个添加的按钮&#xff0c;用户产品功能的创建和添加抽公共数据链接方法添加接口掌握post实现和请求数据处理前端掌握Button\Dilog…

循环高级(2)

6.练习3 打印九九乘法表7.练习3 制表符详解对齐不了原因&#xff1a;name补到8zhangsan本身就是8&#xff0c;补完就变成16解决办法&#xff1a;1.去掉zhangsan\t,这样前后都是82.name后面加2个\t加一个\t&#xff0c;name\t就是占8个&#xff0c;再加一个\t&#xff0c;就变成…

盒马生鲜 小程序 逆向分析

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 逆向分析 部分python代码 params {&…

【Linux系统】线程控制

1. POSIX线程库 (pthreads)POSIX线程&#xff08;通常称为pthreads&#xff09;是IEEE制定的操作系统线程API标准。Linux系统通过glibc库实现了这个标准&#xff0c;提供了创建和管理线程的一系列函数。核心特性命名约定&#xff1a;绝大多数函数都以 pthread_ 开头&#xff0c…

【Spring Cloud Alibaba】前置知识

【Spring Cloud Alibaba】前置知识1. 微服务介绍1.1 系统架构的演变1.1.1 单体应用架构1.1.2 垂直应用架构1.1.3 分布式架构1.1.3.1 SOA架构1.1.4 微服务架构1. 微服务介绍 1.1 系统架构的演变 随着互联网的发展&#xff0c;网站应用的规模也在不断的扩大&#xff0c;进而导致…

2025互联网大厂Java面试1000道题目及参考答案

Java学到什么程度可以面试工作&#xff1f; 要达到能够面试Java开发工作的水平&#xff0c;需要掌握以下几个方面的知识和技能&#xff1a; 1. 基础扎实&#xff1a;熟悉Java语法、面向对象编程概念、异常处理、I/O流等基础知识。这是所有Java开发者必备的基础&#xff0c;也…

记录:HSD部署(未完成)

建数据库 相关文档&#xff1a;Confluence准备&#xff1a;CA文件和备份用的aws key。 CA文件&#xff1a;在namespace添加trust-injectionenabled的标签&#xff0c;会自动生成。 aws key&#xff1a;生成cnpg-backup-creds的secret。安装&#xff1a; 从git仓库获取values模…

【AI】提示词与自然语言处理:从NLP视角看提示词的作用机制

提示词与自然语言处理&#xff1a;从 NLP 视角看提示词的作用机制在人工智能快速发展的今天&#xff0c;大模型成为了人们关注的焦点。而要让大模型更好地理解人类意图、完成各种任务&#xff0c;提示词扮演着关键角色。从自然语言处理&#xff08;NLP&#xff09;的角度来看&a…

2025.8.29机械臂实战项目

好久没给大家更新了&#xff0c;上周末大学大四开学&#xff0c;所以停更了几天&#xff0c;回来后在做项目&#xff0c;接下来的几篇文章&#xff0c;给大家带来几个项目&#xff0c;第一个介绍的是机械臂操作&#xff0c;说是机械臂操作&#xff0c;简单来说&#xff0c;就是…

【机器学习基础】机器学习的要素:任务T、性能度量P和经验E

第一章 机器学习的本质与理论框架 机器学习作为人工智能领域的核心支柱,其理论基础可以追溯到20世纪中叶的统计学习理论。Tom Mitchell在其1997年的经典著作《Machine Learning》中给出了一个至今仍被广泛引用的学习定义:"对于某类任务T和性能度量P,一个计算机程序被认…

wav音频转C语言样点数组

WAV to C Header Converter 将WAV音频文件转换为C语言头文件的Python脚本&#xff0c;支持将音频数据嵌入到C/C项目中。 功能特性 音频格式支持 PCM格式&#xff1a;支持8位、16位、24位、32位PCM音频IEEE Float格式&#xff1a;支持32位浮点音频多声道&#xff1a;支持单声道、…

01.《基础入门:了解网络的基本概念》

网络基础 文章目录网络基础网络通信核心原理网络通信定义信息传递过程关键术语解释网络的分类网络参考模型OSI 参考模型各层核心工作分层核心原则TCP/IP 参考模型&#xff08;4 层 / 5 层&#xff0c;实际应用模型&#xff09;TCP/IP 与 OSI 模型的对应关系传输层核心协议&…

基于vue驾校管理系统的设计与实现5hl93(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表&#xff1a;项目功能&#xff1a;学员,教练,教练信息,预约信息,场地信息,时间安排,车辆信息,预约练车,时间段,驾校场地信息,驾校车辆信息,预约报名开题报告内容&#xff1a;一、选题背景与意义背景随着汽车保有量持续增长&#xff0c;驾校行业规模不断扩大&am…

灰度思维:解锁世界原有本色的密码

摘要本文深入探讨灰度思维的概念内涵及其在处理他人评价中的应用价值。研究指出&#xff0c;灰度思维作为一种超越非黑即白的思维方式&#xff0c;能够帮助个体以更客观、全面的态度接受他人评价的片面性&#xff0c;从而促进个人成长和人际关系和谐。文章分析了他人评价片面性…

动态规划--Day03--打家劫舍--198. 打家劫舍,213. 打家劫舍 II,2320. 统计放置房子的方式数

动态规划–Day03–打家劫舍–198. 打家劫舍&#xff0c;213. 打家劫舍 II&#xff0c;2320. 统计放置房子的方式数 今天要训练的题目类型是&#xff1a;【打家劫舍】&#xff0c;题单来自灵艾山茶府。 掌握动态规划&#xff08;DP&#xff09;是没有捷径的&#xff0c;咱们唯一…

Nuxt.js@4 中管理 HTML <head> 标签

可以在 nuxt.config.ts 中配置全局的 HTML 标签&#xff0c;也可以在指定 index.vue 页面中配置指定的 HTML 标签。 在 nuxt.config.ts 中配置 HTML 标签 export default defineNuxtConfig({compatibilityDate: 2025-07-15,devtools: { enabled: true },app: {head: {charse…

UCIE Specification详解(十)

文章目录4.5.3.7 PHYRETRAIN&#xff08;物理层重训练&#xff09;4.5.3.7.1 Adapter initiated PHY retrain4.5.3.7.2 PHY initiated PHY retrain4.5.3.7.3 Remote Die requested PHY retrain4.5.3.8 TRAIN ERROR4.5.3.9 L1/L24.6 Runtime Recalibration4.7 Multi-module Link…

电商数据的获取方式:API、爬虫、第三方服务及更多

在竞争激烈的电商领域&#xff0c;数据是驱动业务增长的关键。准确、及时地获取电商数据&#xff0c;并进行深入分析&#xff0c;能够帮助企业洞察市场趋势、优化运营策略、提升用户体验。本文将全面介绍电商数据的获取方式&#xff0c;涵盖API接口、网络爬虫技术、第三方数据服…