CPU 佔用率 45% 的“黑洞”:一次 APISIX “黑盒”外掛的 C 級別效能診斷
本文覆盤了一次真實的客戶案例。其 APISIX 閘道器叢集遭遇了嚴重的 CPU 瓶頸,傳統 perf 工具在“黑盒”外掛面前束手無策。本文將詳細展示 OpenResty XRay 團隊如何使用動態追蹤技術,非侵入式地穿透 Lua VM,精確定位到 OpenSSL C 庫中一個佔 44.8% CPU 的 pkey_rsa_decrypt 函式,並揭示了其對系統併發能力的真實影響。
API 閘道器是現代微服務架構的“咽喉”,其效能和穩定性至關重要。
近日,我們的一個客戶——某大型電商平臺——聯絡我們,報告了一個棘手的生產問題:他們核心業務的 APISIX 閘道器叢集在高峰時段 CPU 佔用率持續觸頂 (100% Saturation),導致 P99 延遲(99%分位延遲)急劇抖動,這是一個高優先順序的緊急事件。
當 perf 遇到“黑盒”
客戶的 SRE 團隊迅速介入,並嘗試了以下手段:
top/htop: 結果只顯示openresty程序是 CPU 消耗的主力,沒有更多資訊。perf record: 客戶嘗試用perf record和火焰圖。在最好的情況下,perf 的火焰圖或許能顯示pkey_rsa_decrypt這樣的 C 函式在消耗 CPU,儘管這在 JIT 環境中經常被luajit_vm_dispatch等 VM 符號淹沒。
但這立刻帶來了第二個、也是更致命的問題:無法歸因,perf 無法將這個 C 函式呼叫與任何上游的 Lua 程式碼關聯起來。它得到的是一個“孤立的熱點”。我們不知道是哪個外掛、哪個 API 路由、哪行 Lua 程式碼觸發了這個昂貴的 C 呼叫。
這就是 perf 在混合語言棧 Lua+C 上的核心侷限:它丟失了上下文。 在一個執行著幾十個外掛的複雜閘道器上,一個‘孤立的’ C 函式熱點是無法指導最佳化的。
- APM 監控: 客戶的 APM 系統(基於
ngx.now和ngx.log)顯示,大部分耗時發生在access_by_lua階段。縮小了範圍,但依然太寬泛,access階段可能掛載了十幾個外掛。
團隊透過逐一排查,鎖定在一個用於會話驗證的自定義外掛 (cb-session-validation) 上。但問題在於,這個外掛是由第三方服務商提供的“黑盒”,客戶團隊沒有它的完整原始碼。
這是一個典型的“盲區”:
- 宏觀上,知道 CPU 爆了。
- 中觀上,知道是
access_by_lua階段。 - 微觀上,無法定位到是哪個具體函式導致了消耗。
從“取樣”到“全棧動態追蹤”
根據 OpenResty XRay 技術專家團隊判斷,既然 Lua 級別的 Profiler 和系統級的 perf 都無法提供答案,那麼瓶頸幾乎可以肯定發生在 Lua VM 與 Native C 庫的邊界上。
這是傳統取樣工具的“盲區”。perf 擅長分析 C/C++ 或核心,但是沒有辦法獲取 LuaJIT、V8、JVM 的應用語言級別的程式碼的呼叫棧,因此能夠獲取的資訊非常有限。開源的 APM 工具的使用門檻很高,只有少數開發者能夠熟練使用那些工具,不適合在生產環境部署。
我們建議客戶直接在出問題的生產環境中直接用 OpenResty XRay 進行一次“CT 掃描”。
OpenResty XRay 的核心區別在於它不依賴“取樣” (Sampling),而是使用“動態追蹤” (Dynamic Tracing)。它能非侵入式地實時重建從 NGINX 事件迴圈,到 LuaJIT VM,再到 C 庫乃至核心的完整呼叫棧,無需重啟或修改任何程式碼。
分析任務啟動後,證據清晰地指向了一個驚人的事實:一個名為 pkey_rsa_decrypt 的 C 函式,其 CPU 佔用時間高達 44.8%。
拿到證據鏈
這個 pkey_rsa_decrypt 函式顯然來自 OpenSSL 庫,用於執行 RSA 私鑰解密。但它是如何被呼叫的?
OpenResty XRay 提供的完整火焰圖和呼叫棧給出了以下證據鏈:
@access_by_lua(nginx.conf:310):2
http_access_phase@/usr/local/apisix/apisix/init.lua:721
run_plugin@/usr/local/apisix/apisix/plugin.lua:1154
phase_func@/opt/apisix/cb_plugins/apisix/plugins/cb-session-validation.lua:93 <-- 客戶的“黑盒”外掛
validate_session@/opt/apisix/cblualib/cbmodules/cb-session-validator.lua:283
verify_session@/opt/apisix/cblualib/cbmodules/cb-session-validator.lua:105
load_jwt@/opt/apisix/cblualib/resty/cb_jwt.lua:624
pcall
[builtin#pcall]
@/opt/apisix/cblualib/resty/cb_jwt.lua:250
...
C:pkey_rsa_decrypt [/usr/lib64/libcrypto.so.1.1.1k] <-- 44.8% CPU 瓶頸點
@/usr/src/debug/openssl-1.1.1k-12.el8_9.x86_64/crypto/rsa/rsa_pmeth.c:337
這條證據鏈的價值在於:
- 資訊透明化: 它提供了 perf 缺失的歸因上下文,明確地將 C 語言的熱點
pkey_rsa_decrypt追溯到了其 Lua 肇事者cb-session-validation.lua外掛的第 93 行。 - 連線了 Lua 與 C: 它完美地繪製了從 APISIX 外掛到 JWT 庫(Lua 程式碼),最後“刺入”
libcrypto.so(C 庫)的完整路徑。
在沒有原始碼、無需 GDB、不重啟服務的情況下,我們也知道了是哪個檔案、哪行程式碼觸發了問題,這才是工程師真正需要的、可立即採取行動的洞察。
從“發生了甚麼”到“為甚麼”
定位到 pkey_rsa_decrypt 只是第一步。工程師更關心的是:**為甚麼它會成為瓶頸?**我們基於 OpenResty XRay 的資料,向客戶提出了兩個層面的分析和最佳化假說:
假設 1:過度的加密(演算法問題)
validate_session 階段的非對稱解密如此耗時,我們首先懷疑是金鑰長度問題。如果客戶(或其第三方)使用了 4096-bit 甚至更長的 RSA 金鑰,其計算開銷是驚人的。
我們用 openssl speed 做了一個快速的基準測試,對比了 RSA 2048-bit 和 4096-bit 的效能:
| 演算法 | 簽名操作 (sign/s) | 驗證操作 (verify/s) |
|---|---|---|
| rsa 2048 bits | 1325.3 | 48404.7 |
| rsa 4096 bits | 211.5 | 13877.3 |
| 效能對比 | 2048 是 4096 的 6.2 倍 | 2048 是 4096 的 3.4 倍 |
資料顯示,RSA 2048 的驗證速度是 4096 的 3.4 倍以上。而在 2024 年,2048-bit 的金鑰在絕大多數場景下被認為是足夠安全的。使用 4096-bit 屬於“過度安全”,卻付出了 3-6 倍的效能代價。
假設 2:冗餘的計算(架構問題)
從呼叫棧看,這個昂貴的解密操作發生在每一個請求的“會話驗證”階段。
這很可能是一個架構缺陷。對於一個合法的、未過期的 Session/JWT,其解密結果(即使用者的身份資訊)應該是可以被快取的。
在會話的有效期內(例如 5 分鐘),完全沒有必要為同一個 token 在每一個請求上都重複執行數萬次昂貴的 RSA 解密。
這對系統併發能力意味著甚麼?
這個 44.8% 的 CPU 佔用,本質上是系統吞吐能力的“攔腰斬”。
這意味著閘道器叢集近一半的 CPU 週期,都被浪費在可被快取、可被最佳化的重複性 crypto 運算上。這直接導致了 CPU 提前觸頂,無法處理更多的併發請求,P99 延遲必然飆升。
覆盤總結
這個案例完美地暴露了常規可觀測性和 APM 工具的侷限性。常規監控手段,無論是 perf 還是 APM 之所以束手無策,是因為瓶頸發生在 VM 與 Native C 庫的邊界上,而 perf 只能得觸及 C 的呼叫棧,無法深入獲得 Lua 呼叫棧,缺乏最後一公里的洞察力。
如前所述,perf 或許 能發現 pkey_rsa_decrypt 是熱點。但在 APISIX 這樣的高併發、事件驅動的 JIT 環境中,這幾乎是一個無用的資訊。
問題在於“歸因鴻溝”。perf 無法跨越 LuaJIT VM 的邊界。它不知道這個 C 函式是由 VM 上的 哪段 Lua 程式碼 觸發的。在一個有幾十個外掛、每秒處理數萬請求的系統上,你無法知道是外掛 A 還是外掛 B 造成的。
也就是說:
- perf 看到了 C 函式的繁忙,但無法將它歸因到 Lua 請求。
- APM 看到了 Lua 請求的緩慢,但無法“看穿” C 語言的黑盒。
而 OpenResty XRay 的核心優勢在於全棧動態追蹤。它能重建完整的、跨越“Lua-land”和“C-land”的混合呼叫棧,精確地告訴你:“cb-session-validation.lua 檔案的第 93 行,透過一系列呼叫,最終執行了 pkey_rsa_decrypt,並消耗了 44.8% 的 CPU。”
對於 APISIX、Kong 這類基於 OpenResty 的高效能中介軟體,其效能最佳化的“深水區”往往就在 C 語言層面、FFI 邊界或系統呼叫上。要駕馭這種複雜系統,資深工程師需要一種能提供“新視角”的工具:它必須能夠全棧動態追蹤,將 Lua 和 C 的世界無縫連線起來。
在這次診斷中,OpenResty XRay 扮演了“CT 掃描器”的角色,它讓客戶的團隊停止了盲目猜測,轉而進行基於資料和證據的精確最佳化。我們給客戶的最佳化建議也非常明確:
- (治本)架構最佳化: 在
cb-session-validation.lua中引入lua-resty-lrucache,對解密結果進行程序內快取 (in-process cache)。 - (降級)演算法最佳化: 立即檢查 RSA 金鑰長度,若為 4096-bit,評估降級到 2048-bit 的可行性。
如果你的團隊也在維護複雜的 OpenResty/APISIX 系統,並且厭倦了在 perf 的結果中大海撈針,歡迎申請試用看看系統中的“效能黑洞”究竟是甚麼。
關於 OpenResty XRay
OpenResty XRay 是一款動態追蹤產品,它可以自動分析執行中的應用,以解決效能問題、行為問題和安全漏洞,並提供可行的建議。在底層實現上,OpenResty XRay 由我們的 Y 語言驅動,可以在不同環境下支援多種不同的執行時,如 Stap+、eBPF+、GDB 和 ODB。
關於作者
章亦春是開源 OpenResty® 專案創始人兼 OpenResty Inc. 公司 CEO 和創始人。
章亦春(Github ID: agentzh),生於中國江蘇,現定居美國灣區。他是中國早期開源技術和文化的倡導者和領軍人物,曾供職於多家國際知名的高科技企業,如 Cloudflare、雅虎、阿里巴巴, 是 “邊緣計算“、”動態追蹤 “和 “機器程式設計 “的先驅,擁有超過 22 年的程式設計及 16 年的開源經驗。作為擁有超過 4000 萬全球域名使用者的開源專案的領導者。他基於其 OpenResty® 開源專案打造的高科技企業 OpenResty Inc. 位於美國矽谷中心。其主打的兩個產品 OpenResty XRay(利用動態追蹤技術的非侵入式的故障剖析和排除工具)和 OpenResty Edge(最適合微服務和分散式流量的全能型閘道器軟體),廣受全球眾多上市及大型企業青睞。在 OpenResty 以外,章亦春為多個開源專案貢獻了累計超過百萬行程式碼,其中包括,Linux 核心、Nginx、LuaJIT、GDB、SystemTap、LLVM、Perl 等,並編寫過 60 多個開源軟體庫。
關注我們
如果您喜歡本文,歡迎關注我們 OpenResty Inc. 公司的部落格網站 。也歡迎掃碼關注我們的微信公眾號:
翻譯
我們提供了英文版原文和中譯版(本文)。我們也歡迎讀者提供其他語言的翻譯版本,只要是全文翻譯不帶省略,我們都將會考慮採用,非常感謝!


















