訂閱以接收新文章的通知:

Pingora 開放原始碼:我們用於構建可程式設計網路服務的 Rust 框架

2024-02-28

閱讀時間:5 分鐘
本貼文還提供以下語言版本:EnglishFrançaisDeutsch日本語한국어PortuguêsEspañol简体中文

今天,我們很自豪能夠對 Pingora 開放原始碼,這是一個 Rust 架構,我們一直用該架構來建立服務,為 Cloudflare 上的很大一部分流量提供支援。Pingora 是在 Apache License v2.0發佈的。

Open sourcing Pingora: our Rust framework for building programmable network services

正如我們之前的部落格文章中提到的,Pingora 是一個 Rust 非同步多執行緒架構,可以幫助我們建立 HTTP 代理服務。自我們發佈上一篇部落格文章以來,Pingora 已在我們的全球網路中處理了近千萬億的網際網路請求。

我們正在對 Pingora 開放原始碼,以幫助在我們自己的基礎結構之外建立更好、更安全的網際網路。我們希望為我們的客戶、使用者和其他人提供工具、想法和靈感,以使用記憶體安全架構建置自己的網際網路基礎架構。鑑於業界美國政府越來越意識到記憶體安全的重要性,擁有這樣一個架構尤其重要。基於這一共同目標,我們與網際網路安全研究小組 (ISRG) Prossio 專案合作,幫助推動 Pingora 在網際網路最關鍵基礎架構中的採用。

上一篇部落格文章中,我們討論了構建 Pingora 的原因和方式。在這篇文章中,我們將討論您為什麼要使用以及如何使用 Pingora。

Pingora 同時為代理、用戶端和伺服器提供構建區塊。除了這些元件外,我們還提供了一些實用程式庫,來實作事件計數、錯誤處理和快取等常見邏輯。

架構內容

Pingora 提供程式庫和 API 來在 HTTP/1 和 HTTP/2、TLS 或 TCP/UDP 之上建立服務。作為代理,它支援 HTTP/1 和 HTTP/2 端對端、gRPC 和 websocket 代理。(HTTP/3 支援已在規劃中。)它還具有可自訂的負載平衡和容錯移轉策略。在合規性和安全性方面,它支援常用的 OpenSSL 和 BoringSSL 庫,這些庫具有 FIPS 合規性和後量子加密

除了提供這些功能之外,Pingora 還提供篩選器和回呼,以允許使用者完全自訂服務應如何處理、轉換和轉寄請求。這些 API 對於 OpenResty 和 NGINX 使用者來說尤其熟悉,因為許多 API 直觀地對應到 OpenResty 的「*_by_lua」回呼。

在操作上,Pingora 提供零停機時間的優雅重新啟動,以在不捨棄任何傳入請求的情況下自行升級。syslog、Prometheus、Sentry、OpenTelemetric 和其他必備的可觀察性工具也可以輕鬆與 Pingora 整合。

誰可以從 Pingora 受益

如果符合以下情況,則您應該考慮 Pingora:

**安全性是您的首要任務:**對於用 C/C++ 編寫的服務,Pingora 是一種記憶體更安全的替代方案。雖然有些人可能會爭論程式語言之間的記憶體安全,但從我們的實踐經驗來看,我們發現自己不太可能犯下導致記憶體安全問題的編碼錯誤。此外,由於我們花更少的時間來解決這些問題,因此我們在實作新功能時效率更高。

**您的服務對效能敏感:**Pingora 快速且有效率。正如我們之前的部落格文章中所解釋的,由於 Pingora 的多執行緒架構,我們節省了大量的 CPU 和記憶體資源。對於對系統成本和/或速度敏感的工作負載來說,時間和資源的節省可能非常引人注目。

**您的服務需要大量自訂:**Pingora 代理架構提供的 API 是高度可程式設計的。對於希望建立自訂的進階閘道或負載平衡器的使用者而言,Pingora 提供了強大而簡單的實作方法。我們將在下一節中提供範例。

我們來構建一個負載平衡器

讓我們構建一個簡單的負載平衡器來探索 Pingora 的可程式化 API。負載平衡器將以循環方式輪流選擇 https://1.1.1.1/https://1.0.0.1/ 作為上游。

首先,讓我們建立一個空白的 HTTP 代理。

任何實作 ProxyHttp 特徵(類似 C++ 或 Java 中的介面概念)的物件都是 HTTP 代理。唯一需要的方法是 upstream_peer(),每個請求都會呼叫該方法。此函數應傳回一個 HttpPeer,其中包含要連線的原始 IP 以及連線方式。

pub struct LB();

#[async_trait]
impl ProxyHttp for LB {
    async fn upstream_peer(...) -> Result<Box<HttpPeer>> {
        todo!()
    }
}

接下來我們來實作循環選擇。Pingora 架構已經為 LoadBalancer 提供了常見的選擇演算法,例如循環和雜湊,所以我們就使用這些方法。如果使用案例需要更複雜或自訂的伺服器選擇邏輯,使用者可以簡單地在此函數中自行實作。

由於我們連線的是 HTTPS 伺服器,因此還需要設定 SNI。如果需要,也可以在 HttpPeer 物件中設定憑證、逾時和其他連線選項。

pub struct LB(Arc<LoadBalancer<RoundRobin>>);

#[async_trait]
impl ProxyHttp for LB {
    async fn upstream_peer(...) -> Result<Box<HttpPeer>> {
        let upstream = self.0
            .select(b"", 256) // hash doesn't matter for round robin
            .unwrap();

        // Set SNI to one.one.one.one
        let peer = Box::new(HttpPeer::new(upstream, true, "one.one.one.one".to_string()));
        Ok(peer)
    }
}

最後,讓我們將服務付諸實踐。在此範例中,我們對來源伺服器 IP 進行硬編碼。在現實工作負載中,當呼叫 upstream_peer() 或在背景中時,也可以動態探索來源伺服器 IP。建立服務後,我們只需告訴 LB 服務監聽 127.0.0.1:6188 即可。最後我們建立了一個 Pingora 伺服器,該伺服器將是執行負載平衡服務的處理序。

我們來嘗試一下:

fn main() {
    let mut upstreams = LoadBalancer::try_from_iter(["1.1.1.1:443", "1.0.0.1:443"]).unwrap();

    let mut lb = pingora_proxy::http_proxy_service(&my_server.configuration, LB(upstreams));
    lb.add_tcp("127.0.0.1:6188");

    let mut my_server = Server::new(None).unwrap();
    my_server.add_service(lb);
    my_server.run_forever();
}

我們可以看到代理正在工作,但來源伺服器拒絕了我們並顯示 403。這是因為我們的服務只是代理由 curl 設定的主機標頭 127.0.0.1:6188,這會擾亂來源伺服器。我們如何讓代理糾正這一點?這可以簡單地透過新增另一個名為 upstream_request_filter 的篩選器來完成。在連線來源伺服器之後和傳送任何 HTTP 請求之前,此篩選器會對每個請求執行。我們可以在此篩選器中新增、移除或變更 http 請求標頭。

curl 127.0.0.1:6188 -svo /dev/null
> GET / HTTP/1.1
> Host: 127.0.0.1:6188
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 403 Forbidden

我們再試一次:

async fn upstream_request_filter(…, upstream_request: &mut RequestHeader, …) -> Result<()> {
    upstream_request.insert_header("Host", "one.one.one.one")
}

這次成功了!完整範例可在此處找到。

curl 127.0.0.1:6188 -svo /dev/null
< HTTP/1.1 200 OK

下面是一個非常簡單的圖表,說明了該請求如何流經我們在本範例中使用的回呼和篩選器。Pingora 代理架構目前在請求的不同階段提供更多篩選器和回呼,以允許使用者修改、拒絕、路由和/或記錄請求(和回應)。

在幕後,Pingora 代理架構負責連線集區、TLS 交握、讀取、寫入、剖析請求以及任何其他常見的代理任務,以便使用者可以專注於對他們而言重要的邏輯。

開放原始碼的現在與未來

Pingora 是一個函式庫和工具組,而不是可執行二進位檔。換句話說,Pingora 是為汽車提供動力的引擎,而不是汽車本身。儘管 Pingora 已做好生產準備以供業界使用,但我們瞭解許多人想要一個包含電池、具有低程式碼或無程式碼設定選項的即用型 Web 服務。在 Pingora 之上建立該應用程式將是我們與 ISRG 合作的重點,以擴大 Pingora 的影響力。請繼續關注該專案的未來公告。

其他需要牢記的注意事項

  • **如今,API 的穩定性無法保證。**儘管我們會盡量減少進行重大變更的頻率,但我們仍然保留隨著庫的發展新增、移除或變更元件(例如請求和回應篩選器)的權利,特別是在 1.0 之前的時期。

  • **目前尚未計劃支援基於非 Unix 的作業系統。**我們沒有近期計畫來支援這些系統,但這種情況未來可能會改變。

如何投稿

請隨意在我們的 GitHub 問題追蹤器中提出錯誤報告、文件問題或功能請求。在提出拉取請求之前,我們強烈建議您查看我們的投稿指南

結論

在這篇部落格中,我們宣佈了 Pingora 架構開放原始碼。我們展示了網際網路實體和基礎架構可以從 Pingora 的安全性、效能和可自訂性中受益。我們還示範了使用 Pingora 是多麼容易以及它是如何可自訂的。

無論您是建立生產 Web 服務還是嘗試網路技術,我們都希望您能在 Pingora 中找到價值。這是一個漫長的旅程,但與開放原始碼社群分享這個專案從一開始就是一個目標。我們要感謝 Rust 社群,因為 Pingora 是使用許多優秀的開放原始碼 Rust crate 構建而成。遷移到記憶體安全的網際網路可能感覺像是一場不可能實現的旅程,但我們希望您加入我們。

我們保護整個企業網路,協助客戶有效地建置網際網路規模的應用程式,加速任何網站或網際網路應用程式抵禦 DDoS 攻擊,阻止駭客入侵,並且可以協助您實現 Zero Trust

從任何裝置造訪 1.1.1.1,即可開始使用我們的免費應用程式,讓您的網際網路更快速、更安全。

若要進一步瞭解我們協助打造更好的網際網路的使命,請從這裡開始。如果您正在尋找新的職業方向,請查看我們的職缺
Developer PlatformDevelopersRustOpen SourcePerformance

在 X 上進行關注

Cloudflare|@cloudflare

相關貼文

2024年10月31日 下午1:00

Moving Baselime from AWS to Cloudflare: simpler architecture, improved performance, over 80% lower cloud costs

Post-acquisition, we migrated Baselime from AWS to the Cloudflare Developer Platform and in the process, we improved query times, simplified data ingestion, and now handle far more events, all while cutting costs. Here’s how we built a modern, high-performing observability platform on Cloudflare’s network. ...

2024年10月25日 下午1:00

Elephants in tunnels: how Hyperdrive connects to databases inside your VPC networks

Hyperdrive (Cloudflare’s globally distributed SQL connection pooler and cache) recently added support for directing database traffic from Workers across Cloudflare Tunnels. We dive deep on what it took to add this feature....