Web 開発のベスト プラクティス (Building Real-World Cloud Apps with Azure)
作成者: Rick Anderson、Tom Dykstra
Fix It プロジェクトのダウンロードまたは電子書籍のダウンロード
電子書籍『Building Real World Cloud Apps with Azure』は、Scott Guthrie が開発したプレゼンテーションに基づくものです。 これは、クラウド向け Web アプリの開発を成功させるために役立つ 13 のパターンとプラクティスについて説明しています。 電子書籍の詳細については、最初のチャプターを参照してください。
最初の 3 つのパターンは、アジャイル開発プロセスの設定に関するものでしたが、残りのパターンはアーキテクチャとコードに関するものです。 これは、Web 開発におけるベスト プラクティスのコレクションです。
- スマート ロード バランサーの背後にあるステートレス Web サーバー。
- セッション状態を回避する (回避できない場合は、データベースではなく分散キャッシュを使用します)。
- CDN を使用して静的ファイル資産 (画像、スクリプト) のエッジ キャッシュを行う。
- .NET 4.5 の非同期サポートを使用して呼び出しがブロックされないようにする。
これらのプラクティスは、クラウド アプリに限らず、すべての Web 開発に有効ですが、クラウド アプリでは特に重要です。 これらのプラクティスを組み合わせると、クラウド環境が提供する柔軟性の高いスケーリングを最大限に活用するのに役立ちます。 これらのプラクティスに従わない場合、アプリケーションをスケーリングしようとしたときに制限が発生します。
スマート ロード バランサーの背後にあるステートレス Web 層
ステートレス Web 層とは、Web サーバーのメモリやファイル システムにアプリケーション データを一切保存しないことを意味します。 Web 層をステートレスに保つことで、カスタマー エクスペリエンスの向上とコスト削減の両方を実現できます。
- Web 層がステートレスで、ロード バランサーの背後にある場合は、サーバーを動的に追加または削除して、アプリケーション トラフィックの変化に迅速に対応できます。 実際に使用した分だけサーバー リソースの料金を支払うクラウド環境では、需要の変化に迅速に対応できることで、大幅なコスト削減につながります。
- ステートレス Web 層は、アプリケーションをスケールアウトするためのアーキテクチャが非常にシンプルです。 これにより、スケーリングのニーズにも迅速に対応し、そのプロセスでの開発とテストにかかるコストも削減できます。
- クラウド サーバーは、オンプレミス サーバーと同様に、時折パッチ適用や再起動を行う必要があります。また、Web 層がステートレスであれば、サーバーが一時的に停止した際にトラフィックを再ルーティングしても、エラーや予期しない動作を引き起こすことはありません。
実際のほとんどのアプリケーションでは、Web セッションの状態を保存する必要があります。ここで重要なのは、Web サーバーに保存しないということです。 クライアントの Cookie 内や、キャッシュ プロバイダーを使用したプロセス外のサーバー側の ASP.NET セッション状態内など、別の方法で状態を保存することができます。 ローカル ファイル システムではなく、Windows Azure BLOB ストレージにファイルを保存することができます。
Web 層がステートレスの場合に、Windows Azure Web サイト内にアプリケーションを簡単にスケーリングする方法の例として、管理ポータル内の Windows Azure Web サイトの [スケール] タブを参照してください。
Web サーバーを追加する場合は、インスタンス数スライダーを右にドラッグするだけです。 これを 5 に設定し、[保存] をクリックすると、数秒以内に Windows Azure に Web サイトのトラフィックを処理する 5 台の Web サーバーが作成されます。
インスタンス数を 3 に減らすことも、1 に戻すことも簡単に実行できます。 規模を縮小すると、Windows Azure は時間単位ではなく分単位で課金されるため、コスト削減をすぐに始めることができます。
また、Windows Azure に対して、CPU 使用率に基づいて Web サーバーの数を自動的に増減するように指示することもできます。 次の例では、CPU 使用率が 60% を下回ると、Web サーバーの数が最小で 2 まで減少し、CPU 使用率が 80% を上回ると、Web サーバーの数が最大で 4 まで増加します。
あるいは、サイトがビジー状態になるのが勤務時間中だけだとわかっている場合はどうしますか? Windows Azure に対して、日中は複数のサーバーを稼働させ、夕方、夜間、週末はサーバーを 1 台に減らすように指示することができます。 次の一連のスクリーンショットは、勤務時間外は 1 台のサーバー、勤務時間中の午前 8 時から午後 5 時までは 4 台のサーバーを稼働させるように Web サイトを設定する方法を示しています。
もちろん、これらの設定はすべてスクリプトでもポータルでも行うことができます。
Windows Azure では、Web 層をステートレスに保つことで、サーバー VM を動的に追加または削除できる状況であれば、アプリケーションをほぼ無制限にスケートアウトできます。
セッション状態を回避する
ユーザー セッションの状態をなんらかの形で格納しないのは、実際のクラウド アプリケーションでは実用的でない場合が多いですが、方法によっては、パフォーマンスとスケーラビリティに与える影響が大きくなります。 状態を格納する必要がある場合は、状態の量を少なくし、Cookie に格納することをお勧めします。 この方法を利用できない場合、次にお勧めする方法は、ASP.NET セッション状態と分散型メモリ内キャッシュのプロバイダーを使用することです。 パフォーマンスとスケーラビリティの観点から最もお勧めできないのが、データベースを利用したセッション状態プロバイダーを使用する方法です。
CDN を使用して静的ファイル資産をキャッシュする
CDN は Content Delivery Network (コンテンツ配信ネットワーク) の略語です。 画像やスクリプト ファイルなどの静的ファイル資産を CDN プロバイダーに提供すると、プロバイダーは世界中のデータ センターにこれらのファイルをキャッシュします。これにより、ユーザーがどこからアプリケーションにアクセスしても、キャッシュされた資産に対して比較的迅速な応答と低遅延を実現します。 これにより、サイトの全体的な読み込み時間が短縮され、Web サーバーの負荷が軽減されます。 CDN は、地理的に広く分散した対象ユーザーにサービスを提供する場合に特に重要です。
Windows Azure にも CDN がありますが、Windows Azure または任意の Web ホスティング環境で実行されるアプリケーションで、他の CDN を使用することもできます。
.NET 4.5 の非同期サポートを使用して呼び出しがブロックされないようにする
.NET 4.5 では、C# および VB プログラミング言語が強化され、タスクの非同期処理が大幅に簡素化されました。 非同期プログラミングのメリットは、複数の Web サービス呼び出しを同時に開始する場合など、並列処理の状況だけではありません。 また、高負荷条件下でも、Web サーバーは、より効率的かつ信頼性の高いパフォーマンスを実現できます。 Web サーバーで使用できるスレッドの数には上限があり、高負荷条件下ですべてのスレッドが使用されている場合、受信した要求はスレッドが解放されるまで待機する必要があります。 アプリケーション コードがデータベース クエリや Web サービス呼び出しなどのタスクを非同期で処理していない場合、サーバーが I/O 応答を待機している間、多くのスレッドが不必要に塞がってしまいます。 そのため、高負荷条件下でサーバーが処理できるトラフィックの量が制限されます。 非同期プログラミングでは、Web サービスやデータベースからのデータの返送を待機しているスレッドは、データを受信するまで、新しい要求を処理するために解放されます。 ビジー状態の Web サーバーでも、数百から数千もの要求を迅速に処理することができます。要求はスレッドが解放されるまで待機しません。
前に説明したように、Web サーバーの数を増やすのと同じくらい簡単に、Web サイトを処理する Web サーバーの数を減らすことができます。 そのため、サーバーのスループットを増やすことができる場合は、サーバーの台数が少なくて済むため、コストを削減できます。これは、一定のトラフィック量に対して必要なサーバーの台数が少なくなるためです。
.NET 4.5 非同期プログラミング モデルのサポートは、Web Forms、MVC、Web API 用 ASP.NET 4.5、Entity Framework 6、Windows Azure Storage API に付属しています。
ASP.NET 4.5 での非同期サポート
ASP.NET 4.5 では、非同期プログラミングのサポートが言語だけでなく、MVC、Web Forms、Web API フレームワークにも追加されました。 たとえば、ASP.NET MVC コントローラー アクション メソッドは、Web 要求からデータを受け取り、そのデータをビューに渡します。ビューは、ブラウザーに送信される HTML を作成します。 多くの場合、アクション メソッドは、Web ページに表示したり、Web ページに入力されたデータを保存したりするために、データベースや Web サービスからデータを取得する必要があります。 このようなシナリオで、アクション メソッドを非同期にするのは簡単です。ActionResult オブジェクトを返す代わりに、Task<ActionResult> を返し、async キーワードでメソッドをマークします。 メソッド内では、待機時間を含む操作をコード行が開始する場合、await キーワードでマークします。
データベース クエリのリポジトリ メソッドを呼び出す簡単なアクション メソッドを次に示します。
public ActionResult Index()
{
string currentUser = User.Identity.Name;
var result = fixItRepository.FindOpenTasksByOwner(currentUser);
return View(result);
}
データベース呼び出しを非同期に処理する同じメソッドを次に示します。
public async Task<ActionResult> Index()
{
string currentUser = User.Identity.Name;
var result = await fixItRepository.FindOpenTasksByOwnerAsync(currentUser);
return View(result);
}
コンパイラは、内部で適切な非同期コードを生成します。 アプリケーションが FindTaskByIdAsync
を呼び出すと、ASP.NET が FindTask
要求を行い、ワーカー スレッドがアンワインドされ、別の要求を処理できるようになります。 FindTask
要求が完了すると、スレッドが再起動され、その呼び出しの後に続くコードの処理が続行されます。 FindTask
要求が開始されてからデータが返されるまでの間、スレッドは応答を待機するために塞がれることなく、有効な作業を実行することができます。
非同期コードには多少のオーバーヘッドがありますが、低負荷の条件下ではそのオーバーヘッドは無視できる程度であり、高負荷の条件下では、使用可能なスレッドを待機することで保留されることなく、要求を処理することができます。
ASP.NET 1.1 以降、このような非同期プログラミングを実行できるようになりましたが、記述が困難で、エラーが発生しやすく、デバッグも困難でした。 ASP.NET 4.5 でコーディングが簡素化されたため、ぜひお試しください。
Entity Framework 6 での非同期サポート
4.5 での非同期サポートの一環として、Web サービス呼び出し、ソケット、ファイル システム I/O の非同期サポートが提供されましたが、Web アプリケーションでの最も一般的なパターンはデータベースへのアクセスであり、データ ライブラリでは非同期がサポートされていませんでした。 Entity Framework 6 で、データベース アクセスの非同期サポートが追加されました。
Entity Framework 6 では、データベースにクエリやコマンドを送信するすべてのメソッドに非同期バージョンが用意されています。 次の例は、Find メソッドの非同期バージョンを示しています。
public async Task<FixItTask> FindTaskByIdAsync(int id)
{
FixItTask fixItTask = null;
Stopwatch timespan = Stopwatch.StartNew();
try
{
fixItTask = await db.FixItTasks.FindAsync(id);
timespan.Stop();
log.TraceApi("SQL Database", "FixItTaskRepository.FindTaskByIdAsync", timespan.Elapsed, "id={0}", id);
}
catch(Exception e)
{
log.Error(e, "Error in FixItTaskRepository.FindTaskByIdAsynx(id={0})", id);
}
return fixItTask;
}
この非同期サポートは、挿入、削除、更新、単純な検索だけでなく、LINQ クエリでも機能します。
public async Task<List<FixItTask>> FindOpenTasksByOwnerAsync(string userName)
{
Stopwatch timespan = Stopwatch.StartNew();
try
{
var result = await db.FixItTasks
.Where(t => t.Owner == userName)
.Where(t=>t.IsDone == false)
.OrderByDescending(t => t.FixItTaskId).ToListAsync();
timespan.Stop();
log.TraceApi("SQL Database", "FixItTaskRepository.FindTasksByOwnerAsync", timespan.Elapsed, "username={0}", userName);
return result;
}
catch (Exception e)
{
log.Error(e, "Error in FixItTaskRepository.FindTasksByOwnerAsync(userName={0})", userName);
return null;
}
}
このコードでは、データベースにクエリを送信するのが ToList
メソッドであるため、その Async
バージョンがあります。 Where
および OrderByDescending
メソッドはクエリを構成するだけですが、ToListAsync
メソッドはクエリを実行し、result
変数に応答を保存します。
まとめ
ここで説明する Web 開発のベスト プラクティスは、任意の Web プログラミング フレームワークと任意のクラウド環境に実装できますが、ASP.NET と Windows Azure にはこれを簡単に実行できるツールがあります。 これらのパターンに従うと、Web 層を簡単にスケールアウトでき、各サーバーがより多くのトラフィックを処理できるようになるため、コストを最小限に抑えることができます。
次の章では、クラウドでシングル サインオン シナリオを有効にする方法について説明します。
リソース
詳しくは、次のリソースをご覧ください。
ステートレス Web サーバー:
- Microsoft のパターンとプラクティス - 自動スケーリングのガイダンス。
- Windows Azure Web サイトでの ARR のインスタンス アフィニティの無効化。 Erez Benari のブログ記事では、Windows Azure Web サイトでのセッション アフィニティについて説明しています。
CDN:
- FailSafe: スケーラブルで回復性の高い Cloud Services の構築。 Ulrich Homann、Marc Mercuri、Mark Simms による 9 部構成のビデオ シリーズ。 1:34:00 以降のエピソード 3 での CDN の説明を参照してください。
- Microsoft パターンとプラクティス - 静的コンテンツ ホスティング パターン
- CDN レビュー。 多数の CDN の概要。
非同期の概要:
- ASP.NET MVC 4 での非同期メソッドの使用。 Rick Anderson によるチュートリアル。
- Async および Await を使用した非同期プログラミング (C# および Visual Basic)。 非同期プログラミングの原理、ASP.NET 4.5 での動作、実装のためのコードの記述方法について説明する MSDN ホワイト ペーパー。
- Entity Framework の非同期クエリと保存
- FailSafe: スケーラブルで回復性の高い Cloud Services の構築。 Ulrich Homann、Marc Mercuri、Mark Simms による 9 部構成のビデオ シリーズ。 非同期プログラミングがスケーラビリティに与える影響については、エピソード 4 とエピソード 8 を参照してください。
- ASP.NET 4.5 で非同期メソッドを使用するメリットと重要な問題。 Scott Hanselman のブログ記事では、主に ASP.NET Web Forms アプリケーションでの非同期の使用について説明しています。
Web 開発に関するその他のベスト プラクティスについては、次のリソースを参照してください。
- Fix It サンプル アプリケーション - ベスト プラクティス。 この電子書籍の付録には、Fix It アプリケーションに実装されたベスト プラクティスが数多く記載されています。
- Web 開発者用チェックリスト