本文覆盤了一次真實的客戶案例。其 APISIX 閘道器叢集遭遇了嚴重的 CPU 瓶頸,傳統 perf 工具在“黑盒”外掛面前束手無策。本文將詳細展示 OpenResty XRay 團隊如何使用動態追蹤技術,非侵入式地穿透 Lua VM,精確定位到 OpenSSL C 庫中一個佔 44.8% CPU 的 pkey_rsa_decrypt 函式,並揭示了其對系統併發能力的真實影響。

API 閘道器是現代微服務架構的“咽喉”,其效能和穩定性至關重要。

近日,我們的一個客戶——某大型電商平臺——聯絡我們,報告了一個棘手的生產問題:他們核心業務的 APISIX 閘道器叢集在高峰時段 CPU 佔用率持續觸頂 (100% Saturation),導致 P99 延遲(99%分位延遲)急劇抖動,這是一個高優先順序的緊急事件。

當 perf 遇到“黑盒”

客戶的 SRE 團隊迅速介入,並嘗試了以下手段:

  1. top / htop 結果只顯示 openresty 程序是 CPU 消耗的主力,沒有更多資訊。
  2. 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 函式熱點是無法指導最佳化的。

  1. APM 監控: 客戶的 APM 系統(基於 ngx.nowngx.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

這條證據鏈的價值在於:

  1. 資訊透明化: 它提供了 perf 缺失的歸因上下文,明確地將 C 語言的熱點 pkey_rsa_decrypt 追溯到了其 Lua 肇事者 cb-session-validation.lua 外掛的第 93 行。
  2. 連線了 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 bits1325.348404.7
rsa 4096 bits211.513877.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 掃描器”的角色,它讓客戶的團隊停止了盲目猜測,轉而進行基於資料和證據的精確最佳化。我們給客戶的最佳化建議也非常明確:

  1. (治本)架構最佳化:cb-session-validation.lua 中引入 lua-resty-lrucache,對解密結果進行程序內快取 (in-process cache)。
  2. (降級)演算法最佳化: 立即檢查 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、LuaJITGDBSystemTapLLVM、Perl 等,並編寫過 60 多個開源軟體庫。

關注我們

如果您喜歡本文,歡迎關注我們 OpenResty Inc. 公司的部落格網站 。也歡迎掃碼關注我們的微信公眾號:

我們的微信公眾號

翻譯

我們提供了英文版原文和中譯版(本文)。我們也歡迎讀者提供其他語言的翻譯版本,只要是全文翻譯不帶省略,我們都將會考慮採用,非常感謝!