放棄 Nginx,Cloudflare 用 Rust 重寫了其代理架構 Pingora

最近 Cloudflare 技術博客上介紹了其新一代 Web 代理——Pingora,一個用 Rust 從頭重寫地支持每天處理超 1 萬億個請求,最大限度提高性能並可以節省三分之 CPU 和內存硬件資源的代理服務器。今天請和我們來一起學習這新一代技術代理服務器的 Pingora。

概述

Cloudflare 表示 Pingora 未來會開源, 目前還不能從代碼層面進行相關技術的探索,只能從架構方面一探一斑。

作爲世界上最大的免費 CDN 服務商,Cloudflare 的代理端層運行這世界上最大 Web 請求,每天的客戶端請求超萬億。之前 Cloudflare 的代理端層架構一度使用的基於內部定製化的 Nginx 服務器,但是起不論在性能上,規模上以及功能層面都面臨日益困窘的局面。

架構限制會損害性能

NGINX work(進程)架構在用 CF 用例中存在操作缺陷,會導致嚴重性能和效率開銷。

首先,在 NGINX 中,每個請求只能由單個 Work 處理。這會導致節點 CPU 內核的負載不平衡 ,導致運行緩慢。

由於這種請求進程鎖定效應,執行重 CPU 或阻塞 IO 任務的請求可能會減慢其他請求的速度。舉實際用例來說,最大問題問題是連接重用性問題。代理層節點機器與源服務器建立 TCP 連接以代理 HTTP 請求。連接重用通過重用連接池中先前建立的連接、跳過新連接所需的 TCP 和 TLS 握手來加速請求的 TTFB(第一個字節時間)。 但是,NGINX 連接池基於 Work 的。當請求到達某個 Work 時,它只能重用該 Work 內的連接。當添加更多 NGINX Work 進行擴展時,由於連接分散在所有進程的更多隔離池中其重用性就會變得很差,這樣就需要耗費額外的硬件資源,並且使響應時間變慢。

雖然在這方面通過各種方法做過優化,但是其根源在於 Nginx Work / 進程模型,在要還是該架構就不能從根本上解決該問題。

某些類型的功能難以添加

NGINX 是一個非常好的 Web 服務器、負載均衡器或網關。但 Cloudflare 的業務需求遠不止於此。圍繞 NGINX 構建我們需要的所有功能,但要儘量避免與 NGINX 上游代碼庫有太多分歧,這並不容易。

例如,當重試 / 失敗請求時,業務場景最佳需求是將請求發送到具有不同請求標頭集的不同源服務器。但這不是 NGINX 允許應用層可做的事情。爲此會花費時間和精力來解決 修改 NGINX 源碼來突破限制。

另外 NGINX 純粹是用 C 語言編寫的,其設計上不是內存安全的。使用這樣的第三方代碼庫非常容易出錯。也很容易出現內存安全問題 ,如何儘可能避免這些問題是個問題。

雖然 Nginx 的應用邏輯可以通過 Lua 語言來實現。可以導致的安全風險較小,但性能也較差。 此外,Lua 在處理複雜的代碼和業務邏輯時缺乏有效的靜態類型檢查。

架構選型

在過去幾年中,Cloudflare 團隊在不斷評估和考慮架構升級,基本上有三條路線:

第一、繼續走 NGINX 路線,並使用其 Fork 版本來 100% 滿足業務需求。 儘管 Cloudflare 團隊對此輕車熟路,但鑑於上面提到的架構限制,需要付出大量努力才能以完全支持業務需求。第二、遷移到另一個第三方的代理代碼庫。 比如基於 envoy 等 。但同樣的需要不斷探索重複嘗試,用新車走老路才能磨合。

第三、從零開始,構建內部平臺和框架。 這種選擇需要在開發方面進行需要大量的前期投入。

最後,經過多年探索嘗試,躺平小動的基礎上,最終選擇從零開始構建一個全新的代理架構,這就是 Pingora。

架構

爲了使代理能夠快速、高效和安全地處理每秒數百萬個請求,必須首先做出一些重要的設計決策。

首先,選擇 Rust 作爲項目的語言,因爲它可以在不影響性能的情況下以內存安全的方式完成 C 可以做的事情。

儘管有一些很棒的現成的第三方 HTTP 庫,例如 hyper,但爲了提高最大靈活性,項目從伊始之初就選擇構建自己庫來處理 HTTP 流量的靈活性。

在 Cloudflare 業務中,需要處理整個互聯網流量。必須支持許多奇怪且不符合 RFC 的 HTTP 流量案例。這是 HTTP 社區和 Web 中的一個常見困境,在嚴格遵循 HTTP 規範和適應潛在遺留客戶端或服務器的廣泛生態系統的細微差別之間存在緊張關係。比如,HTTP 狀態代碼在 RFC 9110 中定義爲一個三位整數 ,通常爲 100 到 599 之間。比如 Hyper 就遵從該規定。但是,許多服務器支持擴展使 599 到 999 之間的狀態代碼。

爲了滿足 Cloudflare 在 HTTP 生態系統中的地位要求,需要一個健壯、寬鬆、可定製的 HTTP 庫,該庫可以在 Internet 中生存並支持各種不合規的用例。保證這一點的最好方法從零造自己的輪子。

另一個設計決策是圍繞工作負載調度系統。Pingora 選擇多線程而不是多處理架構。這是爲了輕鬆共享資源,尤其是連接池。我們還決定需要工作竊取(work stealing)功能來避免上面提到的某些類別的性能問題。Tokio 異步運行時結果就非常適合需求。

最後,希望整個項目直觀且對開發人員友好。項目構建目標的不是最終產品,應該可以作爲一個平臺進行擴展,因爲在它之上構建了更多的功能。所以,實現上是一個類似我們決定實現一個 NGINX/OpenResty 的服務器。例如,“請求過濾器” 階段允許開發人員在收到請求標頭時運行代碼來修改或拒絕請求。通過這種設計,可以清晰地分離我們的業務邏輯和通用代理邏輯。這樣可以讓 NGINX 開發者的一些歷史應用可以輕鬆切換到 Pingora 直接使用,這樣可以提高工作效率。

高效開發,高速運行

Pingora 處理幾乎所有需要與源服務器交互的 HTTP 請求(例如緩存未命中),通過實際的性能運行數據可以看 Pingora 性能。

首先,Pingora 上的總體流量顯示,TTFB 中位數減少了 5 毫秒,第 95 % 的請求減少了 80 毫秒。運行代碼更快。甚至舊服務也可以處理亞毫秒範圍內的請求。

時間節省來自源於新架構可以跨所有線程共享連接。這意味着更好的連接重用率,在 TCP 和 TLS 握手上花費的時間更少。

在所有客戶中,與舊服務相比,Pingora 每秒的新連接數只有三分之一。對於一個主要客戶,它將連接重用率從 87.1% 提高到 99.92%,這將新連接減少了 160 倍。爲了更直觀地呈現數字,通過切換到 Pingora,每天爲客戶節省了總計 434 年的 TCP 握手時間。

更多功能

擁有工程師熟悉的開發人員友好界面,同時消除以前的限制,能夠更快地開發更多功能。 新協議等核心功能充當了面向客戶提供的更多產品的構建塊。

Pingora 能夠在沒有重大障礙的情況下添加 HTTP/2 上游支持。所以可以在實現 RPC 功能之後馬上就推客戶使用。要將相同的功能添加到 NGINX 將需要更多的工程努力,並且可能無法實現 。

最近,Cloudflare 推出了 Cache Reserve ,其中 Pingora 使用 R2 存儲作爲緩存層。隨着向 Pingora 添加更多功能,Cloudflare 可以快節奏地提供以前不可行的新產品。

更高效

在生產環境中,Pingora 在相同流量負載的情況下,與舊服務相比,消耗的 CPU 和內存減少了約 70% 和 67%。節省來自幾個因素:

相比 Lua 代碼邏輯,Rust 代碼運行效率更高。最重要的是,它們的架構也存在效率差異。 例如,在 NGINX/OpenResty 中,當 Lua 代碼想要訪問 HTTP 頭時,它必須從 NGINX C 結構中讀取它,分配一個 Lua 字符串,然後將其複製到 Lua 字符串中。之後,Lua 也必須 GC 起新字符串。在 Pingora 中,它只是一個直接的字符串訪問。

多線程模型還使得跨請求共享數據更加高效。NGINX 也有共享內存,但由於實現限制,每次共享內存訪問都必須使用互斥鎖,並且只能將字符串和數字放入共享內存。在 Pingora 中,大多數共享項目可以通過原子引用計數器 。

如上所述,CPU 節省的另一個重要部分是減少了新的連接。與僅通過已建立的連接發送和接收數據相比,TLS 握手成本很高。

更安全

快速安全地發佈功能很困難,尤其是在 Cloudflare 這樣的超大規模下。很難預測在每秒處理數百萬個請求的分佈式環境中可能發生的每個邊緣情況。模糊測試和靜態分析只能緩解這麼多。Rust 的內存安全語義保護免受未定義行爲的影響,並讓工程師確信服務將正確運行。

有了這些保證,可以更多地關注業務服務更改將如何與其他服務或客戶來源進行交互。從而可以以更高的節奏開發功能,而不會受到內存安全和難以診斷崩潰的負擔。

當崩潰確實發生時,工程師需要花時間來診斷它是如何發生的以及是什麼原因造成的。自 Pingora 成立以來,Cloudflare 團隊已經處理了數百萬億個請求,並且還沒有因爲服務代碼而崩潰。

事實上,Pingora 崩潰是如此罕見,當遇到一個問題時,通常會發現不相關的問題。最近 Cloudflare 團隊在服務崩潰後分析過程中發現了 LInux 內核的 Bug,還發現一些服務器的硬件問題,由於可以快速排除由於軟件引起的罕見內存錯誤,即使在幾乎不可能進行重大調試之後也是如此。

結論

總而言之,Cloudflare 在內部已經啓用一個一個更快、更高效、更通用的內部代理 Pingora,作爲起當前和未來產品的代理層架構平臺。Cloudflare 後續會在適當的累積和總結後開源 Pingora。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/s35QYkUD8iKAWNwTlAdXkg