当前位置: 首页 > news >正文

B站网关事故背后:OpenResty 与 Lua 的稳定性代价

注:本文在大模型辅助下完成。

一、一次看似普通的网关事故,为何会导致全站雪崩?

2021 年 7 月 13 日,Bilibili 发生了一次非常经典的网关事故[1]。

事故发生后,线上出现了非常诡异的现象:

  • 所有 OpenResty worker 进程 CPU 100%

  • 但请求几乎无法处理

  • 进程没有 crash

  • 没有 core dump

  • error log 中也没有明显异常
  • 重启无法恢复

  • 回滚代码也无法恢复

最终,整个站点出现大面积故障。

更令人意外的是:

最终定位出的根因,只是一个字符串 "0"。

也就是说,一个看起来几乎不可能引发灾难的数据类型问题,最终却导致:

  • 网关整体失效

  • 请求无法转发

  • 全站雪崩

这次事故后来由 OpenResty 官方团队通过 XRay 工具进行了深度分析[2],也成为 OpenResty 领域最经典的稳定性案例之一。

但真正值得关注的,其实并不是"为什么会出现一个低级 bug",而是:

为什么一个如此微小的问题,居然能够穿透整个网关基础设施,最终演变成全站级事故?

而要理解这个问题,就必须先理解:OpenResty 到底是什么。

二、什么是OpenResty?为什么它如此流行?

很多人会误以为:OpenResty 是一个独立的 API 网关。

实际上并不是。

OpenResty 的本质是"增强版Nginx"。它的核心思想是:以 Nginx 作为高性能网络框架,在 Nginx 内部嵌入 Lua 运行时,允许开发者在 Nginx 请求处理阶段执行 Lua 代码。

也就是说:

OpenResty = Nginx + Lua Runtime

在传统 Nginx 中,开发者主要依赖nginx.conf、rewrite 规则、upstream 配置来控制流量。但 Nginx 的问题是:配置能力强,但编程能力有限。很多复杂场景都很难实现:动态路由、动态鉴权、动态限流、动态灰度、动态 upstream、插件体系。

而 OpenResty 的出现,彻底改变了这一点。它允许开发者在 rewrite、access、balancer、filter 等阶段直接编写 Lua 逻辑。

于是,Nginx 从"配置驱动"变成了"代码驱动"。这也是 OpenResty 在互联网领域快速流行的重要原因。

后来,Kong、Apache APISIX 等知名 API 网关项目,也都选择了OpenResty 作为底层架构。

因为它确实非常灵活。它让网关第一次具备了动态化、插件化、可编程化的能力。

但与此同时,"灵活"本身,也开始成为稳定性问题的来源。这不是说灵活性是错的,而是说:灵活性与稳定性之间,始终存在架构权衡(trade-off

三、为什么OpenResty的技术路线会放大稳定性风险?

很多人讨论 OpenResty 时,首先关注的是性能、动态能力、插件生态。但对于网关而言,真正最重要的能力,其实是稳定性

因为网关不是普通业务系统。它是全站入口、流量枢纽、多业务共享基础设施、高并发长生命周期系统。它一旦出现问题,影响范围往往是全局性的

因此,网关最怕的,其实不是"功能不够",而是"不可预测"。而 OpenResty 的技术路线,恰恰在某些层面放大了这种"不可预测性"。

1. Lua动态语言的问题

Lua 是典型的动态弱类型语言。例如:

local weight = "0" if weight == 0 then ... end

这里 "0" 是字符串,0 是数值。在复杂系统中,这种问题非常容易被忽略。

更关键的是:Lua大量错误只能在运行时暴露,而无法在编译阶段发现。这意味着配置上线后才发现问题、某些边界流量才会触发问题、某些特殊数据才会暴露问题。

对于业务系统而言,这可能只是单请求失败、单实例异常。但对于网关,意味着一个运行时错误,可能直接影响整个流量入口

2. Lua"灵活性"本身就是风险来源

Lua 为了灵活性,引入了大量动态机制:table 动态扩展、metatable 元编程、monkey patch、coroutine、动态 require、共享状态。

这些机制在业务开发中很方便。但在网关系统中,灵活往往意味着不可预测

例如:某个 table 被意外修改、某个共享状态没有同步、某个 coroutine 没有退出、某个模块被热更新覆盖,都可能导致 CPU 飙升、worker 卡死、内存泄漏、请求阻塞。

而且,这类问题往往极难复现,因为它们依赖运行时状态、并发路径、流量模式、特定数据。

3. LuaJIT又进一步增加了复杂性

OpenResty 的高性能,很大程度来自 LuaJIT。LuaJIT 会进行动态热点编译、trace optimization、speculative optimization。

这意味着同一段 Lua 代码,测试环境正常、小流量正常,但高并发线上异常——因为JIT的行为依赖运行时路径。这会让很多问题难复现、难定位、难解释。

而对于网关而言,"无法稳定复现的问题",往往是最危险的问题

这里需要特别说明:LuaJIT 的JIT 编译器本身在此事故中并没有 bug。官方复盘明确指出,最初怀疑 JIT 问题是因为另一个业务团队的未告知操作产生了干扰。但 JIT 的动态优化特性,确实增加了问题定位的复杂度。

4.插件化体系进一步放大了风险

今天很多基于 OpenResty 的网关(如 Kong、Apache APISIX)都强调插件化。但插件化本身也意味着:多团队共享运行时、插件共享 worker、插件共享 Lua VM、插件运行在同一个网关进程内部。

于是,一个插件问题,就可能拖垮整个网关实例

这和传统业务系统(微服务隔离、进程隔离、容器隔离)的风险模型完全不同。在OpenResty 架构中,"业务逻辑""基础设施"之间,实际上没有真正隔离

四、回到B站事故:为什么一个"0"能导致整个网关崩溃?

理解了 OpenResty 的技术路线之后,再回头看 B站事故,就会发现:这并不是一次"偶然事故",而是系统性风险的一次具体暴露

直接原因:类型误用导致死循环

事故最终定位发现:一个字符串 "0" 被错误地当成数值 0 使用。而 lua-resty-balancer 在处理过程中,最终进入了异常逻辑路径。

具体来说,在 lua-resty-balancer 的 _gcd(最大公约数)函数中,代码期望传入的是数值类型。当传入字符串"0" 时,Lua 的取模运算"0" % 0 产生了 nan(非数值),导致无法进入 b == 0 的终止条件,从而陷入无限循环。

随后导致:无限递归/循环 → worker CPU 100% → 整个网关集群全部失效。

系统性原因:动态架构放大了单点缺陷

真正可怕的地方在于,这个问题:

  • 没有 crash

  • 没有 core dump

  • 没有明显 error

  • worker 进程仍然"活着"

只是不再处理请求

而对于网关来说,"活着但无法工作",往往比直接crash更危险。因为:

  • 健康检查可能无法及时发现(进程还在,端口还通)

  • 自动恢复机制可能无法触发

  • 流量还会持续进入故障节点

最终形成:排队 → 超时 → 重试风暴 → 雪崩扩散。

这也是为什么,网关系统最怕的并不是"明确失败",而是"不可预测的异常状态"

五、OpenResty最大的争议:把"业务逻辑"带进了"基础设施"

很多人认为 OpenResty 的问题只是"Lua 不够安全、动态语言不够稳定"。但实际上,更深层的问题,是"业务动态逻辑进入了基础设施"

OpenResty 最大的成功在于:它让业务团队可以快速扩展网关能力。但与此同时,它也让动态脚本、业务逻辑、运行时行为、热更新能力直接进入了 L7 网关、流量入口、核心基础设施。

于是,网关开始逐渐业务化、状态化、动态化、不可预测化。最终,基础设施不再是"稳定内核",而变成了"动态运行平台"

而对于基础设施来说,动态能力越强,稳定性复杂度通常也越高。这也是为什么,近年来越来越多的新一代基础设施开始强调:强类型、静态分析、内存安全、可验证性、沙箱隔离、可预测执行模型。

因为对于基础设施而言,"稳定、可预测、可控制"往往比"灵活"更重要

当然,这并不意味着 OpenResty 的技术路线是错误的。Kong、APISIX 等项目的成功证明,在需要快速迭代、灵活扩展的场景下,OpenResty的权衡是合理的。但 B站事故说明:当这种灵活性缺乏足够的防御机制时,一个字符串"0",就足以让整个大型网站的网关系统全面失效

结语

B站网关事故已经过去数年,但它留下的警示依然鲜活:

对于基础设施,"不可预测""不够灵活"更致命。

OpenResty 用 Lua 的动态性重新定义了网关的灵活性,但也让我们看到了动态语言在基础设施中的脆弱一面。这不是 Lua 的错,也不是 OpenResty 的错,而是我们在享受灵活性带来的便利时,往往低估了防御性编程和边界校验的必要性

一个字符串 "0",最终演变成全站雪崩。这个看似荒诞的因果链,恰恰揭示了分布式系统中最深刻的真理:

系统的稳定性,不取决于最强的一环,而取决于最脆弱的边界条件。

参考资料

[1] 2021.07.13 我们是这样崩的,哔哩哔哩技术,2022年7月

[2] OpenResty XRay 分析和解决 B 站重大线上事故,OpenResty软件,2022年7月

作者简介

章淼,博士,1994年进入清华大学计算机科学与技术系学习,2004年获得博士学位,2004年至2006年在清华大学留校任教,在清华期间曾参与中国第一代核心路由器的研制工作。2012年起在百度工作超过十年,聚焦云网络基础架构的研发工作,是BFE开源项目的发起人。在百度期间积极推动软件工程能力提升,曾担任百度代码规范委员会主席,202110月被授予百度代码规范委员会荣誉主席。2022年出版《代码的艺术:用工程思维驱动软件开发》。20234月起担任瑛菲网络CEO,聚焦研发面向云和大模型场景的现代化流量管理平台。

http://www.zskr.cn/news/1462167.html

相关文章:

  • 租赁企业AI整合倒计时:监管新规Q3生效前必须完成的6项合规性改造清单
  • 新手入门指南:在快马平台上从零开始构建你的第一个17图库网页
  • 2026上海浦东/闵行/宝山/徐汇瓷砖空鼓是什么原因?梅雨季翘边拱起真相解析 - 苏易修缮
  • 抖音视频下载器技术架构解析与高效应用指南
  • 2026年金属雕塑保养全攻略:让艺术之美历久弥新
  • 如何快速使用Layerdivider:AI智能分层工具提升创意工作效率的完整指南
  • 一、Agent 记忆分层设计实践
  • 网络安全和安防建设方案(doc文件)
  • 【仅剩47家获准试点】AI驱动的动态质押率系统:如何用联邦学习在不共享原始数据前提下提升抵押率容忍度22.6%?
  • trick
  • 别再手动转换了!CAPL脚本里整型数组与Hex字符串互转的通用函数库(附完整源码)
  • 遥感入门不求人:用Python+ENVI 5.3快速识别植被、水体与裸土(附光谱曲线对比图)
  • 2026苏州吴中/吴江/昆山瓷砖空鼓需要全部砸掉吗?本地专业答案 - 苏易修缮
  • 2026年泰安装修设计公司推荐榜:尚城装饰中高端家装/高端别墅设计/整装施工/全屋定制/轻奢品质家装/宋式美学与新中式极简风家居推荐 - 品牌企业推荐师(官方)
  • 我用 AI 模拟字节跳动三面,系统设计轮答到一半面试官沉默了
  • 【v 2.7.5 新版】爆火 Open Claw 完整部署教程(含安装包)
  • 2026苏州全城免砸砖修瓷砖空鼓靠谱吗?本地实测真实效果 - 苏易修缮
  • 智能担保不是加AI,而是重定义风险契约——基于27家金融机构实测数据的5维评估模型
  • PCB外层及高端制程收放板方案:效率与防护的分段配置
  • 技术赋能:AKShare如何重构Python金融数据获取体验
  • 从零开始电路设计:光控夜灯实战与创客电子入门
  • 烟台外墙保温水包砂技术全解析 本土品牌实测案例复盘 - 奔跑123
  • 服务器上百台,SSH逐台装监控到猴年马月?我用Ansible三分钟全部搞定
  • 广东geo优化服务商广东谋根文化DeepSeek 大模型深度评测与实战指南
  • PDF Arranger:零基础也能上手的PDF页面管理神器,像搭积木一样玩转PDF!
  • 第三阶段Day01【Linux快照、目录结构、基础命令、命令帮助手册】
  • 明星最常穿的F2国风潮鞋清单~
  • 如何高效部署微信视频号实时弹幕监控系统:完整技术方案
  • 【AI担保融合实战指南】:20年风控专家亲授3大落地场景、5步集成法与避坑清单
  • Oracle的CURRENT REDO丢失,数据丢失风险分析