400 要修,429 要等
一句话总结
写入一侧的拒绝有两种。400 要去修,429 要去等。把这两者用同一个代码处理的采集器,两种都会处理错。
为什么需要它
接入新服务的那天,采集器日志里涌出大量拒绝。负责人打开重试逻辑就下班了。第二天早上,拒绝依旧,而采集器的缓冲区已经满了,连别的服务的日志都被挤压了。
拒绝是 400。标签名里带有连字符,这样的行不管重发多少次,永远都会被拒绝。重试没有改善情况,只是把队列填满了。反过来,同一周里另一个服务出现的 429,重试才是正确答案,而那边却不重试,直接丢弃了。
工作原理
Loki 拒绝传入请求的原因大致有四种。
| 情形 | 代码 | 处理 |
|---|---|---|
| 标签名不符合语法 | 400 | 修改发送一侧 |
标签个数超过上限(max_label_names_per_series) |
400 | 减少标签 |
行太长(max_line_size) |
400 | 截断或拆分 |
超过每个流的速率(per_stream_rate_limit) |
429 | 退避后重试 |
标签名的规则与 Prometheus 相同——以字母或下划线开头,只能使用字母、数字和下划线。连字符不行,也不能以数字开头。想把 Kubernetes 标签原样搬过来,常常会撞上这堵墙,所以最好事先在采集器一侧放入改名的设置。
速率限制是以流为单位的。所以即使是同一个服务,如果标签组合有多个,每个组合都会各自获得限额。反过来,如果减少标签、合并流,流量就会集中到一个流上,出现 429。降低基数和避开速率限制,朝相反的方向拉扯——不了解这种张力,就会只修一边,却把另一边弄炸。
突发(burst)又给这幅图添了一层。per_stream_rate_limit_burst 是允许瞬间超过限额的量。所以平时平静、突然爆发的流量,开头一段时间能通过,之后才会收到 429。“一开始行,突然就不行了”这个症状,真身大多就是这个。
收到 429 时,正确的客户端要具备三样——指数退避、抖动、重试上限。没有抖动,多个采集器就会以同样的节奏再次涌来,一起撞上同一堵墙。没有上限,把 400 误当成 429 时,就会永远重试下去。
在现场相遇的样子
最常见的事故,就是上面的“对 400 重试”。办法是在采集器设置里用状态码明确重试条件,把 400 一类的送进隔离队列,让人去看。
第二是过长的行。一个堆栈跟踪超过上限,那个请求就会整个被拒绝,同一批里好好的行也一起掉了。所以要在采集器里先截断,或者把批设小,缩小受影响的范围。
第三是标签个数。把 Kubernetes 元数据全部变成标签,很容易超过上限。只留下需要的五六个,其余放在正文里,拒绝没有了,流的数量也减少了。
下一项实验要做什么
在 /config 里亲手读出服务器持有的限额,做成表格,实际发送错误的标签和过长的行,收到 400 及其正文。然后启动一个设置了较低速率限额的第二个 Loki,收到真正的 429,并用数字说明为什么开头几次因为突发而通过了。最后加上退避重试,做到一行不丢、全部存下。