1725065198
2024-08-30 06:42:16
Web サーバーが内部からどのように見えるか考えたことはありますか?自分でそれを作成することを夢見たことがありますか?あなたは正しい場所にいます!
PandApache3 の開発に特化したこの最初の技術記事へようこそ。
この記事と次の記事は、Apache2 と競合する準備ができている軽量で最小限の Web サーバーである PandApache3 の内部動作を説明することを目的としています (ここでは、私たちは 29 年金で退職する予定です)。
これらの記事はドキュメントではありません。 PandApache3 が進化しても更新されません。むしろ、彼らの目標は、コードと設計の選択肢を共有して説明することです。
プロジェクトの一般的な理解を促進するために、コードの特定の部分がよりわかりやすく簡略化されます。
次に進む前に、PandApache3 をご存知ですか?必要に応じて、この以前の記事をリストして詳細を学ぶことができます。 PandApache3、Apache キラー
しかし、どこから始めればよいでしょうか?プロジェクトをゼロから説明するのは常に困難です。初心者が混乱する可能性のある詳細に迷わないように、開始するために重要な要素に焦点を当てます。まず、サービスの最も基本的なタスクである起動について説明します。
テイクオフ: PandApache3 の最初のステップ
PandApache3 はサービスの開始時に何をしますか? HTTP リクエストを受け入れてポートをリッスンする前に、いくつかのタスクを実行する必要があります。このパートでは、サービスへの最初の接続が確立される前に実行されるアクションに焦点を当てます。
私たちの起動メソッドは次のように呼ばれます StartServerAsync、これはサーバーの起動時に呼び出される最初のメソッドです。
public static async Task StartServerAsync()
{
Logger.Initialize();
Server.STATUS = "PandApache3 is starting";
Logger.LogInfo($"{Server.STATUS}");
ServerConfiguration.Instance.ReloadConfiguration();
_ConnectionManager = new ConnectionManager();
TerminalMiddleware terminalMiddleware = new TerminalMiddleware();
RoutingMiddleware routingMiddleware = new RoutingMiddleware(terminalMiddleware.InvokeAsync, fileManager);
LoggerMiddleware loggerMiddleware = new LoggerMiddleware(authenticationMiddleware.InvokeAsync);
FuncHttpContext, Task> pipeline = loggerMiddleware.InvokeAsync;
await _ConnectionManagerWeb.StartAsync(pipeline);
}
最初のステップは、ロガーを初期化することです。ロガーは、すべてのサーバーのアクション、エラー、メッセージを記録する重要なクラスです。これは、3 行目に「開始中」というステータスが記録されているように、起こり得る問題を報告する準備ができている必要があるため、起動中に特に重要です。
ログ情報は、選択した構成に応じて 2 つの場所で入手できます。
- ログ ファイル内。これはサービスの古典的な構成です。 PandApache3.log ファイルが作成され、各イベントがそこに記録されます。
- コンソール内。ログ ファイルに加えて、またはログ ファイルの代わりとして、コンソール出力またはターミナルでログを直接確認する場合に非常に便利です。
これら 2 つのオプションを組み合わせて、ニーズに応じてログの管理方法を選択することもできます。
私たちの間に
NoLog を選択したり、ファイルではなくコンソールにのみログを記録したりするのはなぜですか?一見すると、ログをファイルに保存しないのは奇妙に思えるかもしれません。ただし、この決定は、PaaS に優しいように設計された PandApache3 にとって戦略的です。数千のインスタンスを含むサービスとしてのプラットフォーム (PaaS) を管理する場合、サーバーにログを保存すると、アクセシビリティとディスク容量の問題が発生する可能性があります。したがって、アプリケーションによって生成されたログをコンソールから ADX や Elastic Search などの専用システムにリダイレクトする方が賢明です。
このアプローチにより、アプリケーション開発中にフィードバックを迅速に取得することもできます。
最後に、PandApache3 で NoLog を使用できる (ファイルとコンソールの両方でのログの書き込みを無効にする) ことは、サービスによって提供される柔軟性の直接的な結果です。
セットアップの詳細:

他のサービスと同様に、PandApache3 は構成可能です。したがって、ログを初期化した後、構成のロードが 2 番目に実行する必須の手順になります。この設定はマシン上の PandApache3.conf ファイルとして利用でき、PandApache3 の動作と機能において重要な役割を果たします。
public void ReloadConfiguration()
{
string fullPath = Path.Combine(_configurationPath, "PandApache3.conf");
if (!File.Exists(fullPath))
{
throw new FileNotFoundException("The configuration file didn't exist", fullPath);
}
try
{
foreach (var line in File.ReadLines(fullPath))
{
if (string.IsNullOrWhiteSpace(line) || line.Trim().StartsWith("#"))
{
continue;
}
else
{
var parts = line.Split(new[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 2)
{
var key = parts[0].Trim();
var value = parts[1].Trim();
MapConfiguration(key, value);
}
}
}
Logger.LogInfo("Configuration reloaded");
}
catch (Exception ex)
{
throw new Exception($"Error during configuration reload: {ex.Message}");
}
}
public void MapConfiguration(string key, string value)
{
var actionMap = new Dictionarystring, Actionstring>>
{
["servername"] = v => ServerName = v,
["serverip"] = v =>
{
if (IPAddress.TryParse(v, out var parsedIPAddress))
ServerIP = parsedIPAddress;
else
Logger.LogWarning("Server IP invalid");
},
["serverport"] = v => TrySetIntValue(v, val => ServerPort = val, "Server port invalid"),
};
if (actionMap.TryGetValue(key.ToLower(), out var action))
{
action(value);
}
else
{
Logger.LogWarning($"Unknown configuration key: {key}");
}
}
この機能 ReloadConfigurationPandApache3.conf ファイルの各行 (コメントを除く) をロードし、各キーを値に関連付けます。次に、関数 MapConfiguration(憲法のように 🤓) 辞書を持っています (actionMap) これにより、値をクラス変数に関連付ける前に、各キーを実行するアクションにマップできるようになります。
たとえば次の行の場合: ["servername"] = v => ServerName = v,
辞書キーは servername 関連するアクションは v => ServerName = v、 または v 値を表します。アクションは、この値をプロパティに割り当てるラムダ関数です。 ServerName。
必要な情報が揃ったので、サーバーは指定された仕様に従って起動し、問題が発生した場合にはフィードバックを送信する準備が整いました。次のステップである接続管理に進みましょう。
私たちの間に
構成内のパラメーター エラーはブロックされていません。アラートは発行されますが、サービスは引き続き開始されます。構成ファイルがない場合は、アプリケーションのデフォルト設定が使用されます。
いつも私たちの間に
JSON や YAML ではなくテキスト形式の .conf ファイルを選択したのはなぜですか?まずその単純さです。優れたエディターがないと問題が発生する可能性がある JSON や YAML の編集とは異なり、最初の構成ファイルをテキスト形式で記述することほど簡単なことはありません。さらに、テキスト形式はコメントを受け入れるため、構成ファイルの自己文書化に非常に便利です。将来的には、構成を管理するために複数のファイル形式をサポートすることも除外されません。
PandApache3 の核心: 接続マネージャー

PandApache3 サーバーの中心は、オブジェクトで表される接続マネージャーにあります。 ConnectionManager。
_ConnectionManager = new ConnectionManager();
この比較的単純なオブジェクトには 2 つの重要な属性があります。 TcpListener そして pipeline。
public TcpListener Listener { get; set; }
private FuncHttpContext, Task> _pipeline;
の TcpListenerは、クライアントが TCP プロトコル経由でサーバーに接続できるようにする基本コンポーネントです。私たちの変数に関しては _pipeline、これは HTTP コンテキストをパラメータとして受け取る非同期関数を表します (HttpContext) タスクを返します (Task)。図で表すと、パイプラインは、各 HTTP リクエストで実行する一連のアクションです。各アクションは、いわゆるミドルウェアによって実行されます。
正確には、コードの残りの部分で、受信した各 HTTP リクエストに使用するミドルウェアをセットアップします。
TerminalMiddleware terminalMiddleware = new TerminalMiddleware();
RoutingMiddleware routingMiddleware = new RoutingMiddleware(terminalMiddleware.InvokeAsync);
LoggerMiddleware loggerMiddleware = new LoggerMiddleware(authenticationMiddleware.InvokeAsync);
FuncHttpContext, Task> pipeline = loggerMiddleware.InvokeAsync;
ここには 3 つのミドルウェアがあります。
- ターミナルミドルウェア
- ルーティングミドルウェア
- ロガーミドルウェア
各ミドルウェアは、明確に定義されたチェーン内の次のミドルウェアを呼び出します (ロガーがルーティングを呼び出し、次にルーティングがターミナルを呼び出します)。このミドルウェア チェーン (パイプライン) は接続マネージャー (ConnectionManager)。
すべてが整ったので、接続マネージャーを開始できます。
await _ConnectionManagerWeb.StartAsync(pipeline);
機能 StartAsync単に設定するだけです TcpListener設定で定義されているポートと IP アドレスをリッスンしてから、オンにします。
public async Task StartAsync(FuncHttpContext, Task> pipeline)
{
Listener = new TcpListener(ServerConfiguration.Instance.ServerIP, ServerConfiguration.Instance.ServerPort);
Logger.LogInfo($"Web server listening on {ServerConfiguration.Instance.ServerIP}:{ServerConfiguration.Instance.ServerPort}");
Listener.Start();
_pipeline = pipeline;
}
ほら、これでサーバーが起動し、受信接続を受信する準備が整いました。
私たちの間に
ミドルウェアが何をするかは、現時点では重要ではありません。覚えておく必要があるのは、TCP リスナーで受信した接続の処理を担当する ConnectionManager が、すべての接続をこのミドルウェア チェーンをこの順序で通過させることです。
ただし、名前は非常にわかりやすいので、各ミドルウェアの役割を推測することができます。
- ロガー: 受信リクエストをログに記録します。
- ルーティング: リクエストを正しいリソースに転送します。
- ターミナル: チェーン内の最後のミドルウェア。特に何も行いませんが、存在します。
いつも私たちの間に
ミドルウェアを通過するリクエストは、往路だけでなく復路 (逆方向) にも通過します。この例では、これは、リクエストが最初に最初のミドルウェアによってログに記録され、その後、取得された応答がチェーンの最後になった同じミドルウェアによってもログに記録されることを意味します。
PandApache3 の舞台裏を一緒に探索していただき、本当にありがとうございました。このプロジェクトを発展させるには、あなたの考えとサポートが不可欠です。 🚀
以下のコメント欄でアイデアや感想をお気軽に共有してください。あなたとチャットするのが待ちきれません!
Twitter で私の冒険をフォローしてください @pykpyky すべてのニュースを最新の状態に保つために。
完全なプロジェクトを見つけることもできます。 GitHub のライブコーディングセッションに参加してください ツイッチ エキサイティングでインタラクティブなセッションのために。スクリーンの向こうでお会いしましょう!
#PandApache3 #の詳細 #ランスメント #コード