链路追踪eagleEye介绍

news/2024/11/30 13:30:16/

介绍

淘宝现在是一个由很多个应用集群组成的非常复杂的分布式系统。这些应用里面主要有处理用户请求的前端系统和有提供服务的后端系统等。这些应用之间一般有RPC调用和异步消息通讯两种手段,RPC 调用会产生一层调一层的嵌套,一个消息发布出来更会被多个应用消费,另外,应用还会访问分库分表的数据库、缓存、存储等后端,以及调用其他外部系统如支付、物流、机彩票等。
请试想一下,现在淘宝一个买家点击下单按钮所产生的网络请求到达淘宝服务器之后,就会触发淘宝内网数百次的网络调用。这些调用中有哪些出问题会影响这次交易,有哪些步骤会拖慢整个处理流程,双十一的交易高峰需要给应用集群分配多少台机器,这些都是需要考虑的。但是调用环境的复杂度,已经很难用人力去做准确的分析和评估了,这时候 EagleEye 就派上了用场。
EagleEye (鹰眼)通过收集和分析在不同的网络调用中间件上的日志埋点,可以得到同一次请求上的各个系统的调用链关系,有助于梳理应用的请求入口与服务的调用来源、依赖关系,同时,也对分析系统调用瓶颈、估算链路容量、快速定位异常有很大帮助。另外,业务方也可以在调用链上添加自己的业务埋点,使网络调用和实际业务内容得到关联。
异步场景:EagleEye基于ThreadLocal实现上下文的存储,所以当业务方使用同步的方式时对使用者透明,但是该方式无法支持异步线程的场景,所以在使用异步线程时需要手动传递上下文,当业务逻辑转移到异步线程时,需要先备份 EagleEye 的调用上下文到异步任务中,保证链路的正确性。
对异步场景的修改方案一(推荐)
提交异步线程前,需要做 EagleEye 上下文备份

Object ctx = EagleEye.getRpcContext(); // 从当前 ThreadLocal 备份
MyAsyncTask task = new MyAsyncTask();  // 这里的 MyAsyncTask 是一个业务自定义的 Runnable
task.setRpcContext(ctx); // 将 ctx 保存到 task 中
Future future = bizThreadPoolExecutor.submit(task); // 提交任务
// 后面继续执行其他逻辑,或者用 future.get() 等待任务的结果,都没有问题
// 如果 submit 多个 task,每个 task 都需要保存一份 ctx
真正执行任务时,还原 EagleEye 上下文
class MyAsyncTask implements Runnable {private Object ctx; // 用于存放之前保存的 EagleEye 上下文public void setRpcContext(Object ctx) { this.ctx = ctx; }public void run() {EagleEye.setRpcContext(ctx);  // 还原到 ThreadLocaltry {// 开始做异步逻辑,如调用 HSF、Notify、Tair 之类// ...} finally {EagleEye.clearRpcContext();  // 务必清理 ThreadLocal 的上下文,避免异步线程复用时出现上下文互串的问题}}
}

核心

TraceId

在复杂的分布式系统环境下,EagleEye是一个有广泛用途的调用分析和问题排查工具。与一般的调用信息埋点日志相比,EagleEye埋点的一个显著的不同点在于它的每条日志都有与每次请求关联的上下文ID,我们称为TraceId。通过TraceId,后期的日志处理时可以把一次前端请求在不同服务器记录的调用日志关联起来,重新组合成当时这个请求的调用链。因此,EagleEye不仅可以分析到应用之间的直接调用关系,还可以得到他们的间接调用关系、以及上下游的业务处理信息;对于调用链的底层系统,可以追溯到它的最上层请求来源以及中间经过的所有节点;对于调用链的上层入口,可以收集到它的整棵调用树,从而定位下游系统的处理瓶颈,当下游某个应用有异常发生时,能迅速定位到问题发生的位置。

rpcId

为了区别同一个调用链下多个网络调用的顺序和嵌套层次,EagleEye还需要传输和记录RpcId。
RpcId用0.X1.X2.X3…Xi来表示,Xi都是非负整数,根节点的RpcId固定从0开始,第一层网络调用的RpcId是0.X1,第二层的则为0.X1.X2,依次类推。例如,从根节点发出的调用的RpcId是0.1、0.2、0.3,RpcId是0.1的节点发出的RpcId则为0.1.1、0.1.2、0.1.3。
EagleEye 本身具备透明传输 TraceId、RpcId 的能力,这个能力通用可以用来传输业务的一些数据。例如,如果希望追加自己的业务信息到调用链上,应用可以通过 EagleEye 提供的 API 进行链路分组、链路打标等,EagleEye 会把这些信息一层层的透明往后端系统传递,而且信息也同样记录在每条调用日志里面。
EagleEye 数据透传的功能,在全链路压测、子帐号信息传递、二套环境项目隔离、强弱依赖检测、敏感操作的前端来源跟踪等场景都起到了不可缺少的重要作用。
用户数据(UserData)

EagleEye日志中的UserData字段为业务自定义的数据,该字段包含了RPC层的业务数据,该数据分为两种: 1.本地数据:记录在本地日志中; 2.链路数据:记录在本地日志中,并会跟随调用链传递至下游。
UserData是以"@“开头的字符串数据,可能包含多对业务数据,每对业务数据以KV格式记录,分隔符为不可见字符,两个KV之间的分隔符是0x12,K和V的分隔符是0x14,以K的格式区分本地数据还是链路数据,本地数据的K以”@"开头。 例子: K(本地)=,V=RPC; K(链路)=i,V=7325abff。其中|是EagleEye的日志字段分隔符,第一个@标识该字段为UserData,第二个@标识rpcName这个K是本地数据
|@@rpcName0x14RPC0x12i0x147325abff0x12
使用:
在业务代码中调用EagleEye-Core的API即可 本地数据:
EagleEye.attribute(“rpcName”, “RPC”);
链路数据:
EagleEye.putUserData(“i”, “7325abff”);

一般情况下,用户需要在客户端将TraceID、RpcID和UserData附在请求中,发送给服务端,服务端收到请求后将该信息放回EagleEye的上下文中。
客户端

RpcRequest createRpcRequest() {// 构造请求对象RpcRequest request = new RpcRequest();// 把 EagleEye 的当前上下文作为“附件”传送出去// 常见的 RPC 可以看看是否支持使用 header、attachment、attributes、properties 之类的手段传递request.addAttachment("EagleEye-TraceId", EagleEye.getTraceId());request.addAttachment("EagleEye-RpcId", EagleEye.getRpcId());request.addAttachment("EagleEye-UserData", EagleEye.exportUserData());return request;
}

服务端

// 从请求获取TraceId、RpcId、UserData,还原EagleEye调用链上下文
String traceId = request.getAttachment("EagleEye-TraceId");
String rpcId = request.getAttachment("EagleEye-RpcId");
String userData = request.getAttachment("EagleEye-UserData");
// 重新构建上下文
Map<String, String> context = new HashMap<String, String>();
context.put(EagleEye.TRACE_ID_KEY, traceId);
context.put(EagleEye.RPC_ID_KEY, rpcId);
context.put(EagleEye.USER_DATA_KEY, userData);
EagleEye.setRpcContext(ctx);

如何开启一次链路追踪:EagleEye.startTrace,这个方法会生成一个唯一的追踪标识,并将其存储在当前线程的上下文中,以便在当前线程中进行传递。同时,EagleEye.startTrace方法还会记录当前请求的开始时间,并将其存储在追踪上下文中,用于计算各个节点和服务的执行时间。
EagleEye.startTrace(traceId, traceName, 90); //标识一次trace调用的开始,traceName为用户设置,EagleEye在展示时会以该参数为分组条件(注意不要发散)
如果第一个参数为null,表示当前调用链没有父调用链,即当前调用链是根调用链,也就是整个调用链的起点。因为在分布式系统中,一个请求通常会经过多个进程和服务,每个服务都可能包含多个调用链,如果没有根调用链作为起点,那么就无法将这些调用链关联起来,从而无法准确地追踪和分析整个请求的调用链,影响了分布式系统的调试和性能分析。如果当前调用链有父调用链,则需要将第一个参数设置为父调用链的traceID,从而将当前调用链与父调用链关联起来,形成完整的调用链。

链路数据表

如下:
链路数据:
K 含义
i 入口标识符,最多8个十六进制字符
i2 口的别名标识符,最多8个十六进制字符(别名标识符)
s 链路签名,最多8个十六进制字符
r 入口签名,入口 IP 地址,在 TraceId 前缀不是 Java 入口 IP 的时候生成
t 线上全链路压测使用,值为 1 时表示这个链路是压测链路,使用了压测数据
tchain 线上全链路压测使用,记录压测的业务链路
u 在 tbsession 的埋点,由业务在前端传入的 UserId
w 前端染色埋点,由无线前端生成的ID
o 由业务在前端传入的用户子帐号信息
em 在 BUC Filter 的埋点,内部系统的操作员工号
ip 在 BUC Filter 的埋点,客户端IP
scm_project 二套环境项目标识,HSF、Notify 等都需要识别


http://www.ppmy.cn/news/423150.html

相关文章

Java中的数组和集合

文章目录 数组一维数组使用示例注意事项 多维数组使用示例注意事项 集合ListArrayListLinkedList SetHashSetTreeSet MapHashMapTreeMap 总结 数组 Java 中的数组是一种容器&#xff0c;可以用来存储一组相同类型的元素。数组可以是一维的&#xff0c;也可以是多维的。 一维数组…

Windows老电脑的系统哪个更好

请问一下各位大佬&#xff0c;Windows7 32位的系统和64位的哪个适合老电脑使用&#xff0c;大概是4年前的。

计算机专业装win几,低配电脑装win10还是win7系统比较合适

最近在群里碰到有个用户非常纠结&#xff0c;他不知道低配电脑装win10还是win7系统比较合适&#xff1f;经过小编了解完具体情况后&#xff0c;小编决定把这个问题整理成一篇文章来分享给大家关于安装win10系统还是win7系统的问题。 对于不懂电脑系统的用户来说&#xff0c;有些…

安装simo的服务器装什么系统好用,电脑装什么系统好?

电脑装什么系统好?xp/win7/win8/win8.1系统的优劣&#xff1f;2014-12-03 很多用户在安装电脑系统的时候不是道电脑装哪种系统更好用&#xff0c;xp/win7/win8/win8.1系统究竟哪个更适合你的电脑&#xff0c;其实要根据你的电脑的具体配置(主要是&#xff1a;CPU和内存)来判断…

买电脑装什么系统好?win7还是win10?

对于这个问题&#xff0c;镁客君还是比较有发言权的&#xff0c;因为我两台电脑一个win7一个win10&#xff0c;在此镁客君分以下几个方面来综合对比这两个系统的差别&#xff0c;供各位参考 第一&#xff0c;安装系统比较&#xff1a; 我们可以发现Win7的安装速度要比Win10的…

win10的计算机是哪个版本,Windows 10系统都有哪些版本?老旧电脑装哪个版本的win10好?...

原标题&#xff1a;Windows 10系统都有哪些版本&#xff1f;老旧电脑装哪个版本的win10好&#xff1f; 按照微软官方的公告&#xff0c;安装windows10系统需要满足以下的要求&#xff1a; 以上是微信官方对安装windows时系统所需要的硬件最低要求&#xff0c;如果你的设备硬件无…

老电脑可以安装win11系统吗

win11系统是目前大家比较关注的新的操作系统&#xff0c;对于电脑配置有一定的要求&#xff0c;但是还是有些使用老电脑的用户跃跃欲试&#xff0c;不知道老电脑可以安装win11吗&#xff1f;我们可以尝试通过u盘重装的方法来给老电脑安装win11。那么老电脑如何安装win11系统&am…

电脑小白新装win10系统,要装哪些必备软件?

相信这个问题会有很多人回答&#xff0c;而且每个人的答案都不同。 私人观点&#xff1a; 1、 360不是必备的&#xff0c;因为它是电脑速度第一杀手&#xff0c;装上就能让你电脑慢20% 正确的作法是&#xff0c;用的时候装上&#xff0c;清理一下垃圾&#xff0c;用完卸载掉…