Please enable Javascript to view the contents

全栈后端架构与运维难题:exhaustion(耗尽) 与 intermittence(间歇)

 ·  ☕ 5 分钟

28m00c

我是个后端软件 Troubleshooting(故障解决) 爱好者。更准确地说,对 troubleshooting 已经有点像上瘾般的,难以自制的嗜好。这有时候是件好事,但有时候带出很多工作和生活上的困扰。无论如何,性格决定命运,既然改变不了,不如好好利用。

从业 20 年 troubleshooting 过各种类型的生产故障。本文只想讨论两种类型的故障:

  • 资源耗尽(exhaustion)的故障
  • 间歇(intermittence) 出现的故障

题外话。这是一篇水文,最近生活上出现了很多 “故障”,忙于 troubleshooting 生活故障了(BTW,我真的非常讨厌做生活的 troubleshooting),所以写点水文。也算丰富一下自己的写作范围吧,毕竟越水的东西,生命周期越长,不像具体技术。

资源耗尽(exhaustion)

Design for Failure 是所有软件架构设计者都知道的 robustness 原则。而 资源耗尽(exhaustion) 是 Failure 的一种类型。资源耗尽(exhaustion) 本质上有两层识别过程:

  • 对资源依赖的全方位识别
  • 对依赖资源的可用情况、需求情况有深入的分析

就两行文字,听起来很容易。但实践起来,是个全方位考验技术积累的活。这话怎讲?现代软件的设计,最大好处是有很多面向开发者的抽象的概念、编程方法、API。但在开发者享受抽象带来的简洁的同时,也同时必须承受抽象 “欺骗” 性带来的失真,以及失真后对细节把握不足的风险。哈哈,这句话说得太抽象了,不像我的风格了。举个例子:

用 Java 写代码时,没有多少人会关心创建一条线程:

  • 需要什么操作系统资源
  • 每种资源需要多少

人们总是假设,要么这是无限的,要么是使用量不大,不需要考虑。而很多时候,这也的确是符合工程需求的假设。但对于高并发/负载的应用,情况就不同了。以基于 Java/JVM 的 Kafka 对 Linux mapping in the virtual address space(mmap) 的使用为例:

Linux 对每个进程的 mmap 数量有限制,vm.max_map_count 默认 65535 。在默认配置与大负载情况下,会出现这个情况

[14.236s][warning][os,thread] Attempt to deallocate stack guard pages failed (0x00007f61a50a0000-0x00007f61a50a4000).

Java HotSpot(TM) 64-Bit Server VM warning: INFO: os::commit_memory(0x00007f61a50a0000, 16384, 0) failed; error='Not enough space' (errno=12)

# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 12288 bytes for committing reserved memory.
# An error report file with more information is saved as:
# /opt/kafka/bin/hs_err_pid2423.log

热点资源识别

上面说了,资源耗尽(exhaustion) 本质上有两层识别过程:

  • 对资源依赖的全方位识别
  • 对依赖资源的可用情况、需求情况有深入的分析

对于上面例子,需要在架构师、开发、测试、运维这几个角色当中,有最少一个人去深入了解过这个 “middleware” 的实现细节:

  • 分析出资源依赖
  • 识别出需要重点关注的资源

并识别出需要重点关注的资源 这个是最难的。因为一个 middleware/library 不是我们自己实现的,其中依赖的资源类型很多,如果每个都去分析,工程上是不可行的。所以需要有这个能力:

  • 预估系统流量/负载
  • 系统流量/负载如何映射/转换到目标组件
  • 目标组件的流量/负载如何映射/转换到使用的资源类别中

说白了,就是 “热点资源识别” 能力。这个能力大概是这样建立起来的:

  • 对组件的理解
  • 对组件使用到资源的理解。

对组件的理解 可以用几个方法达到:

  • 组件的文档。主要是组件运维管理相关的,如: admin docs/Monitor docs/Troubleshooting FAQ
  • 源码。不可能全看,更多时候,是在故障发生后,客观分析故障原因和线索链时阅读
  • 基础资源知识的积累。这是个长期的过程,包括 OS/Hardware

间歇(Intermittent) 出现的故障

Troubleshooting(故障解决) 几大任务:

  1. 现场分析
  2. 故障可能原因(root cause)列举
  3. root cause 证据链分析与收集
  4. 故障重现实验
  5. 提出 解决(fix) / 规避(Workaround) / 缓解(mitigation) 故障 的方法
  6. 故障解决实验
  7. 生产解决灰度发布

对于 间歇(Intermittent) 出现的故障 上面的所有任务都变得困难。我曾经花了数月时间,才重现了一个生产上的 间歇(Intermittent) 出现的故障

Istio/Envoy TCP Proxy half-closed connection leak for 1 hour in some scenarios

对于这类型的故障,如果事发时没记录相关的 log/metrics ,就只能凭经验去有方向性地猜和证明了。

后话

AI/GPT/LLM 大热后,人们开始有这样的想法:

  1. 大脑中的知识的积累并不重要,因为都可以需要时从 AI 问答中提取
  2. 程序员的计算机基础知识不重要,因为 AI 可以为我们代劳思考

关于第 1 点,我是有保留的,原因是 AI 可以给我们问题的答案,但对具体单位、项目、个人的特殊性个性的感知,还因训练数据源未打通而不及人类。只要人自己掌握了知识,结合对现实的感知,把知识有机串联起来,人掌握的知识才能比 AI 按需提取有价值。

计算机世界的闭环:在 AI 未能写代码之前,计算机世界是不能形成闭环的,但现在可以了。如果你把计算机作为一个主体,可以这么反过来说:

在 AI 编程之前,计算机需要人工辅助编程去定义工作。计算机工作的意义,由程序员定义。而在 AI 编程之后,计算机自己就完成了编程的闭环,工作的意义,可以由计算机自己定义。这,可能是个哲学问题了。
而人类研发的角色将离开这个 内环,放到更外的环,更贴近人类抽象思维的环上。直到有一天,内环 出了故障,而人类知之甚少。好像这也没什么大不了。

想想,CPU 也有 microcode 而又有多少软件开发者有需要了解 microcode,和在 microcode 出现 bug 时,能 troubleshooting 出来呢?

吾生也有涯,而知也无涯 。以有涯随无涯,殆已!已而为知者,殆而已矣!为善无近名,为恶无近刑。缘督以为经,可以保身,可以全生,可以养亲,可以尽年。
—— 《庄子·养生主》

面对现代知识的无限,谁都不可能真 “全栈” 。 但这个栈要学到什么深度? 每个人都有不同答案,交给读者去思考了。

分享

Mark Zhu
作者
Mark Zhu
An old developer