我看到不少人在指责 Cloudflare 使用 Rust 的 unwrap() 不合理,在我看来,这种说法并不成立。问题的实质在于:当输入数据超出开发者的预期时,怎样进行处理。路径无非以下三种:
- 最糟糕的情况:使用了 C 这类 unsafe 语言,且缺乏必要的边界检查与防护,导致缓冲区溢出、内存被写脏,进而引发难以复现和排查的崩溃或未定义行为,最终可能需要花费大量时间才能定位根因。
- 继续运行但吞掉错误:在检测到异常数据后选择忽略或降级处理,保持进程继续运行。这种方式是否可行,高度依赖于业务对该数据的正确性和完整性的依赖程度,并非普适解。
- 显式上报并中止:在发现不符合预期的状态时,主动触发错误并终止进程(例如 Rust 中的 unwrap() 触发 panic,或其他语言中抛出未捕获异常),让问题以响亮的方式暴露出来,便于快速定位。
当系统遭遇此类异常时,可选策略其实就围绕这些模式,并不存在其他解法。因此,无论是 unwrap() 导致的 panic,还是其他语言中未捕获异常引发的崩溃,本质上都是进程终止这一结果,只是表现形式和可观测性不同。
(从系统架构层面,还可以实现配置自动回滚等辅助系统解决,但是这依然依赖于进程主动中止再去补救)
本次事件中,Cloudflare 真正暴露出的短板在于:对于这类关键服务上的 panic 级别错误,缺乏直接的告警与可观测能力。按理说,这种核心组件一旦发生 panic,应当立刻触发高优先级告警,并将完整调用栈和上下文第一时间推送到负责工程师面前,使其能够在数分钟内精准锁定问题代码路径。
然而,从事后披露的处理流程来看,他们在排查过程中先投入精力去确认是否遭遇攻击,再层层下钻才逐步接近真正根因。这说明在监控与告警体系上,对类似 panic 这种“程序自己已经明确喊出不正常”的信号,Cloudflare 并没有建立足够直接、可信、可操作的响应链路,而这才是这次事故中真正的设计缺失。