如何实现链路追踪(使用篇链路追踪)
我们先了解下分析对象和聚合维度的具体概念,再介绍实时分析与监控告警的具体用法。
作者:涯海
在前面文章里面,我们介绍了单链路的筛选与轨迹回溯,是从单次请求的视角来分析问题,类似查询某个快递订单的物流轨迹。但单次请求无法直观反映应用或接口整体服务状态,经常会由于网络抖动、宿主机 GC 等原因出现偶发性、不可控的随机离群点。当一个问题发生时,应用负责人或稳定性负责人需要首先判断问题的实际影响面,从而决定下一步应急处理动作。因此,我们需要综合一段时间内所有链路进行统计分析,这就好比我们评估某个物流中转站点效率是否合理,不能只看某一个订单,而要看一段时间内所有订单平均中转时间与出错率。
统计分析是我们观察、应用分布式链路追踪技术的重要手段。我们既可以根据不同场景要求进行实时的后聚合分析,也可以将常用的分析语句固化成规则生成预聚合指标,实现常态化监控与告警。相对于链路多维筛选,统计分析需要明确分析对象与聚合维度。其中,分析对象决定了我们对哪些指标进行聚合操作,比如请求量、耗时或错误率。而聚合维度决定了我们对哪些特征进行统计对比,比如不同应用、接口、IP、用户类型的统计量对比。接下来,我们先了解下分析对象和聚合维度的具体概念,再介绍实时分析与监控告警的具体用法。
01 分析对象分析对象,又称为度量值(Measure),决定了我们对哪些指标进行聚合操作。常见的度量值包括“黄金三指标”——请求量、错误和耗时。除此之外,消息延迟、缓存命中率或自定义度量值也是高频应用的分析对象,我们来逐一了解下。
(一)请求量请求量可以说是我们最熟悉的度量值之一。这个接口被调用了多少次?这一分钟的请求量与上一分钟相比有什么变化,与前一天相比有什么变化?这些问题都是我们在日常运维过程中最熟悉的对话。
请求量通常按照一个固定的时间间隔进行统计,比如按秒统计的请求量通常称之为 QPS(Queries Per Second),有些场景也会称之为 TPS(Transactions Per Second)。两者有一些细微差别,但含义基本相同,经常被混用。我们可以使用 QPS 来衡量系统的单位时间吞吐能力,用以指导系统资源的分配调度;或者观测用户行为的变化,判断系统是否出现异常。
如下图所示,创建订单接口每天上午 10 点和 12 点的请求量都会有一个周期性的突增,这种情况大概率是整点促销活动的正常表现,我们在做资源容量评估时需要参考整点的峰值请求量,而不是系统平均请求量,否则每当流量突增时系统可用性就可能大幅下降,影响用户体验。
下面第二张图的创建订单接口在 10 月 8 号凌晨 00:39 分有一个非常明显的下跌,并且前一天的曲线是比较平滑的,这种现象大概率是接口异常导致的,已经影响了用户的下单体验,需要深入排查定位原因。
请求 QPS 的变化趋势反映了系统吞吐能力的变化,是请求量最常用的一种方式。但在某些离线计算场景,对短时间内的吞吐变化不敏感,更需要一个比较大的时间间隔内的吞吐总量统计,比如一天或一周的请求处理总量。以便灵活分配离线计算资源。
错误每一次链路请求都会对应着一个状态:成功,或失败。一次失败的请求通常称之为错误请求。单条错误请求可能由于各种各样的偶发性原因不予关注。但是当错误数累积到一定程度,超过一定阈值时,就必须要进行处理,否则会引发大面积的系统故障。
错误指标除了像请求量一样,分为错误 QPS 和错误总量之外,还有一种比较特殊的统计方式,就是错误率。错误率是指在单位时间间隔内错误数占请求总数的比例。比如 A 接口一分钟内被调用了 10000 次,其中有 120 次是错误调用,那么 A 接口这一分钟的错误比率就是 120 / 10000 = 0.012,也就是 1.2%。
错误率是衡量系统健康程度的关键指标,针对它的健康阈值设置不会受请求量的周期性变化影响。比如下单接口的请求量在白天达到峰值的 10000 QPS,在夜间的谷值只有 100 QPS,全天的请求量变化范围在 100 ~ 10000 QPS 之间。相应的错误量变化范围在 0.2 ~ 20 QPS 之间,而错误率基本固定在 0.2% 左右。无论是使用固定阈值或同环比的方式,错误数都很难精确反映系统实际的健康程度,而错误率使用起来就简单、有效的多。比如设置错误率超过 1% 时就发送告警短信,超过 5% 就电话通知稳定性负责人立即排查。
(二)耗时
耗时也是我们非常熟悉的度量值之一,简单地说就是这次请求的处理花费了多长时间。但是不同于请求量。对耗时次数或总量的统计不具备实用价值,最常用的耗时统计方式是平均耗时。比如 10000次调用的耗时可能各不相同,将这些耗时相加再除以 10000 就得到了单次请求的平均耗时,它可以直观的反映当前系统的响应速度或用户体验。
不过,平均耗时有一个致命的缺陷,就是容易被异常请求的离散值干扰,比如 100 次请求里有 99 次请求耗时都是 10ms,但是有一次异常请求的耗时长达1分钟,最终平均下来的耗时就变成(60000 10*99)/100 = 609.9ms。这显然无法反映系统的真实表现。因此,除了平均耗时,我们还经常使用耗时分位数和耗时分桶这两种统计方式来表达系统的响应情况。
耗时分位数分位数,也叫做分位点,是指将一个随机变量的概率分布范围划分为几个等份的数值点,例如中位数(即二分位数)可以将样本数据分为两个部分,一部分的数值都大于中位数,另一部分都小于中位数。相对于平均值,中位数可以有效的排除样本值的随机扰动。
举个例子,你们公司每个同事的薪资收入可能各不相同,如果财务负责人要统计公司的中间薪资水平有两种方式,一种就是把所有人的薪资加在一起再除以人数,这样就得到了平均薪资;还有一种是将薪资从高到低排序,选取排在中间的那个人的薪资作为参考值,也就是薪资中位数。这两种做法的效果对比如下图所示:
分位数被广泛应用于日常生活的各个领域,比如教育领域的成绩排布就大量使用了分位数的概念,大家最熟悉的应该就是高考录取分数线。假如某大学在 A 省招收 100 人,而该省有 1 万人报考该大学,那么该大学的录取分数线就是所有报考学生成绩的 99 分位数,也就是排名前 1% 的同学可以被录取。无论该省的高考试题是偏难还是偏简单,都能准确录取到预定的招生人数。
将分位数应用在 IT 领域的耗时指标上,可以准确的反映接口服务的响应速度,比如 99分位数可以反映耗时最高的前 1% 接口请求的处理时间。对于这部分请求来说服务的响应速度可能已经达到了一个无法忍受的程度,例如 30 秒钟。相对于平均耗时,耗时 99 分位数额外反映了 3 个重要的信息:
- 有 1% 的服务请求可能正在忍受一个超长的响应速度,而它影响到的用户是远大于 1% 的比例。因为一次终端用户请求会直接或间接的调用多个节点服务,只要任意一次变慢,就会拖慢整体的终端体验。另外,一个用户可能会执行多次操作,只要有一次操作变慢,就会影响整体的产品体验。
- 耗时 99 分位数是对应用性能瓶颈的提前预警。当 99 分位数超出可用性阈值时,反映了系统服务能力已经达到了某种瓶颈,如果不加处理,当流量继续增长时,超时请求影响的用户比例将会不断扩大。虽然你现在处理的只是这 1% 的慢请求,但实际上是提前优化了未来 5%、10%,甚至更高比例的慢请求。
- 什么样的用户请求更可能触发 99 分位数的异常?根据经验表明,往往是那些数据体量大,查询条件复杂的“高端”用户更容易触发慢查询。同时,这部分用户通常是影响产品营收和口碑的高价值用户,绝不能置若罔闻,而要优先响应解决。
duration>3s AND serviceName="orderCenter" | SELECT SUM(request_count) AS total_count, spanName GROUP BY spanName ORDER BY total_count DESC LIMIT 10
除了 99 分位数,常用的耗时分位数还包括 99.9、95、90、50 分位数,可以根据应用接口的重要性和服务质量承诺(SLA)选择适当的分位数进行监控和预警。当一条时间序列上的分位数连在一起就形成了一条“分位线”,可用于观察耗时是否存在异常的变化趋势,如下图所示:
耗时直方图
耗时分位数和平均值将接口响应速度抽象成了有限的几个数值,比较适合监控和告警。但是,如果要做深度的分析,识别所有请求的耗时分布情况,那就没有比直方图更适合的统计方式了。
直方图大家可能不是很熟悉,平时接触的也比较少。它的横坐标代表请求耗时,纵坐标代表请求次数,并且横/纵坐标值通常都是非等分的,因为耗时与次数的分布通常是不均衡的,使用非等分坐标轴更容易观测重要且低频的慢请求分布,而等分坐标轴很容易将低频值忽略掉。如下图所示,我们可以直观的发现不同耗时范围内的请求次数分布:耗时在 100ms 左右的请求次数最多,超过了 10000 次;耗时在 5-10s 范围内次数也不少,接近 1000 次,而超过 30s 以上的请求也有接近 10 次。
直方图可以与分位数结合使用,每一个耗时分位数都会落在直方图具体的某个区间内,如下图所示 P99 分位数落在了 3s 的区间。这样,我们不仅能够快速发现最慢的 1% 请求耗时阈值是3s,还能进一步区分这 1% 最慢的请求在 3-5s,5-7s,7-10s,10s 以上的具体分布数量。同样的 P99 分位数(3s),慢请求全部集中在 3-5s 区间,和全部集中在 10s 以上区间所反映的问题严重程度,以及问题背后的原因可能是完全不同的。
通过对比不同时段的直方图分布,我们可以精准发现每一个耗时区间的变化情况。如果你的业务是面向终端用户,每一个长尾请求都代表着一次糟糕的用户体验,那你应该重点关注耗时区间最高的那部分变化,比如 P99 分位数所在的区间;如果你的系统是负责图形图像处理,更加看重单位时间内的吞吐率,不那么在意长尾耗时,那你应该优先关注大部分请求的耗时变化,比如 P90 或 P50 所在区间的分布变化。
直方图能够为我们分析耗时问题提供更丰富的细节,在后续章节的实践案例中我们将做进一步的介绍。
(三)其他度量值请求量、错误和耗时又被称为“黄金三指标”,可以应用于绝大部分类型的链路请求,如 HTTP,RPC,DB等。除此之外,一些特殊的请求类型,具备独特的场景特性,需要一些新的度量值来表达其语义,例如缓存命中率、消息时延、任务调度时延等。这一类度量值的通用性不高,但是可以恰当地描述所属类型的关键特征。下面我们以缓存命中率为例,了解下它的典型用法。
缓存命中率小玉负责的订单中心会调用存储在 Redis 缓存中的商品详情,只有查询缓存未命中时才会去请求数据库。有一个问题一直苦恼着小玉,就是每次促销活动刚开始的时候就会出现访问量激增又下降再缓慢回升,伴随耗时大幅抖动的现象,而缓存和数据库的请求量也会相对应的抖动变化,如下图所示:
我们可以看到缓存请求量的变化是与创建订单接口大致相同的,而数据库的请求量有一个比较大幅的增长。可以初步判断是由于促销活动初期出现了大量缓存未命中,从而调用数据库导致的创建订单接口耗时异常,因为查询数据库的耗时开销要远大于缓存。那么,缓存未命中的原因又是什么呢?主要有两种常见原因,一种是查询了大量冷数据导致的缓存命中率下降,另一种是查询量激增导致缓存连接被打满,超过其服务提供能力。两种原因的具体表现可以结合缓存命中率指标进一步区分,如下图所示。
为了减少冷数据对促销活动体验的影响,可以提前进行缓存预热提高命中率;而连接打满的问题可以提前调整客户端或服务端的缓存连接池最大连接数限制,或者提前扩容。缓存命中率下降的严重后果会导致大量请求击穿数据库,最终导致整体服务不可用。因此,在生产环境中建议对缓存命中率设置告警,提前发现风险。
(四)自定义度量值除了分布式链路追踪框架默认生成的通用度量值外,我们还可以将自定义度量值添加到 Attributes 对象中,再对其执行统计、分析和告警等操作。这些自定义度量值可以很好的拓展分布式链路追踪在业务域的应用。比如,我们将每笔订单的金额添加至 Attributes 中,类似 attributes.price = 129.0 。接下来,按照接口维度聚合订单金额,就可以看到不同接口的关联收入,并给出相应的漏斗分析图。帮助业务同学分析哪一步行为影响了用户的最终支付,造成了潜在的营收损失,如下图所示。
02 聚合维度
分析对象决定了我们要对哪些指标进行操作,而聚合维度决定了对该指标从多少个切面进行统计分析。通过对不同切面进行展开和对比,我们能够发现这些指标值为什么会发生这样或那样的一些变化。例如某个接口一段时间内的平均耗时为 3s,但是分布在两个不同的版本,其中 v1 版本的平均耗时只有 1s,而 v2 版本的平均耗时却高达 5s。此时,我们可以将问题明确的聚焦在 v2 版本上,观察 v2 版本相对于 v1 版本有哪些不同,进而定位耗时高的原因,如下图所示。
如果说分析对象回答了“是什么”的问题,那么聚合维度就回答了“为什么”的问题。选择一个恰当的聚合维度进行展开,可以帮忙我们有效的分析异常发生的特征分布,例如版本、IP、用户类型、流量类型等等。如果单个维度不足以定位问题,就需要进行多个维度的聚合分析,比如查看特定应用特定版本在不同用户类型的接口耗时变化。
与分析对象类似,常用的聚合维度可以分为链路框架自动生成的基础特征维度,以及用户自定义标签维度这两类,如下所示:
(一)基础特征与自定义标签结合使用
- 链路基础特征聚合:在链路特征筛选的章节我们介绍过链路基础特征主要是指调用链本身所具备的一些基础信息,比如接口名称,所属应用,节点IP、请求状态等等。无论是何种编程语言、何种埋点框架,这些基础特征都是由链路埋点框架自行生成的,也是最常用的聚合分析维度。目前主流的 Tracing 或 APM 产品都会提供预置的应用服务维度聚合指标,例如 Jaeger、Zipkin、Skywalking 等开源实现还会提供对应的监控大盘。
- 用户自定义标签聚合:除了链路基础特征以外,用户如果想要扩展业务属性,就可以通过链路 SDK 向 Attributes 里添加自定义标签,例如流量类型、用户类型、业务领域、商品类目、销售渠道等等。自定义标签提供了一种从业务视角分析流量问题的手段,灵活的运用自定义标签可以更好的发挥分布式链路追踪的数据价值。不过,由于自定义标签具有一定的埋点改造和运维成本,目前在生产环境还没有被大规模应用起来,需要 Tracing 产品提供更加灵活、成本更低的打标方案,例如白屏化动态打标,我们在后续章节再进行详细介绍。
小玉作为订单中心的应用负责人,对于核心接口的版本更新一直非常谨慎,按照惯例,她会先在预发环境进行灰度用户引流,对比新老版本的差异,确认无误后再发布至生产环境。此时,小玉会同时对应用、接口、环境、IP等多个基础特征进行聚合,再结合自定义的版本标签对比流量状态变化,如下图所示,v1.1 新版本的接口耗时大幅上升,错误率也远高于 v1.0 版本,应该立即停止发布,回滚版本,避免影响线上用户体验。
在这个案例中,由于 v1.1 版本的灰度流量要远小于 v1.0 版本,如果没有按照版本维度进行聚合比对,新版本的异常问题就会被整体流量平均稀释掉,难以被提前发现。只能等到发布比例增加到足够大的程度,对线上用户造成更加严重的影响后,才可能被定位。
(二)注意事项
不是所有的聚合维度都是有意义的,一个有效的聚合维度必须具备一个前提,就是它的值分布在有限的、肉眼可观测的数据集,而不能无限发散。比如一个日活上亿的 APP 应用,不应该直接将访问用户的 UserId 作为聚合维度,因为聚合后的上亿条曲线完全无法观测,不具备监控和告警价值。相对应的,我们可以选择用户类型作为聚合维度,区分游客、普通会员、白金会员、钻石会员等不同用户类型的访问情况。
还有一种情况是,聚合维度部分发散,比如 URL 里面有部分字段包含了时间戳,UID 等变量信息。此时,我们需要对 URL 做模糊化处理,通过收敛算法将不断变化的字段收敛成星号 *,保留不变的协议、域名、路径等,收敛前后的效果如下图所示。
剩余60%,完整内容请点击下方链接查看:
https://developer.aliyun.com/article/1190427?utm_content=g_1000370418
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
,
免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com