一個微秒量級的反向代理環節,在特定條件下產生了 93% 的效能損耗,且常規監控指標呈現出虛假的“一切正常”。這是一個典型的觀測盲區,也是系統性雪崩的隱患。本文覆盤了一次從 15 倍效能差距最佳化至 10% 預期損耗的深度調優。相比單純的修復,我們更關注如何用 OpenResty XRay 穿透編譯器行為與連線管理的底層瓶頸。在現代高併發架構中,對工程細節的掌控力,直接決定了基礎設施的穩定性邊界。

93% 損耗背後的觀測盲區

在任何成熟的工程團隊中,效能驗收通常是一個確定性的、流程化的環節。尤其對於閘道器這類基礎元件,其架構(Client → OpenResty Gateway → Upstream)已經是一個被反覆驗證的成熟模式。理論上,一個僅做輕量級轉發的閘道器,其效能開銷應是可預測且極低的。

然而,在對新版閘道器進行基準壓測時,我們遭遇了驚人的效能衰退:

  • 上游服務基線 (直連壓測): 94,706 QPS
  • 新版閘道器 (反向代理): 6,301 QPS

效能憑空蒸發了 93%。一個簡單的反向代理,竟然造成了近 15 倍的效能差距。系統表面上風平浪靜:沒有錯誤日誌,配置也符合常規認知,問題顯然已經下沉到我們日常觀測的視野之外。

OpenResty XRay 揭示連線複用問題

我們使用 OpenResty XRay 對閘道器進行 CPU 效能分析,執行 lj-c-on-cpu 工具生成火焰圖:

CPU 火焰圖被兩大塊異常寬闊的平臺所佔據:connect()close() 系統呼叫。

在高效能網路服務中,這兩者本應是幾乎不可見的“背景噪音”。它們的異常凸顯,指向一個致命的問題:上游連線沒有被複用。每一個進來的請求,都在觸發一次全新的 TCP 握手和揮手。在高併發場景下,這無異於一場“連線風暴”,核心將大量時間消耗在建立和銷燬連線上,而非真正的資料處理。

透過 OpenResty XRay 提供的完整呼叫棧,我們迅速鎖定了問題根源:

  • Connect 路徑: ngx_http_proxy_handlerngx_http_upstream_connectconnect()
  • Close 路徑: ngx_http_upstream_finalize_requestngx_close_connectionclose()

診斷清晰明瞭:我們的 upstream 配置塊中,遺漏了 keepalive 指令。這是一個看似微小、卻足以在生產環境中引發雪崩的配置疏忽。

我們立即啟用了上游 keepalive。效果立竿見影:效能提升:從 6,301 QPS 到 21,923 QPS,提升 3.48 倍!

Screenshot

connect()close() 的尖峰已然消失。第一個效能黑洞被填補,但故事還未結束。

對比分析發現編譯選項問題

儘管效能大幅回升,但資深的工程師直覺告訴我們,事情沒那麼簡單。我們將當前版本的效能(21,923 QPS)與上一個穩定版本(24,115 QPS)進行對比,發現仍然存在約 10% 的效能差距

這 10% 的差距從何而來?它雖然不像 15 倍的鴻溝那樣觸目驚心,但對於追求極致效能的基礎設施而言,任何不明不白的衰退都是不可接受的。

  • 當前版本的火焰圖“更深”:呼叫棧的層級明顯多於穩定版本。
  • 特定函式呼叫增多:例如,ngx_http_gunzip_body_filter 這樣的函式在呼叫鏈中頻繁出現,而在穩定版本中則幾乎不可見。

檢視 ngx_http_gunzip_body_filter 的程式碼:

static ngx_int_t
ngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    int                     rc;
    ngx_uint_t              flush;
    ngx_chain_t            *cl;
    ngx_http_gunzip_ctx_t  *ctx;

    ctx = ngx_http_get_module_ctx(r, ngx_http_gunzip_filter_module);

    if (ctx == NULL || ctx->done) {
        return ngx_http_next_body_filter(r, in);
    }
}

理論上,Nginx/OpenResty 的 filter 鏈大量使用了尾呼叫(tail-call),在最佳化編譯下,這些呼叫應該被編譯器“拉平”,幾乎沒有額外開銷。而當前版本的火焰圖形態,恰恰暗示著編譯器最佳化可能失效了

編譯器選項對指令執行效率的影響

順著這條線索,我們核查了兩個版本的構建指令碼,發現:

當前開發版本使用了 -O0 編譯選項。

原因是在開發過程中,為了方便除錯某個問題,臨時將編譯最佳化關閉了。但在功能開發完成後,忘記恢復為最佳化編譯選項

-O0 意味著:

  1. 函式內聯失效 - 小函式呼叫無法被內聯
  2. 尾呼叫最佳化失效 - 呼叫棧變深
  3. 冗餘程式碼未消除 - 更多無用指令
  4. 暫存器使用效率低 - 更多記憶體訪問

這些看似微小的編譯器行為差異,累積起來,不多不少,恰好“偷”走了我們 10% 的效能。

我們將編譯選項恢復為標準的 -O2,重新構建並部署。

結果顯示效能完全恢復,與穩定版本持平。至此,兩個效能問題被徹底解決。

15 倍 QPS 差異的工程性覆盤

這次從 94K QPS 到 6K QPS 的效能排查,暴露了現代複雜系統中兩個深刻的挑戰:

  1. “隱形上下文”的漂移:效能不僅由程式碼邏輯決定,更取決於構建與執行時環境的“隱形上下文”——例如編譯選項、核心引數和執行時配置。這些上下文的變化往往在 Code Review 中難以察覺,卻能導致災難性的後果。本次的 keepalive 缺失和 -O0 編譯選項就是最好的例證。

  2. 突破觀測盲區:為何你需要動態追蹤?當問題下沉至核心與編譯器層面,傳統的 Logs & Metrics 往往保持靜默。 本案例證明,面對系統級瓶頸,我們需要的是 OpenResty XRay 這樣具備穿透力的工具:

  • 全鏈路取樣:穿透應用層直達核心,快速識別宏觀瓶頸(如連線風暴)
  • 異常模式識別:透過火焰圖快速識別非預期的 connect/close 路徑。
  • 基線對比:定位微觀層面的細微差異(如編譯器行為)

效能問題往往不是非黑即白的 Bug,而是灰度的效率流失。 在複雜的系統架構中,僅憑經驗猜測是低效的。我們需要透過 OpenResty XRay 構建起從應用邏輯到核心呼叫的完整可觀測性,讓我們有能力去度量、分析和最佳化那些隱藏在系統深處的“灰色地帶”,從而確保系統的長期穩定與卓越效能。

關於 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. 公司的部落格網站 。也歡迎掃碼關注我們的微信公眾號:

我們的微信公眾號

翻譯

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