决定丢掉什么的那件事
一句话总结
取样不是减少成本的装置,而是决定看什么的设计。如果定错了,就没有实际需要的线路。
为什么需要这个?
每个请求都会产生数十个范围。如果每秒有1000个请求,一天就有数十亿个范围。如果全部存储的话,存储成本会超过应用程序成本。
但是如果随意留下1%,在出现障碍时,没有那个请求的痕迹的可能性是99%。所以采样不是从“要留下多少”入手,而是从“一定要留下什么”入手。
三种方式
| 方式 | 什么时候决定 | 优点 | 代价 |
|---|---|---|---|
| 头 | 请求开始时 | 便宜简单,传播容易 | 不能只选择慢请求 |
| 尾部 | 完成跟踪后 | 准确选择错误·慢速 | 要收集范围,太贵了 |
| 延迟限制 | 每秒N个上限 | 即使狂奔也不会增加费用 | 狂奔路段的样本会减少 |
实际组合一般是这样的。
- **在头部减少10~20%**的基本线条
- **错误是100%**强制(
traceparent打开的样本标志传播) - 只对重要的端点进行尾采样,以减少延迟。
采样决定需要传播。
这是在这里最容易出错的地方。采样必须以轨迹单位来确定, 如果每个服务单独设定的话,痕迹就会碎裂。
프런트(20%) → API(20%) → 결제(20%)
각자 정하면 세 서비스가 모두 남길 확률 = 0.2³ = 0.8%
→ 완전한 트레이스는 거의 안 남고, 조각난 스팬만 쌓인다
W3Ctraceparent头部的最后一个字节是样本标志。在前面一圈
做出决定后,后面只需要遵守那个决定。
traceparent: 00-4bf92f...36-00f067aa0ba902b7-01
↑ 01 = 샘플됨, 00 = 아님
OpenTelemetry SDK的ParentBased(root=TraceIdRatioBased(0.2))实现这个。
有父母的话,就按照父母的决定,没有父母的时候(是根的话)才掷20%的骰子。
TraceIdRatioBased只要使用就会在每个服务中单独滚动,出现上述0.8%的问题。
TraceIdRatioBased为什么不是随机,而是用trace ID的哈希来确定,原因就在这里。
有。相同的trace ID无论在哪个服务中计算都会得到相同的答案,所以设置
如果一样的话,决定就会自动一致。
100%留下错误的方法
“把错误都留下”这句话虽然容易,但在头采样中是不可能的。请求开始 因为不知道什么时候它会失败。有两种绕行路线。
**应用程序打开标志。**检测到错误时,强制记录当前范围 标记为目标。虽然无法恢复已经过去的父母范围,但下面还有剩余。
**使用尾部采样。**收藏家在看到整个痕迹后进行判断,所以很准确。 政策是这样的样子。
tail_sampling:
decision_wait: 10s
policies:
- name: errors # 오류는 전부
type: status_code
status_code: {status_codes: [ERROR]}
- name: slow # 2초 넘는 것은 전부
type: latency
latency: {threshold_ms: 2000}
- name: baseline # 나머지는 5%
type: probabilistic
probabilistic: {sampling_percentage: 5}
政策会被评价为OR——只要有一点正确就留下。
尾采样隐藏的成本
收藏家把整个痕迹都收集到内存中后进行判断。所以出现了两种情况。
- 内存:等待时间(通常5~30秒)×每秒轨迹数累积
- 路由:相同轨迹的跨度必须用相同的收集器。所以在收集器前面
trace_id设定标准路由平衡器。不设定的话,路径会碎裂,判断就会不正确。
如果不知道这两种情况,打开尾采样,则采集器会死于OOM,死去的过程中,整个跟踪会消失。
常见的误解
“提高采样比例会更准确” — 指标不应该与采样无关。请求数、错误率、延迟是指标,跟踪是“查看该请求在哪里花费了时间”的工具。如果通过跟踪计算比例,采样偏差就会被保留。
“只要留下错误就行”——不是错误,而是慢请求会提供更多信息。错误虽然也会在日志中留下,但正常响应却花了3秒的请求,如果没有跟踪,就无法解释。
在实际工作中真正重要的东西
采样决定必须在前面一个人下一次,然后传播到最后。如果中间服务根据自己的判断重新确定,则只剩下一半的轨迹。
traceparent的最后标志(01/00)承担了那个决定。自动计量会监控这个,但直接制作HTTP客户端的代码如果重新写头,那么从那个点开始决定就会被初始化。