ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

高并发系统设计——分布式Trace技术选型

2022-02-02 23:58:34  阅读:265  来源: 互联网

标签:调用 服务 请求 Trace 选型 traceId spanId 日志 分布式


摘要

你的垂直电商系统在引入 RPC 框架,和注册中心之后已经完成基本的服务化拆分了,系统架构也有了改变:

现在,你的系统运行平稳,老板很高兴,你也安心了很多。而且你认为,在经过了服务化拆分之后,服务的可扩展性增强了很多,可以通过横向扩展服务节点的方式,进行平滑地扩容了,对于应对峰值流量也更有信心了。但是这时出现了问题:你通过监控发现,系统的核心下单接口在晚高峰的时候,会有少量的慢请求,用户也投诉在 APP 上下单时,等待的时间比较长。而下单的过程可能会调用多个 RPC 服务,或者使用多个资源,一时之间,你很难快速判断,究竟是哪个服务或者资源出了问题,从而导致整体流程变慢,于是,你和你的团队开始想办法如何排查这个问题。

一、分布式链路追踪技术背景

因为在分布式环境下,请求要在多个服务之间调用,所以对于慢请求问题的排查会更困难。先看看在一体化架构中,是如何排查这个慢请求的问题的。最简单的思路是:打印下单操作的每一个步骤的耗时情况,然后通过比较这些耗时的数据,找到延迟最高的一步,然后再来看看这个步骤要如何的优化。如果有必要的话,你还需要针对步骤中的子步骤,再增加日志来继续排查,

虽然这个方式比较简单,但你可能很快就会遇到问题:由于同时会有多个下单请求并行处理,所以,这些下单请求的每个步骤的耗时日志,是相互穿插打印的。你无法知道这些日志,哪些是来自于同一个请求,也就不能很直观地看到,某一次请求耗时最多的步骤是哪一步了。那么,你要如何把单次请求,每个步骤的耗时情况串起来呢?

一个简单的思路是:给同一个请求的每一行日志,增加一个相同的标记。这样,只要拿到这个标记就可以查询到这个请求链路上,所有步骤的耗时了,我们把这个标记叫做 requestId,我们可以在程序的入口处生成一个 requestId,然后把它放在线程的上下文中,这样就可以在需要时,随时从线程上下文中获取到 requestId 了。有了 requestId,你就可以清晰地了解一个调用链路上的耗时分布情况了。于是,你给你的代码增加了大量的日志,来排查下单操作缓慢的问题。很快, 你发现是某一个数据库查询慢了才导致了下单缓慢,然后你优化了数据库索引,问题最终得到了解决。用户反馈某些商品业务打开缓慢;商城首页打开缓慢。你开始焦头烂额地给代码中增加耗时日志,而这时你意识到,每次排查一个接口就需要增加日志、重启服务,这并不是一个好的办法,于是你开始思考解决的方案。

在一体化架构中,单次请求的所有的耗时日志,都被记录在一台服务器上,而在微服务的场景下,单次请求可能跨越多个 RPC 服务,这就造成了,单次的请求的日志会分布在多个服务器上。当然,你也可以通过 requestId 将多个服务器上的日志串起来,但是仅仅依靠 requestId 很难表达清楚服务之间的调用关系,所以从日志中,就无法了解服务之间是谁在调用谁。因此,我们采用 traceId + spanId 这两个数据维度来记录服务之间的调用关系(这里 traceId 就是 requestId),也就是使用 traceId 串起单次请求,用 spanId 记录每一次 RPC 调用。

说起来可能比较抽象,我给你举一个具体的例子。

比如,你的请求从用户端过来,先到达 A 服务,A 服务会分别调用 B 和 C 服务,B 服务又会调用 D 和 E 服务:

我来给你讲讲图中的内容:

  • 用户到 A 服务之后会初始化一个 traceId 为 100,spanId 为 1;
  • A 服务调用 B 服务时,traceId 不变,而 spanId 用 1.1 标识,代表上一级的 spanId 是 1,这一级的调用次序是 1;
  • A 调用 C 服务时,traceId 依然不变,spanId 则变为了 1.2,代表上一级的 spanId 还是 1,而调用次序则变成了 2,以此类推。

通过这种方式,我们可以在日志中,清晰地看出服务的调用关系是如何的,方便在后续计算中调整日志顺序,打印出完整的调用链路。

那么 spanId 是何时生成的,又是如何传递的呢?这部分内容可以算作一个延伸点,能够帮你了解分布式 trace 中间件的实现原理。

  • 首先,A 服务在发起 RPC 请求服务 B 前,先从线程上下文中获取当前的 traceId 和 spanId,然后,依据上面的逻辑生成本次 RPC 调用的 spanId,再将 spanId 和 traceId 序列化后,装配到请求体中,发送给服务方 B。
  • 服务方 B 获取请求后,从请求体中反序列化出 spanId 和 traceId,同时设置到线程上下文中,以便给下次 RPC 调用使用。在服务 B 调用完成返回响应前,计算出服务 B 的执行时间发送给消息队列。
  • 当然,在服务 B 中,你依然可以使用切面编程的方式,得到所有调用的数据库、缓存、HTTP 服务的响应时间,只是在发送给消息队列的时候,要加上当前线程上下文中的 spanId 和 traceId。

这样,无论是数据库等资源的响应时间,还是 RPC 服务的响应时间就都汇总到了消息队列中,在经过一些处理之后,最终被写入到 Elasticsearch 中以便给开发和运维同学查询使用。

而在这里,你大概率会遇到的问题还是性能的问题,也就是因为引入了分布式追踪中间件,导致对于磁盘 I/O 和网络 I/O 的影响,**而我给你的“避坑”指南就是:**如果你是自研的分布式 trace 中间件,那么一定要提供一个开关,方便在线上随时将日志打印关闭;如果使用开源的组件,可以开始设置一个较低的日志采样率,观察系统性能情况再调整到一个合适的数值。

二、分布式链路追踪技术的技术原理

三、分布式链路追踪技术的技术选型

博文参考

zipkinhttps://zipkin.io/

Jaegerwww.jaegertracing.io/

Pinpointhttps://github.com/pinpoint-apm/pinpoint

SkyWalkinghttp://skywalking.apache.org/

分布式链路追踪技术对比 | sharedCode

10张图带你入门分布式链路追踪系统原理 - 知乎

标签:调用,服务,请求,Trace,选型,traceId,spanId,日志,分布式
来源: https://blog.csdn.net/weixin_41605937/article/details/122773163

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有