インスピレーション
今日のマイクロサービス アーキテクチャでは、リバース プロキシは、受信リクエストを管理し、さまざまなバックエンド サービスにルーティングする上で重要な役割を果たしています。
リバース プロキシは、アプリケーションの Web サーバーの前に配置され、クライアント マシンからのリクエストをインターセプトします。これには、負荷分散、セキュリティの向上につながるオリジンサーバーの IP アドレスの隠蔽、キャッシュ、レート制限など、多くの利点があります。
分散型マイクロサービス アーキテクチャでは、単一のエントリ ポイントが必要です。 Nginx などのリバース プロキシ サーバーは、このようなシナリオに役立ちます。サーバーの複数のインスタンスを実行している場合、効率的なリクエスト ルーティングの管理と確保は困難になります。この場合、Nginx のようなリバース プロキシが完璧なソリューションです。ドメインを Nginx サーバーの IP アドレスに指定すると、Nginx は、各インスタンスで処理される負荷を処理しながら、構成に従って受信リクエストをいずれかのインスタンスにルーティングします。
Nginx はどのようにして優れているのでしょうか?
Nginx がどのようにして非常に高い信頼性と速度で大規模なリクエストをサポートできるかを詳しく説明した Nginx の記事「Nginx アーキテクチャ」を一読することをお勧めします。
つまり、Nginx にはマスター プロセスと多数のワーカー プロセスがあります。キャッシュ ローダーやキャッシュ マネージャーなどのヘルパー プロセスもあります。マスタープロセスとワーカープロセスはすべての重い作業を実行します。
- マスター プロセス: 構成を管理し、子プロセスを生成します。
- キャッシュ ローダー/マネージャー: 最小限のリソースでキャッシュのロードとプルーニングを処理します。
- ワーカー プロセス: 接続、ディスク I/O、およびアップストリーム通信を管理し、ノンブロッキングかつ独立して実行します。
ワーカー プロセスは複数の接続をノンブロッキングで処理し、コンテキストの切り替えを減らします。これらはシングルスレッドで独立して実行され、キャッシュやセッション データなどの共有リソースに共有メモリを使用します。このアーキテクチャは、Nginx がコンテキスト スイッチの数を減らし、ブロッキング マルチプロセス アーキテクチャよりも高速に速度を上げるのに役立ちます。
これからインスピレーションを得て、マスター プロセスとワーカー プロセスの同じ概念を使用し、ワーカー プロセスごとに数千の接続を処理できる独自のイベント駆動型リバース プロキシ サーバーを実装します。
プロジェクトのアーキテクチャ
リバース プロキシの実装は、次の重要な設計原則に従っています。
- 構成主導: すべてのプロキシの動作は YAML 構成ファイルで定義されるため、ルーティング ルールを簡単に変更できます。
- 型安全性: TypeScript と Zod スキーマにより、構成の有効性と実行時の型安全性が保証されます。
- スケーラビリティ: Node.js クラスター モジュールにより、複数の CPU コアを利用してパフォーマンスを向上させることができます。
- モジュール性: 構成、サーバー ロジック、スキーマ検証のための個別のモジュールによる懸念の明確な分離。
プロジェクトの構造
├── config.yaml # Server configuration ├── src/ │ ├── config-schema.ts # Configuration validation schemas │ ├── config.ts # Configuration parsing logic │ ├── index.ts # Application entry point │ ├── server-schema.ts # Server message schemas │ └── server.ts # Core server implementation └── tsconfig.json # TypeScript configuration
主要コンポーネント
- config.yaml: ポート、ワーカー プロセス、アップストリーム サーバー、ヘッダー、ルーティング ルールなどのサーバーの構成を定義します。
- config-schema.ts: Zod ライブラリを使用して検証スキーマを定義し、構成構造が正しいことを確認します。
- server-schema.ts: マスター プロセスとワーカー プロセス間で交換されるメッセージ形式を指定します。
- config.ts: YAML 構成ファイルを解析および検証するための関数を提供します。
- server.ts: クラスターのセットアップ、HTTP 処理、リクエスト転送などのリバース プロキシ サーバー ロジックを実装します。
- index.ts: エントリ ポイントとして機能し、コマンドライン オプションを解析してサーバーを開始します。
構成管理
設定システムは YAML を使用します。仕組みは次のとおりです:
server: listen: 8080 # Port the server listens on. workers: 2 # Number of worker processes to handle requests. upstreams: # Define upstream servers (backend targets). - id: jsonplaceholder url: jsonplaceholder.typicode.com - id: dummy url: dummyjson.com headers: # Custom headers added to proxied requests. - key: x-forward-for value: $ip # Adds the client IP to the forwarded request. - key: Authorization value: Bearer xyz # Adds an authorization token to requests. rules: # Define routing rules for incoming requests. - path: /test upstreams: - dummy # Routes requests to "/test" to the "dummy" upstream. - path: / upstreams: - jsonplaceholder # Routes all other requests to "jsonplaceholder".
受信リクエストはルールに照らして評価されます。リバース プロキシは、パスに基づいて、リクエストを転送する上流サーバーを決定します。
構成の検証 (config-schema.ts)
Zod を使用して、構成検証のための厳密なスキーマを定義します。
import { z } from "zod"; const upstreamSchema = z.object({ id: z.string(), url: z.string(), }); const headerSchema = z.object({ key: z.string(), value: z.string(), }); const ruleSchema = z.object({ path: z.string(), upstreams: z.array(z.string()), }); const serverSchema = z.object({ listen: z.number(), workers: z.number().optional(), upstreams: z.array(upstreamSchema), headers: z.array(headerSchema).optional(), rules: z.array(ruleSchema), }); export const rootConfigSchema = z.object({ server: serverSchema, }); export type ConfigSchemaType = z.infer<typeof rootconfigschema>; </typeof>
構成の解析と検証 (config.ts)
config.ts モジュールは、構成ファイルを解析および検証するためのユーティリティ関数を提供します。
import fs from "node:fs/promises"; import { parse } from "yaml"; import { rootConfigSchema } from "./config-schema"; export async function parseYAMLConfig(filepath: string) { const configFileContent = await fs.readFile(filepath, "utf8"); const configParsed = parse(configFileContent); return JSON.stringify(configParsed); } export async function validateConfig(config: string) { const validatedConfig = await rootConfigSchema.parseAsync( JSON.parse(config) ); return validatedConfig; }
リバース プロキシ サーバー ロジック (server.ts)
サーバーは、スケーラビリティのために Node.js クラスター モジュールを利用し、リクエストの処理のために http モジュールを利用します。マスター プロセスはリクエストをワーカー プロセスに分散し、ワーカー プロセスがリクエストを上流のサーバーに転送します。 server.ts ファイルを詳しく調べてみましょう。このファイルには、リバース プロキシ サーバーのコア ロジックが含まれています。各コンポーネントを分析し、それらがどのように連携してスケーラブルなプロキシ サーバーを作成するかを理解します。
サーバー実装は、Node.js のクラスター モジュールを使用したマスターワーカー アーキテクチャに従います。この設計により、次のことが可能になります。
- 複数の CPU コアを利用する
- リクエストを同時に処理します
- 高可用性を維持する
- リクエスト処理を分離する
-
マスタープロセス:
- ワーカープロセスを作成します
- 受信したリクエストをワーカー全体に分散します
- ワーカープールを管理します
- ワーカーのクラッシュと再起動を処理します
-
ワーカープロセス:
- 個々の HTTP リクエストを処理する
- リクエストをルーティング ルールと照合します
- リクエストを上流サーバーに転送します
- 応答を処理してクライアントに送り返す
マスタープロセスのセットアップ
├── config.yaml # Server configuration ├── src/ │ ├── config-schema.ts # Configuration validation schemas │ ├── config.ts # Configuration parsing logic │ ├── index.ts # Application entry point │ ├── server-schema.ts # Server message schemas │ └── server.ts # Core server implementation └── tsconfig.json # TypeScript configuration
マスター プロセスはワーカーのプールを作成し、環境変数を通じて各ワーカーに設定を渡します。これにより、すべてのワーカーが同じ構成にアクセスできるようになります。
リクエスト配布
server: listen: 8080 # Port the server listens on. workers: 2 # Number of worker processes to handle requests. upstreams: # Define upstream servers (backend targets). - id: jsonplaceholder url: jsonplaceholder.typicode.com - id: dummy url: dummyjson.com headers: # Custom headers added to proxied requests. - key: x-forward-for value: $ip # Adds the client IP to the forwarded request. - key: Authorization value: Bearer xyz # Adds an authorization token to requests. rules: # Define routing rules for incoming requests. - path: /test upstreams: - dummy # Routes requests to "/test" to the "dummy" upstream. - path: / upstreams: - jsonplaceholder # Routes all other requests to "jsonplaceholder".
マスター プロセスは、単純なランダム分散戦略を使用してリクエストをワーカーに割り当てます。このアプローチは、ラウンドロビン アルゴリズムや最小接続アルゴリズムほど洗練されていませんが、ほとんどのユース ケースで適切な負荷分散を提供します。リクエスト分散ロジック:
- プールからワーカーをランダムに選択します
- ワーカー間でバランスの取れたワークロードを作成します
- 従業員が不在の可能性がある特殊なケースに対処します
ワーカープロセスリクエストロジック
各ワーカーはメッセージをリッスンし、リクエストをルーティング ルールと照合して、適切な上流サーバーに転送します。
import { z } from "zod"; const upstreamSchema = z.object({ id: z.string(), url: z.string(), }); const headerSchema = z.object({ key: z.string(), value: z.string(), }); const ruleSchema = z.object({ path: z.string(), upstreams: z.array(z.string()), }); const serverSchema = z.object({ listen: z.number(), workers: z.number().optional(), upstreams: z.array(upstreamSchema), headers: z.array(headerSchema).optional(), rules: z.array(ruleSchema), }); export const rootConfigSchema = z.object({ server: serverSchema, }); export type ConfigSchemaType = z.infer<typeof rootconfigschema>; </typeof>
マスター プロセスは、Node.js IPC (プロセス間通信) を使用して、必要なすべてのリクエスト情報を含む標準化されたメッセージ ペイロードを構築し、Zod スキーマを使用してメッセージ構造を検証することにより、ワーカーと通信します。
ワーカーは実際のリクエストの処理とプロキシを処理します。各ワーカー:
- 環境変数から設定をロードします
- Zod スキーマを使用して構成を検証します
- 構成の独自のコピーを維持します
ワーカーは次の方法で上流サーバーを選択します:
- ルールから適切なアップストリーム ID を見つける
- アップストリームサーバー構成の検索
- 上流サーバーの存在を検証しています
リクエスト転送メカニズム:
- 上流サーバーへの新しい HTTP リクエストを作成します
- 応答データをストリーミングします
- レスポンスボディを集約します
- マスタープロセスに応答を送り返します
サーバーの実行
サーバーを実行するには、次の手順に従います:
- プロジェクトをビルドします:
import fs from "node:fs/promises"; import { parse } from "yaml"; import { rootConfigSchema } from "./config-schema"; export async function parseYAMLConfig(filepath: string) { const configFileContent = await fs.readFile(filepath, "utf8"); const configParsed = parse(configFileContent); return JSON.stringify(configParsed); } export async function validateConfig(config: string) { const validatedConfig = await rootConfigSchema.parseAsync( JSON.parse(config) ); return validatedConfig; }
- サーバーを起動します:
if (cluster.isPrimary) { console.log("Master Process is up ?"); for (let i = 0; i { const index = Math.floor(Math.random() * WORKER_POOL.length); const worker = WORKER_POOL.at(index); if (!worker) throw new Error("Worker not found."); const payload: WorkerMessageSchemaType = { requestType: "HTTP", headers: req.headers, body: null, url: ${req.url}, }; worker.send(JSON.stringify(payload)); worker.once("message", async (workerReply: string) => { const reply = await workerMessageReplySchema.parseAsync( JSON.parse(workerReply) ); if (reply.errorCode) { res.writeHead(parseInt(reply.errorCode)); res.end(reply.error); } else { res.writeHead(200); res.end(reply.data); } }); }); server.listen(port, () => { console.log(Reverse Proxy listening on port: ${port}); }); }
- 開発モード:
const server = http.createServer(function (req, res) { const index = Math.floor(Math.random() * WORKER_POOL.length); const worker = WORKER_POOL.at(index); const payload: WorkerMessageSchemaType = { requestType: "HTTP", headers: req.headers, body: null, url: ${req.url}, }; worker.send(JSON.stringify(payload)); });
上のスクリーンショットでは、1 つのマスター ノードと 2 つのワーカー プロセスが実行されていることがわかります。リバース プロキシ サーバーはポート 8080 でリッスンしています。
config.yaml ファイルには、jsonplaceholder と dummy という 2 つの上流サーバーを記述します。サーバーに届くすべてのリクエストを jsonplaceholder にルーティングしたい場合は、ルールを次のようにします:
├── config.yaml # Server configuration ├── src/ │ ├── config-schema.ts # Configuration validation schemas │ ├── config.ts # Configuration parsing logic │ ├── index.ts # Application entry point │ ├── server-schema.ts # Server message schemas │ └── server.ts # Core server implementation └── tsconfig.json # TypeScript configuration
同様に、/test エンドポイントへのリクエストをダミーの上流サーバーにルーティングする必要がある場合は、ルールを次のように設定します。
server: listen: 8080 # Port the server listens on. workers: 2 # Number of worker processes to handle requests. upstreams: # Define upstream servers (backend targets). - id: jsonplaceholder url: jsonplaceholder.typicode.com - id: dummy url: dummyjson.com headers: # Custom headers added to proxied requests. - key: x-forward-for value: $ip # Adds the client IP to the forwarded request. - key: Authorization value: Bearer xyz # Adds an authorization token to requests. rules: # Define routing rules for incoming requests. - path: /test upstreams: - dummy # Routes requests to "/test" to the "dummy" upstream. - path: / upstreams: - jsonplaceholder # Routes all other requests to "jsonplaceholder".
これをテストしてみましょう!
うわー、すごいですね! localhost:8080 に移動していますが、応答として、jsonplaceholder.typicode.com のホームページを受信したことがわかります。エンド ユーザーは、別のサーバーからの応答が表示されていることさえ知りません。このため、リバース プロキシ サーバーが重要です。同じコードを実行する複数のサーバーがあり、それらのすべてのポートをエンド ユーザーに公開したくない場合は、抽象化レイヤーとしてリバース プロキシを使用します。ユーザーは、非常に堅牢で高速なサーバーであるリバース プロキシ サーバーにアクセスし、要求をルーティングするサーバーを決定します。
ここで localhost:8080/todos にアクセスして、何が起こるか見てみましょう。
私たちのリクエストは再び jsonplaceholder サーバーにリバース プロキシされ、解決された URL: jsonplaceholder.typicode.com/todos から JSON レスポンスを受け取りました。
通信の流れ
完全なリクエスト フローを視覚化してみましょう:
クライアントがリクエストを送信 → マスタープロセス
マスタープロセス → 選択されたワーカー
ワーカー → 上流サーバー
上流サーバー → ワーカー
ワーカー → マスタープロセス
マスタープロセス → クライアント
パフォーマンスに関する考慮事項
マルチプロセス アーキテクチャにより、いくつかのパフォーマンス上の利点が得られます。
- CPU 使用率: ワーカー プロセスは、利用可能なハードウェア リソースを利用して、さまざまな CPU コアで実行できます。
- プロセスの分離: 1 つのワーカーでクラッシュしても他のワーカーに影響を与えず、信頼性が向上します。
- 負荷分散: リクエストをランダムに分散することで、単一のワーカーが過負荷になるのを防ぎます。
今後の改善点
現在の実装は機能しますが、次のように拡張できます。
- 負荷分散の改善: ラウンドロビンや最小接続など、より高度なアルゴリズムを実装します。
- ヘルスチェック: 上流サーバーの定期的なヘルスチェックを追加します。
- キャッシュ: 応答キャッシュを実装して、上流サーバーの負荷を軽減します。
- メトリクス: 監視用にプロメテウススタイルのメトリクスを追加します。
- WebSocket サポート: WebSocket 接続を処理するためにプロキシを拡張します。
- HTTPS サポート: SSL/TLS 終了機能を追加します。
まとめ
リバース プロキシ サーバーをゼロから構築するのは、最初は恐ろしいように思えるかもしれませんが、これまで調べてきたように、やりがいのある経験です。 Node.js クラスター、TypeScript、YAML ベースの構成管理を組み合わせることで、Nginx からインスピレーションを得たスケーラブルで効率的なシステムを作成しました。
この実装にはまだ改善の余地があり、負荷分散、キャッシュ、WebSocket サポートの改善などは検討すべきアイデアのほんの一部です。しかし、現在の設計は、さらなる実験と拡張のための強力な基盤を確立します。ここまで進めていただければ、リバース プロキシについてさらに深く掘り下げたり、ニーズに合わせたカスタム ソリューションの構築を開始したりできるようになりました。
私の作品につながりたい場合、または私の作品をもっと見たい場合は、私の GitHub や LinkedIn をチェックしてください。
このプロジェクトのリポジトリはここにあります。
ご意見、フィードバック、改善のアイデアをお聞かせください。読んでいただきありがとうございます。コーディングを楽しんでください。 ?
以上がNode.js と TypeScript を使用して Nginx のようなスケーラブルなリバース プロキシ サーバーを構築するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

JavaScript文字列置換法とFAQの詳細な説明 この記事では、javaScriptの文字列文字を置き換える2つの方法について説明します:内部JavaScriptコードとWebページの内部HTML。 JavaScriptコード内の文字列を交換します 最も直接的な方法は、置換()メソッドを使用することです。 str = str.replace( "find"、 "置換"); この方法は、最初の一致のみを置き換えます。すべての一致を置き換えるには、正規表現を使用して、グローバルフラグGを追加します。 str = str.replace(/fi

単純なJavaScript関数は、日付が有効かどうかを確認するために使用されます。 関数isvaliddate(s){ var bits = s.split( '/'); var d = new Date(bits [2] '/' bits [1] '/'ビット[0]); return !!(d &&(d.getmonth()1)== bits [1] && d.getdate()== number(bits [0])); } //テスト var

この記事では、jQueryを使用して、DOM要素の内側のマージン値とマージン値、特に外側の縁と要素の内側の縁の特定の位置を取得して設定する方法について説明します。 CSSを使用して要素の内側と外側の縁を設定することは可能ですが、正確な値を取得するのは難しい場合があります。 // 設定 $( "div.header")。css( "margin"、 "10px"); $( "div.header")。css( "padding"、 "10px"); このコードはそうだと思うかもしれません

この記事では、10個の例外的なjQueryタブとアコーディオンについて説明します。 タブとアコーディオンの重要な違いは、コンテンツパネルの表示方法と非表示にあります。これらの10の例を掘り下げましょう。 関連記事:10 jQueryタブプラグイン

ウェブサイトのダイナミズムと視覚的な魅力を高めるために、10の例外的なjQueryプラグインを発見してください!このキュレーションされたコレクションは、画像アニメーションからインタラクティブなギャラリーまで、多様な機能を提供します。これらの強力なツールを探りましょう。 関連投稿: 1

HTTP-Consoleは、HTTPコマンドを実行するためのコマンドラインインターフェイスを提供するノードモジュールです。 Webサーバー、Web Servに対して作成されているかどうかに関係なく、HTTPリクエストで何が起こっているかをデバッグして正確に確認するのに最適です

このチュートリアルでは、カスタムGoogle検索APIをブログまたはWebサイトに統合する方法を示し、標準のWordPressテーマ検索関数よりも洗練された検索エクスペリエンスを提供します。 驚くほど簡単です!検索をyに制限することができます

次のjQueryコードスニペットを使用して、Divコンテンツがコンテナ要素領域を超えたときにスクロールバーを追加できます。 (デモンストレーションはありません、それを直接firebugにコピーしてください) // d =ドキュメント // w =ウィンドウ // $ = jQuery var contentarea = $(this)、 wintop = contentarea.scrolltop()、 docheight = $(d).height()、 winheight = $(w).height()、 divheight = $( '#c


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

SublimeText3 中国語版
中国語版、とても使いやすい

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

MantisBT
Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

ドリームウィーバー CS6
ビジュアル Web 開発ツール

DVWA
Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、
