Rust 上に構築された新しい HTTP プロキシである Pingora を紹介できることを嬉しく思います。 1 日あたり 1 兆を超えるリクエストを処理し、パフォーマンスを向上させ、Cloudflare の顧客に新機能を提供しながら、必要な CPU リソースとメモリ リソースは元のプロキシ インフラストラクチャの 3 分の 1 のみです。
Cloudflare が拡張し続けるにつれて、NGINX の処理能力ではもはやニーズを満たせないことがわかりました。長年にわたってうまく機能していましたが、時間が経つにつれて、私たちの規模での課題に対処するには限界があることに気づきました。したがって、パフォーマンスと機能のニーズを満たすために、いくつかの新しいソリューションを構築する必要があると感じました。
Cloudflareの顧客とユーザーは、HTTPクライアントとサーバー間のプロキシとしてCloudflareグローバルネットワークを使用します。私たちは多くの議論を行い、多くのテクノロジーを開発し、QUIC や HTTP/2 の最適化などの新しいプロトコルを実装して、ネットワークに接続するブラウザーやその他のユーザー エージェントの効率を向上させてきました。
今日は、この方程式に関連するもう 1 つの側面、つまりネットワークとインターネット サーバー間のトラフィックの管理を担当するプロキシ サービスに焦点を当てます。このプロキシ サービスは、CDN、ワーカーフェッチ、トンネル、ストリーム、R2、その他多くの機能と製品のサポートとパワーを提供します。
従来のサービスをアップグレードすることにした理由を詳しく調べ、Pingora システムの開発プロセスを探ってみましょう。このシステムは、Cloudflare の顧客のユースケースと規模に合わせて特別に設計されています。
近年、NGINX を使用する際にいくつかの制限に遭遇しました。一部の制限については、それらを回避する方法を最適化または採用しました。ただし、より困難な制限がいくつかあります。
NGINX ワーカー (プロセス) アーキテクチャ [4] には、私たちのユースケースにとって運用上の欠陥があり、パフォーマンスと効率に悪影響を及ぼします。
まず第一に、NGINX では、各リクエストは 1 つのワーカーによってのみ処理されます。これにより、すべての CPU コア [5] 間で負荷の不均衡が生じ、速度低下が発生します [6]。
このリクエスト プロセスのロック効果により、CPU を集中的に使用するリクエストやブロック IO タスクを実行するリクエストは、他のリクエストの速度を低下させる可能性があります。これらのブログ投稿で指摘されているように、私たちはこれらの問題の解決に多くの時間を費やしてきました。
このユースケースで最も重要な問題は、マシンが HTTP リクエストをプロキシするオリジン サーバーとの TCP 接続を確立するときの接続の再利用です。接続プールからの接続を再利用すると、新しい接続に必要な TCP および TLS ハンドシェイクをスキップできるため、リクエストの TTFB (最初のバイトまでの時間) が高速化されます。
ただし、NGINX 接続プール [9] は 1 つのワーカーに対応します。リクエストがワーカーに到達すると、そのワーカー内でのみ接続を再利用できます。拡張するために NGINX ワーカーを追加すると、接続がすべてのプロセスにわたってより分離されたプールに分散されるため、接続の再利用は悪化します。その結果、TTFB が遅くなり、維持しなければならない接続が増加し、当社とお客様のリソース (およびお金) が消費されます。
過去のブログ投稿で述べたように、これらの問題のいくつかについては回避策を提供しました。しかし、ワーカー/プロセス モデルという根本的な問題を解決できれば、これらの問題はすべて自然に解決されるでしょう。
一部の機能は追加が困難です
NGINX は、優れた Web サーバー、ロード バランサー、またはシンプルなゲートウェイです。しかし、Cloudflare はそれ以上のことを行います。以前は必要なすべての機能を NGINX を中心に構築していましたが、NGINX アップストリーム コードベースからの分岐を避けようとするのは簡単ではありませんでした。
たとえば、リクエスト/リクエストの失敗[10]を再試行するときに、異なるリクエスト ヘッダーのセットを使用して、別のオリジン サーバーにリクエストを送信したい場合があります。しかし、NGINX はこれを許可しません。この場合、NGINX の制限を回避するために時間と労力を費やす必要があります。
同時に、私たちが使用を余儀なくされているプログラミング言語は、これらの問題を軽減するのに役立ちません。 NGINX は純粋に C で書かれており、設計上メモリ安全ではありません。このようにサードパーティのコードベースを使用すると、非常にエラーが発生しやすくなります。経験豊富なエンジニアであっても、メモリの安全性の問題 [11] に陥りやすいため、これらの問題はできる限り回避したいと考えています。
C を補完するために使用するもう 1 つの言語は Lua です。リスクは低くなりますが、パフォーマンスも低くなります。さらに、複雑な Lua コードやビジネス ロジックを扱う場合、静的型付けが欠けていることに気づくことがよくあります [12]。
さらに、NGINX コミュニティはあまり活発ではなく、開発は「密室」で行われることがよくあります [13]。
独自の構築を選択する
過去数年間、顧客ベースと機能セットが拡大し続ける中、私たちは 3 つのオプションを評価し続けてきました。
過去数年間、私たちはこれらのオプションを四半期ごとに評価してきました。どのオプションが最適であるかを決定するための明確な公式はありません。数年間にわたり、私たちは最も抵抗の少ない道を歩み続け、NGINX を強化し続けました。ただし、場合によっては、独自の代理店を構築することの ROI の方が価値があると思われる場合もあります。私たちはエージェントをゼロから構築することを求め、夢のエージェント アプリケーションの設計を開始しました。
毎秒数百万のリクエストに対応する高速、効率的、安全なプロキシを構築するには、まずいくつかの重要な設計上の決定を下す必要がありました。
Rust[16] をプロジェクトの言語として選択したのは、パフォーマンスに影響を与えることなく、C で実行できることをメモリ安全な方法で実行できるためです。
hyper[17] など、優れた既製のサードパーティ HTTP ライブラリがいくつかありますが、HTTP トラフィックの処理における柔軟性を最大限に高め、独自の Innovate に確実に従うことができるようにしたかったため、独自のライブラリを構築することにしました。ペースで。
Cloudflare では、インターネット全体のトラフィックを処理します。私たちは、HTTP トラフィックの多くの奇妙なケースや RFC に準拠していないケースをサポートする必要があります。これは HTTP コミュニティと Web に共通するジレンマであり、HTTP 仕様に厳密に従うか、潜在的なレガシー クライアントやサーバーのより広範なエコシステムの微妙な違いに適応するか、難しい選択をする必要があります。
HTTP ステータス コードは、RFC 9110 で 3 桁の整数 [18] として定義されており、通常は 100 ~ 599 の範囲であると予想されます。 Hyper はそのような実装の 1 つです。ただし、多くのサーバーは 599 ~ 999 のステータス コードの使用をサポートしています。この特集では、議論のさまざまな側面を探る質問 [19] を作成しました。ハイパー チームは最終的にこの変更を受け入れましたが、そのような要求を拒否するには十分な理由があり、これは私たちがサポートする必要があった多くの不遵守事例の 1 つにすぎませんでした。
HTTP エコシステムにおける Cloudflare の地位を満たすためには、インターネットのさまざまなリスク環境に耐え、さまざまな非準拠のユースケースをサポートできる、堅牢で耐性があり、カスタマイズ可能な HTTP ライブラリが必要です。これを保証する最善の方法は、独自のアーキテクチャを実装することです。
次の設計上の決定は、ワークロード スケジューリング システムに関するものです。リソース、特に接続プーリングを簡単に共有するために、マルチプロセッシング [20] ではなくマルチスレッドを選択します。私たちは、上記の特定のカテゴリのパフォーマンス問題を回避するために、ワーク スティーリング [21] も実装する必要があると考えています。 Tokio 非同期ランタイムは、私たちのニーズに完全に適合します [22]。
最後に、私たちはプロジェクトを直感的で開発者にとって使いやすいものにしたいと考えています。私たちが構築しているものは最終製品ではありませんが、その上にさらに多くの機能が構築されるため、プラットフォームとして拡張可能である必要があります。私たちは、NGINX/OpenResty[23]と同様の「リクエストライフサイクル」イベントに基づいてプログラマブルインターフェイスを実装することにしました。たとえば、Request Filter ステージを使用すると、開発者はリクエスト ヘッダーの受信時にリクエストを変更または拒否するコードを実行できます。この設計により、ビジネス ロジックと一般的なプロキシ ロジックを明確に分離できます。以前 NGINX に取り組んでいた開発者は、Pingora に簡単に切り替えることができ、すぐに生産性が向上します。
話を早送りしてみましょう。 Pingora は、オリジン サーバーとの対話を必要とするほぼすべての HTTP リクエスト (キャッシュ ミスなど) を処理し、その過程で大量のパフォーマンス データを収集します。
まず、Pingora がどのように顧客のトラフィックを高速化するかを見てみましょう。 Pingora の全体的なトラフィックは、TTFB の中央値が 5 ミリ秒の減少、95 パーセンタイルの減少が 80 ミリ秒であることを示しています。それはコードの実行が速くなったからではありません。私たちの古いサービスでも、ミリ秒未満の範囲でリクエストを処理できます。
すべてのスレッドで接続を共有する新しいアーキテクチャにより、時間の節約が可能になります。これは、接続の再利用が向上し、TCP および TLS ハンドシェイクにかかる時間が短縮されることを意味します。
全顧客全体で見ると、Pingora の 1 秒あたりの新規接続数は、古いサービスと比較して 3 分の 1 にすぎません。ある大手顧客では、接続の再利用が 87.1% から 99.92% に増加し、新しい接続が 160 分の 1 に減少しました。ざっくり言うと、Pingora に切り替えることで、顧客とユーザーの 434 年間にわたる毎日の握手を節約できました。
たとえば、大きな障害を伴うことなく、HTTP/2 アップストリーム サポートを Pingora に追加することができました。これにより、お客様がすぐに gRPC を利用できるようになります[24]。同じ機能を NGINX に追加するには、より多くのエンジニアリング作業が必要となり、不可能になる可能性があります [25]。
最近、Pingora が R2 ストレージをキャッシュ レイヤーとして使用する Cache Reserve[26] の開始を発表しました。 Pingora にさらに多くの機能を追加することで、以前は実現できなかった新しい製品を提供できるようになります。
#########もっと効率的な#########
実稼働環境では、Pingora は、以前のサービスと比較して、同じトラフィック負荷の下で CPU 消費量が約 70%、メモリ消費量が約 67% 少なくなります。節約にはいくつかの要因があります。より安全な
私たちの規模では、機能を迅速かつ安全にリリースすることは困難です。 1 秒あたり数百万のリクエストを処理する分散環境で発生する可能性のあるすべてのエッジケースを予測することは困難です。ファズテストと静的解析では軽減できる範囲は限られています。 Rust のメモリセーフ セマンティクスは、未定義の動作から私たちを守り、サービスが正しく実行されるという確信を与えてくれます。
要約
要約すると、当社は現在および将来の製品のプラットフォームとして機能する、より高速で効率的、そして多用途な社内エージェントを構築しました。
以上がピンゴラってすごいですね! Nginxを超える超人気Webサーバーの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。