November 2015
Volume 30 Number 12
ASP.NET - ASP.NET を高性能ファイル ダウンローダーとして使用する
速度が遅く、障害が発生しやすい接続は、大きなファイルのダウンロードにとって長年悩みの種です。空港のコンコースで頼りない WiFi に接続し、長時間のフライトでのプレゼンテーション作成作業に備えてメディアを集める場面や、アフリカのサバンナで太陽光発電の送水ポンプ用に衛星中継を介して大きなインストール ファイルのダウンロードを試みる場面を想像してみててください。どちらも、大きなファイルのダウンロードに失敗すると、時間の損失、生産性の浪費、任務成功の危機につながります。
しかし、方法はあります。今回は、接続状態が悪く、大きなファイルの転送中にオフラインになりやすいことが原因で失敗したダウンロードの再開と続行の問題に対処するユーティリティを作成する方法を取り上げます。
背景情報
以前から、非常に単純で使いやすいクライアント プログラムを使用して (または単純に Web ブラウザをクライアントに使用して)、既存の IIS Web サーバーに簡単に追加できるシンプルなファイル ダウンローダー ユーティリティを作成したいと考えていました。
IIS Web サーバーは、拡張性の高いエンタープライズ レベルの Web サーバーであることが既に証明されており、長年、ブラウザーにファイルを提供しています。多数の HTTP Web 要求を同時に並列処理する IIS Web サーバーの能力を活用し、それをファイルのダウンロード (コピー) に当てはめるというのが基本的な考え方です。
実際、サイズの大きなファイルをダウンロードできるファイル ダウンローダー ユーティリティは、世界中のユーザーが求めています。世界中となると、ネットワークのリンク速度が遅く、障害が発生しやすい人里離れた地域に住んでいるユーザーも対象になります。人里離れた地域のユーザーは現在もモデム リンクや障害が発生しやすい衛星中継を使用している可能性があります。そのようなリンクには、不定期にオフラインになったり、断続的にオンラインとオフラインを繰り返すものがあります。それを考慮して、ユーティリティは、ファイルのダウンロードに失敗した部分だけを再試行できる機能を備えた非常に回復力の高いものにする必要があります。低速リンクでの大きなファイルのダウンロードのためにユーザーが徹夜するようではいけません。また、ネットワーク リンクに小さな不具合があったときに、ダウンロードのプロセス全体を最初からやり直すことも考えられません。さらに、ダウンロードする大きなファイルをサーバー メモリにバッファリングしないようにして、サーバー メモリの使用量を最小限に抑える必要があります。そうしないと、多くユーザーが同時にファイルをダウンロードすると、メモリ使用量が増大してサーバーに障害が発生する可能性があります。
逆に、クライアントとサーバーがどちらも複数の CPU とネットワーク カードを備えたハイエンド コンピューターで、信頼性の高い高速ネットワーク リンクを使用できる幸運なユーザーの場合は、複数のスレッドと複数の接続を使用してファイルをダウンロードできるようにします。そのとき、ファイルの複数のチャンクを、すべてのハードウェア リソースを使用して同時かつ並列にダウンロードできるようにしながら、同時にサーバー メモリの使用量を最小限に抑えます。
簡単に言えば、シンプルでマルチスレッドと並列処理に対応し、メモリ使用量が少ないファイル ダウンロード ユーティリティを作成する必要があります。そのため、このユーティリティでは、ファイルをチャンクに分割し、個別のスレッドで各チャンクをダウンロードして、ダウンロードに失敗したチャンクのみを再試行できるようにします。
今回のサンプル プロジェクトでは、ファイル ダウンロード ユーティリティのコードを含め、初歩的な基盤インフラストラクチャを提供しています。このプロジェクトは拡張可能なので、ニーズに応じて洗練していくことができます。
サンプル プロジェクトの概要
基本的には、DownloadHandler.dll が既存の IIS Web サーバーをマルチスレッド ファイル ダウンローダーに変換します。このダウンローダーでは、スタンドアロン実行可能クライアント (FileDownloader.exe) からの単純な URL を使用して、ファイルを複数のチャンクに分けて並列にダウンロードします (図 1 参照)。パラメーター (chunksize=5242880) は省略可能です。指定しなければ、既定で、ファイル全体が 1 つのチャンクとしてダウンロードされます。図 2 と 図 3 は、ファイルの失敗した部分のみを成功するまで繰り返し再試行する様子を示しています。他のほとんどのファイル ダウンロード ソフトウェアとは異なり、ダウンロード全体を最初から完全にやり直す必要はありません。
図 1 DownloadHandler.dll の処理フローの概要 (FileDownloader.exe をクライアントに使用)
図 2 スタンドアロン実行可能ダウンロード クライアント (失敗したチャンクを含む)
図 3 スタンドアロン実行可能ダウンロード クライアント (再試行後)
図 1 は、DownloadHandler.dll と FileDownloader.exe の設計概要です。サーバー コンピューターのハード ドライブ上のファイルのチャンクが DownloadHandler.dll と FileDownloader.exe を経由して、クライアント コンピューターのハード ドライブ上のファイルに保存されるという処理フローになっています。このプロセスに関係する HTTP プロトコル ヘッダーも示しています。
図 1 では、FileDownloader.exe が単純な URL を使用してサーバーを呼び出すことで、ファイルのダウンロードを開始します。この URL には、ダウンロードするファイルの名前を URL クエリ文字列パラメーターとして含めます (file=file.txt)。内部的には、HTTP メソッド (HEAD) を使用して、最初にサーバーから応答ヘッダーのみが返されるようにしています。応答ヘッダーの 1 つには合計ファイル サイズが含まれます。その後、クライアントは Parallel.ForEach 構文を使用して反復処理を行い、パラメーター (chunksize=5242880) のチャンク サイズを基に合計ファイル サイズをチャンク (バイト範囲) に分割します。反復ごとに、Parallel.ForEach 構文は、関連するバイト範囲を渡して、処理メソッドを個別のスレッドで実行します。処理メソッド内では、クライアントは同じ URL を使用してサーバーに対して HttpWebRequest 呼び出しを行います。内部的には、その処理メソッドに指定されたバイト範囲 (Range: bytes=0-5242880、Range: bytes=5242880-10485760 など) を含めた HTTP 要求ヘッダーを追加します。
サーバー コンピューターでは、IHttpAsyncHandler インターフェイス (System.Web.IHttpAsyncHandler) の実装が、個別のスレッドで各要求を処理し、HttpResponse.TransmitFile メソッドを実行して、要求されたバイト範囲を、サーバー コンピューターのファイルからネットワーク ストリームに (明示的なバッファリングを行わずに) 直接書き込みます。そのため、サーバーでのメモリへの影響はほとんど生じません。サーバーは HTTP ステータス コード 206 (部分的内容) を返します。内部的には、返すバイト範囲を識別する HTTP 応答ヘッダー (Content-Range: bytes 0-5242880/26214400、Content-Range: bytes 5242880-10485760/26214400 など) を追加します。各スレッドはクライアント コンピューターで HTTP 応答を受け取ると、応答として返されたバイト列を、HTTP 応答ヘッダー (Content-Range) によって識別して、クライアント コンピューターのハード ドライブ上のファイルの該当部分に書き込みます。スレッドは、非同期オーバーラップ ファイル I/O を使用します (I/O 要求パケットがカーネル モード ドライバーにディスパッチされてファイル書き込み操作が完了する前に Windows I/O マネージャーによって I/O 要求がシリアル化されないようにするため)。ユーザーモードの複数のスレッドがすべてファイル書き込みを行い、非同期オーバーラップ I/O 用にファイルを開けない場合、要求をシリアル化します。カーネル モード ドライバーは一度に 1 つの要求しか受け取りません。非同期オーバーラップ I/O の詳細については、ハードウェア デベロッパー センター サイトの「ドライバーで同時に複数の I/O 要求を処理できるようにする」(bit.ly/1NIaqxP、英語) および「非同期 I/O のサポート」(bit.ly/1NIaKMW、英語) を参照してください。
IHttpAsyncHandler に非同期機能を実装するには、オーバーラップ I/O 構造体を I/O 完了ポートに手動で送信します。このようにすると、CLR ThreadPool では、完了ポート スレッドのオーバーラップ構造体により提供される完了デリゲートが実行されます。これらは、ほとんどの組み込み async メソッドで使用される完了ポート スレッドと同じです。一般に、ほとんどの I/O 中心の処理では新しい組み込み async メソッドを使用するのがベストですが、今回は、大きなファイルをサーバー メモリに明示的にバッファリングしないで転送するという傑出した機能を実現するために、HttpResponse.TransmitFile 関数を使用する必要がありました。これはすばらしい機能です。
Parallel.ForEach は主に CPU 中心の処理に使用します。Parallel.ForEach には実行をブロックする性質があるため、本来、サーバー実装で使用すべきではありません。IIS が着信要求へのサービス提供に使用するスレッドがなくなってしまわないように、処理を CLR ThreadPool の通常のワーカー スレッドではなく、CLR ThreadPool の完了ポート スレッドにオフロードします。また、より効率的に完了ポートで処理する方法を使用すると、サーバーでのスレッドの使用がある程度抑えられます。サンプル プロジェクトのコードの IOThread クラス上部のコメント セクションに詳しい説明と図を用意し、CLR ThreadPool の完了ポート スレッドとワーカー スレッドの違いを解説しています。このユーティリティの主な目的は、ユーザーを何百万人もの規模にすることではありません。そのため、サイズの大きなファイルを転送する際には、必要に応じて追加のサーバー スレッドを使って HttpResponse.TransmitFile 関数を実行し、サーバーで大量のファイル転送に必要なメモリを節約できます。基本的に、サーバーでのメモリ使用量が非常に少ない HttpResponse.TransmitFile 関数を使用するために、(スレッドを使用せず組み込みの async メソッドを使用するのではなく) サーバーのスレッドを追加で使用することで、スケーラビリティを犠牲にしています。今回取り上げる範囲外になりますが、必要に応じて組み込み async メソッドを、バッファリングしないファイル I/O と組み合わせて使用して、追加のスレッドを使用することなく同じようなメモリ節約を実現することができます。しかし、すべてをセクター単位に揃える必要があるため、適切に実装するのはやや困難です。そのうえ、マイクロソフトは FileOptions 列挙型から NoBuffering 項目を意図的に取り除いたようで、バッファリングしないファイル I/O が実質的に利用できなくなっており、これを可能にするには手作業での細かい処理が必要です。この不適切な実装に関連するリスクが非常に心配だったため、リスクの低い選択肢として、十分にテストされている HttpResponse.TransmitFile を使用することにしました。
FileDownloader.exe は複数のスレッドを起動します。各スレッドは個別にファイルの各部分 (バイト範囲) に対応する HttpWebRequest 呼び出しを行います。ファイルは指定した [Chunk Bytes] に分割されたファイルを、合計サイズに基づいてダウンロードします (図 2 参照)。
HttpWebRequest 呼び出しで指定されたファイル部分 (バイト範囲) のダウンロードに失敗したスレッドは、(失敗したバイト範囲のみに対して) 同じ HttpWebRequest 呼び出しを繰り返し行い、呼び出しが最終的に成功するまで再試行します (図 3 参照)。ダウンロード済みのファイル部分を失うことはありません。これにより、低速接続の場合、ダウンロードにかかる時間を大幅に節約できます。これで、頻繁にオフラインになり、障害が発生しやすい接続による悪影響を実質的に取り除くことができます。また、複数のスレッドを使用してファイルの異なる部分を同時に並列で (明示的なバッファリングを行わずネットワーク ストリームに直接および) 非同期オーバーラップ ファイル I/O を使用してハード ドライブにダウンロードするという設計になっているため、信頼性の低い接続が実際にオンラインであるわずかな時間に達成可能なダウンロードの量を最大化できます。このダウンローダーは、ネットワーク リンクがオンラインに復帰するたびに、残りの部分を継続的にダウンロードして作業を完了します。以前の作業が失われることはありません。ここでは、このツールを「再開可能」なファイル ダウンローダーではなく、「再試行可能」なファイル ダウンローダーだと考えています。
この違いを、架空の例で説明します。ダウンロードに一晩かかる大きなファイルをダウンロードするとします。職場から帰るときに、再開可能なファイル ダウンローダーを起動して実行しておきます。朝、職場に来ると、10% の段階でファイルのダウンロードが失敗しており、再開準備が整った状態になっています。しかし、ダウンローダーを再開するということは、残りの 90% を完了するために、再度一晩中ダウンローダーを実行する必要があります。
今度は、職場から帰るときに、再試行可能なファイル ダウンローダーを起動して実行しておきます。朝、職場に来ると、10% の段階の 1 つのチャンクでファイルのダウンロードが失敗していましたが、ファイルの残りのチャンクのダウンロードは継続して行われていました。この場合は、1 つのチャンクを再試行するだけでダウンロードが完了します。ネットワーク リンクに一時的な不具合が発生してチャンクのダウンロードに失敗した後でも、このダウンローダーは夜通し実行を続け、ネットワーク リンクがオンラインに復帰したときに残りの 90% のダウンロードを完了しています。
Web ブラウザーに組み込まれている既定のダウンロード クライアントも、https://localhost/DownloadPortal/Download?file=test.txt&chunksize=5242880 などの URL を使用して、ダウンロード クライアントとして使用できます。
Web ブラウザーをダウンロード クライアントとして使用する場合も、パラメーター (chunksize=5242880) は省略可能です。パラメーターを含めない場合、サーバーでは同じ HttpResponse.TransmitFile を使用してファイル全体を 1 つのチャンクとしてダウンロードします。含める場合は、各チャンクに対して個別の HttpResponse.TransmitFile 呼び出しを行います。
図 4 は、部分的内容をサポートしない Web ブラウザーをダウンロード クライアントとして使用する場合の DownloadHandler.dll の設計概要です。サーバー コンピューターのハード ドライブ上のファイルのチャンクが DownloadHandler.dll と Web ブラウザーを経由して、Web ブラウザーのコンピューターのハード ドライブ上のファイルに保存されるという処理フローになっています。
図 4 DownloadHandler.dll の処理フローの概要 (部分的内容をサポートしない Web ブラウザーをクライアントとして使用)
この IIS Web サーバーの IHttpAsyncHandler インターフェイス実装が優れているのは、HTTP 応答で Accept-Ranges HTTP ヘッダー (Accept-Ranges: bytes) を送信して、サーバーがファイルの部分 (部分的内容の範囲) を提供することをクライアントに通知する「バイト サービング」をサポートする点です。Web ブラウザー内の既定のダウンロード クライアントが部分的内容をサポートしている場合、Range HTTP ヘッダー (Range: bytes=5242880-10485760) を HTTP 要求に含めてサーバーに送信できます。サーバーは、部分的内容をクライアントに送り返すときに、Content-Range HTTP ヘッダー (Content-Range: bytes 5242880-10485760/26214400) を HTTP 応答に含めて送り返します。そのため、使用する Web ブラウザーとブラウザーに組み込みの既定のダウンロード クライアントによっては、スタンドアロン実行可能クライアントと同じようなメリットを得られる場合があります。ただし、ほとんどの Web ブラウザーは、独自のカスタム ダウンロード クライアントを作成できるようになっていて、ブラウザーにプラグインして、組み込みの既定のダウンロード クライアントを置き換えることができます。
サンプル プロジェクトの構成
サンプル プロジェクトでは、DownloadHandler.dll と IOThreads.dll を仮想ディレクトリの \bin ディレクトリにコピーし、次のように web.config の handlers セクションと modules セクションにエントリを追加します。
<handlers>
<add name="Download" verb="*" path="Download"
type="DownloaderHandlers.DownloadHandler" />
</handlers>
<modules>
<add name="CustomBasicAuthenticationModule" preCondition="managedHandler"
type="DownloaderHandlers.CustomBasicAuthenticationModule" />
</modules>
IIS Server に仮想ディレクトリがない場合は、\bin ディレクトリを含む仮想ディレクトリを作成し、そのディレクトリをアプリケーションに設定して、Microsoft .NET Framework 4 のアプリケーション プールを使用するようにします。
カスタム基本認証モジュールは、現在多くの ASP.NET Web サイトで使用されているのと同じ、使いやすい AspNetSqlMembershipProvider を使用して、ファイルのダウンロードに必要なユーザー名とパスワードを SQL Server の aspnetdb データベースに保存します。AspNetSqlMembershipProvider の便利なメリットの 1 つは、ユーザーが Windows ドメインのアカウントを所持している必要がないことです。AspNetSqlMembershipProvider のインストール方法や、ユーザー アカウントと SSL 証明書を構成するために必要な IIS サーバーの設定に関する詳細な説明は、サンプル プロジェクト コードの、CustomBasicAuthenticationModule クラス上部のコメント セクションに記載しています。その他、IIS サーバーを調整する高度な構成オプションについてここでは扱いませんが、IT 部門のサーバー管理担当者が既に設定している場合がほとんどです。もし未設定の場合は、TechNet ライブラリの bit.ly/1JRJjNS (英語) を参照してください。
これだけです。とても簡単ですね。
魅力的な要素
この設計の最も魅力的な要素は、処理が速いことではなく、オンラインとオフラインを絶えず繰り返す、信頼性の低い不安定なネットワーク リンクによって発生するネットワーク停止に対して、回復力が高く、フォールト トレラントであることです。通常、1 つのファイルを、1 つのチャンク、1 つの接続でダウンロードすると、最高のスループットが得られます。
この原則には、いくつか特殊な例外があります。たとえば、ファイルを個別の部分としてダウンロードしますが、各部分の取得元のミラー サーバーが異なるようなミラー化サーバー環境です (図 5 参照)。しかし、一般的に、複数のスレッドでファイルをダウンロードする場合、通常はネットワークがボトルネックとなって、1 つのスレッドでファイルをダウンロードするよりも速度は実質的に遅くなります。ただし、ダウンロード プロセス全体をやり直すことなく、ファイルのダウンロードに失敗した部分だけを成功するまで繰り返し再試行できる場合、準フォールト トレランスだといえます。
図 5 非常に初歩的なミラー インフラストラクチャをシミュレートする架空の将来の機能強化
また、図 5 のように、将来の機能強化として設計を修正して、非常に初歩的なミラー サーバー インフラストラクチャをシミュレートした場合、準冗長と考えることのできるものになります。
基本的に、この設計によって、信頼性の低いネットワーク経由でもファイルを確実にダウンロードできます。ネットワーク リンクに短時間の不具合が発生しても、最初からやり直す必要はなく、失敗したファイル部分を再試行するだけです。設計に追加するとよい (より回復機能に優れた設計にする) のは、ダウンロードの進行中にダウンロードの現在の進捗状況をハード ドライブのファイルに保存する機能です。このようにすることで、クライアント アプリケーションやクライアント コンピューターを再起動した場合でも、基本的に失敗したダウンロードを再試行できるようになります。ただし、この実装は、読者の課題にします。
前述の傑出した機能に匹敵するもう 1 つの魅力的な要素は、サーバーで HttpResponse.TransmitFile を使用してファイルのバイトを (明示的なバッファリングを行うことなく) 直接ネットワーク ストリームに書き込み、サーバーのメモリへの影響を最小限に抑えている点です。非常に大きなファイルをダウンロードしているときでも、サーバーメモリへの影響がほとんど感じられないのは驚くべきことです。
他にも、これほど重要ではありませんが、それでも魅力的な要素が 3 つあります。
1 つは、設計にフロントエンド クライアントとバックエンド サーバーの両方が含まれているため、サーバー側構成を完全に制御できることです。サーバーを他の人が所有していて自分で構成設定を変更できない場合、その設定によってファイルのダウンロード プロセスが大きく妨げられることがよくありますが、これにより、構成設定を自由に変更できるようになります。たとえば、クライアント IP アドレスごとに課せられる接続制限の制約を、通常の接続制限の 2 よりも大きな値に調整できます。また、クライアント接続ごとの調整制限をより大きな値に調整できます。
2 つ目は、フロントエンド クライアント (FileDownloader.exe) とバックエンド サーバー (DownloadHandler.dll) 内のサンプル プロジェクト コードが、単純かつ明確なブロックになっていることです。このサンプル コード ブロックから、HTTP プロトコルで部分的内容のバイト範囲を簡単に使用するのに不可欠な HTTP 要求と応答ヘッダーの使用方法がわかります。バイト範囲を要求するためにクライアントが送信する必要のある HTTP 要求ヘッダーや、部分的内容としてバイト範囲を返すためにサーバーが送信する必要のある HTTP 応答ヘッダーを簡単に確認できます。比較的簡単にコードを変更して、この単純な基本機能の上に高度な機能を実装したり、より洗練されたソフトウェア パッケージで高度な機能を使用できるように実装することができます。また、これをシンプルな開始テンプレートとして使用すると、Content-Type: multipart/byteranges、Content-MD5: md5-digest、If-Match: entity-tag などの他の高度な HTTP ヘッダーのサポートを追加することも比較的簡単になります。
3 つ目は、IIS Web サーバーを使用する設計なので、IIS Web サーバー組み込み機能のメリットの一部が自動的に得られることです。たとえば、通信は自動的に暗号化され (SSL 証明書を使用する HTTPS を使用)、圧縮されます (gzip 圧縮を使用)。ただし、非常に大きなファイルに gzip 圧縮を実行すると、サーバーの CPU に大きな負荷をかけることになるため、大きなファイルの gzip 圧縮はお勧めできない場合があります。しかし、サーバーの CPU で追加の負荷を処理できる場合、非常に小さく圧縮したデータを効率的に転送できるため、システムのスループット全体に大きな違いが生じる可能性があります。
将来の機能強化
サンプル プロジェクト コードには、ファイル ダウンローダーが動作するために必要な最低限の中核機能しか含めていません。サンプル プロジェクト コードでは、シンプルで理解しやすい設計を保つことを目標にしました。そのため、これをベースにして、比較的簡単に機能の強化や追加が可能です。サンプル プロジェクト コードは単なる出発点となるベース テンプレートです。これを運用環境で使用するには、さまざまな追加の機能強化が必要になります。この高度な追加機能を提供する上位の抽象層を追加する作業は、読者の課題として残しておきます。しかし、重要性の高い機能強化について、詳しく説明しておきます。
サンプル プロジェクト コードには、現在、ファイルの MD5 ハッシュ チェックサムを含める機能がありません。実環境では、なんらかのファイル チェックサム手法を使用して、クライアントにダウンロードされたファイルがサーバーのファイルと一致しており、改ざんや改変を受けていないことを確認することが不可欠です。これは、HTTP ヘッダーで、ダイジェスト (Content-MD5: md5-digest) を使用することで簡単に実行できます。実際に、初期のプロトタイプの 1 つでは、ファイルが要求されるたびにファイルの MD5 ファイル チェックサムを実行して、ヘッダーにダイジェスト (Content-MD5: md5-digest) を含めてからファイルを返していました。その後、クライアントは、受け取ったファイルに同じ MD5 ハッシュ チェックサムを実行して、結果のダイジェストがサーバーから返されたヘッダーのダイジェスト (Content-MD5: md5-digest) と一致するかどうかを確認します。一致しなければ、ファイルは改ざんされているか、破損しています。これでファイルが変更されていないことを確認するという目標は実現できますが、ファイルが大きい場合、サーバーの CPU に極度の負荷がかかり、実行時間が非常に長くなります。
実際には、ファイルが作成されてから削除されるまでの間に 1 回だけファイルに (バックグラウンドで) MD5 ハッシュ チェックサム処理を実行し、ファイル名をキーとして、結果のダイジェストをディクショナリに保存する必要があるでしょう。その後は、単純にディクショナリを参照するだけで、サーバーでファイルのダイジェストを取得できます。そのダイジェストは、サーバーからファイルを返すときに、サーバーの CPU にほとんど影響を与えることなく、ヘッダーに追加できます。
サンプル プロジェクト コードには、現在、クライアントが膨大な数のスレッドを使用することや、ファイルを膨大な数のチャンクに分割することを制限する機能もありません。ファイルを確実にダウンロードできるよう、クライアントに対して基本的に「必要な処理を何でも」許可しています。実環境では、1 つのクライアントがサーバーをハイジャックして他のすべてのクライアントがリソースを利用できなくなることのないよう、クライアントに制限を課すなんらかのインフラストラクチャが必要になるでしょう。
図 5 は、現在の設計の "chunksize" パラメーターの代わりに、URL クエリ文字列パラメーターとして「ノード名/バイト範囲」のリストを提供するように設計を変更することで、非常に初歩的なミラー インフラストラクチャをシミュレートする架空の将来の機能強化を表しています。現在の設計は、"chunksize" パラメーターに基づいて合計ファイルサイズをチャンクに分割するように内部的に反復処理して、各チャンクに対して HttpWebRequest を開始しています。しかし、これは比較的簡単に変更して、「ノード名/バイト範囲」のペアを単純に反復処理して、各ペアに対して HttpWebRequest を開始することで、異なるサーバーからファイルの各チャンクを取得するようにできます。
HttpWebRequest 用の URL を作成するには、サーバー名を「ノード名/バイト範囲」ペアのリストの関連するノード名で置き換え、関連するバイト範囲を Range HTTP ヘッダーに追加 (Range: bytes=0-5242880) した後、URL から「ノード名/バイト範囲」リストを完全に削除するだけです。なんらかのメタデータ ファイルを使用すると、ファイル部分が存在するサーバーを識別できます。要求元のコンピューターでは、各サーバーに分散されたファイル部分から 1 つのファイルを構成できます。
ファイルが 10 台のサーバーにミラー化されている場合、この設計を変更して、ファイル部分 1 をサーバー 1 ミラー コピーから、部分 2 をサーバー 2 ミラー コピーから、部分 3 をサーバー 3 ミラー コピーから取得するというようにできます。ここでも、ミラー サーバーで破損したチャンクがないこと、および実際にファイル全体を受け取ったことを確認するために、ファイルのすべての部分を受け取ってクライアントで完全なファイルに再構成した後、ファイルに MD5 ハッシュ チェックサムを実行することが不可欠です。また、サーバーを国内で地理的に分散させ、処理負荷が最も低い状態のサーバーを特定する精巧なインテリジェンスを構築してコードに組み込み、それらのサーバーを使用して要求にサービスしてファイルのチャンクを返すようにすることで、設計をさらに洗練し、次のレベルに高めることができます。
まとめ
この設計の目標は、高速で、拡張性の高いファイル ダウンローダーを作成することではなく、一時的なネットワーク停止から非常に容易に回復できるファイル ダウンローダーを作成することでした。
設計では、バイト範囲と部分的内容を「バイト サービング」する HTTP プロトコル ヘッダーの使用方法を非常に単純でわかりやすく説明できるように力を入れました。
調査したところ、シンプルな HTTP バイト サービングの方法や、HTTP プロトコルでバイト範囲ヘッダーを使用する方法のわかりやすい好例を見つけるのは、実際、非常に難しいことでした。ほとんどの例は、かなり複雑であるか、HTTP プロトコルで他にも多くのヘッダーを使用して高度な機能を多数実装しており、理解しにくいものでした。そのような例では、将来的に強化や拡張が難しいことは言うまでもありません。
比較的簡単に実験して徐々に高度な機能を追加でき、HTTP プロトコルのより高度な機能を追加する上位の抽象層の実装もできるように、必要最低限の機能を備えた、シンプルで充実した基盤を提供しようと考えました。
学習に使用でき、その後のベースとしても使用できる簡単な例を提供したかったのです。それではお楽しみください。
Doug Duernerは、マイクロソフト テクノロジが搭載された大規模なシステムの設計および実装を 15 年以上手がけている、シニア ソフトウェア エンジニアです。以前は、Fortune 500 に名を連ねるいくつかの金融機関に務めていました。また、ある商用ソフトウェア企業で大規模な分散ネットワーク管理システムを設計および開発した経験を持ち、そのシステムは DISA こと米国国防情報システム局 ("Global Information Grid" に使用) や米国国務省で使われていました。彼は心からのコンピューターおたくで、あらゆる側面に取り組んでいますが、最も複雑で困難な技術的ハードルにやりがいを感じていて、特にだれもが「不可能」と言う問題を好んでいます。Duerner の連絡先は coding.innovation@gmail.com (英語のみ) です。
Yeon-Chang Wangは、マイクロソフト テクノロジが搭載された大規模なシステムの設計および実装を 15 年以上手がけている、シニア ソフトウェア エンジニアです。以前は、Fortune 500 に名を連ねるいくつかの金融機関に務めていました。また、ある商用ソフトウェア企業で大規模な分散ネットワーク管理システムを設計および開発した経験を持ち、そのシステムは DISA こと米国国防情報システム局 ("Global Information Grid" に使用) や米国国務省で使われていました。また、世界最大の半導体メーカーの 1 つに向けて、大規模なドライバー認定システムを設計および実装したこともあります。Wang は、コンピューター科学の修士号を取得しています。複雑な問題が大好物で、連絡先は yeon_wang@yahoo.com (英語のみ) です。
この記事のレビューに協力してくれたマイクロソフト技術スタッフの Stephen Cleary および James McCaffrey に心より感謝いたします。