.NET アプリケーションのパフォーマンスとスケーラビリティの向上
J.D. Meier, Srinath Vasireddy, Ashish Babbar, and Alex Mackman
Microsoft Corporation
May 2004
日本語版最終更新日 2005 年 8 月 17 日
要約: パフォーマンス目標値を達成する ASP.NET アプリケーションを構築するには、ボトルネックが発生しやすい場所とその原因、およびその予防策をとるための手順について、把握しておく必要があります。また、健全なアーキテクチャと設計、ベスト プラクティスに基づくコーディング、そして最適化したプラットフォームおよび Microsoft(R) .NET Framework 構成を、組み合わせる必要があります。この章では、これらを個別に扱っていきます。
目次
目標
概要
この章の使いかた
アーキテクチャ
パフォーマンスとスケーラビリティに関する問題
設計上の考慮事項
実装に関する考慮事項
スレッド処理についての説明
スレッド処理に関するガイドライン
リソース管理
ページ
サーバー コントロール
データ連結
キャッシングについての説明
キャッシングに関するガイドライン
状態管理
アプリケーション状態
セッション状態
ビュー ステート
HTTP モジュール
文字列管理
例外管理
COM 相互運用
データ アクセス
セキュリティに関する考慮事項
IIS 6.0 に関する考慮事項
展開上の考慮事項
まとめ
補足資料
目標
- ページ応答時間を向上させる。
- 拡張性のある Web アプリケーションを設計する。
- サーバー コントロールを効率的に使う。
- 効率的なキャッシング ストラテジを採用する。
- 適切な状態管理について分析し、適用する。
- ビュー ステートの影響を最小化する。
- セキュリティに影響を及ぼすことなくパフォーマンスを向上させる。
- COM 相互運用スケーラビリティの問題を最小限に抑える。
- スレッド処理を最適化する。
- リソース管理を最適化する。
- よく見られるデータ結合のミスを避ける。
- サーバー負荷を減らすようなセキュリティ設定にする。
- よく見られる展開ミスを避ける。
概要
パフォーマンス目標値を達成する ASP.NET アプリケーションを構築するには、ボトルネックが発生しやすい場所とその原因、およびその予防策をとるための手順について、把握しておく必要があります。また、健全なアーキテクチャと設計、ベスト プラクティスに基づくコーディング、そして最適化したプラットフォームおよび Microsoft(R) .NET Framework 構成を、組み合わせる必要があります。この章では、これらを個別に扱っていきます。
この章では、まず ASP.NET アプリケーションのアーキテクチャについて調べ、次に Web 要求が HTTP パイプラインと ASP.NET パイプラインにおいてどのように処理されていくかについて、詳しく解説します。また、各段階で発生する処理について説明し、よく見られるパフォーマンス上のボトルネックを特定していきます。それに続き、ASP.NET の設計に関する、一連のガイドラインを提示します。このガイドラインに従うことにより、大きなコストを伴う再エンジニアリングでしか修正できないようなパフォーマンス上の問題を、トップレベルの設計で回避することができます。最後は、ASP.NET のパフォーマンスに関する主要問題を、各節で説明していきます。これらの問題には、ページとコントロールの問題、キャッシング、リソース管理、セッション状態とビュー ステートの問題、スレッド処理、例外管理と文字列管理、COM 相互運用、などがあります。
この章の使いかた
この章を、ASP.NET アプリケーションのパフォーマンス向上に役立ててください。ここで扱う設計上の考慮事項、コーディング技法、最適化したプラットフォームおよび Microsoft(R) .NET Framework 構成についての情報は、新規および既存のアプリケーションに適用できます。この章を最大限に活用する方法を、以下に掲げます。
-
目的のトピックだけを参照するか、最後まで読む - この章で掲げる大見出しを見れば、関心のあるトピックを見つけることができます。あるいは、この章を最初から最後まで読み、パフォーマンスとスケーラビリティに関する設計上の問題について、徹底した知識を得ることもできます。
-
チェックリストを利用する - このガイドの「チェックリスト」にある「チェックリスト: ASP.NET パフォーマンス」を利用してください。この章で示すガイドラインについて素早く確認し、評価することができます。
-
ASP.NET 実行インフラストラクチャについて知る - 実行インフラストラクチャについて理解することにより、パフォーマンスについて最適化したコードを作成できます。
-
パフォーマンスとスケーラビリティに関する主な問題を知る - この章の「パフォーマンスとスケーラビリティに関する問題」を読み、ASP.NET アプリケーションのパフォーマンスとスケーラビリティに影響を及ぼし得る、主要な問題について学んでください。これらを理解することは、パフォーマンスとスケーラビリティに関する問題を効果的に特定し、この章で提供する推奨事項を適用する上で重要です。
-
パフォーマンスを考慮に入れて設計する - この章の「設計上の考慮事項」を読み、ベスト プラクティスに基づく設計ガイドラインを学んでください。
-
この章の「アーキテクチャ」の節を利用する - ASP.NET の仕組みを理解するのに役立ちます。アーキテクチャを理解することにより、設計と実装において、より正確な判断を下せるようになります。
-
この章の「設計上の考慮事項」の節を利用する - ASP.NET コードの実装選択に影響を与える、上位レベルの設計判断について理解できるようになります。
-
第 13 章「コード レビュー: .NET アプリケーション パフォーマンス」を読む - 具体的なガイダンスについては、「ASP.NET」の節を参照してください。
-
アプリケーションのパフォーマンスを計測する - 第 15 章の「.NET アプリケーション パフォーマンスの計測」の「ASP.NET」、および「.NET フレームワーク テクノロジ」を読み、アプリケーションのパフォーマンスの測定に使用できる、主なメトリクスについて学んでください。アプリケーションのパフォーマンスを測定できるようになることは、パフォーマンス上の問題を特定し、解決する上で重要です。
-
アプリケーションのパフォーマンスをテストする - 第 16 章「.NET アプリケーション パフォーマンスのテスト」を読み、アプリケーションのパフォーマンス テストを行なう方法を学んでください。一貫したテスト プロセスを用い、結果を分析することは重要です。
-
アプリケーションのパフォーマンスをチューニングする - 第 17 章「.NET アプリケーション パフォーマンスのチューニング」の「ASP.NET」を読み、チューニング メトリクスの使用によって特定したパフォーマンス問題の解決方法を学んでください。
アーキテクチャ
ASP.NET は、ホストを必要とします。Windows Server(TM) 2003 では、デフォルトのホストは Internet Information Services (IIS) 6.0 ワーカー プロセス (W3wp.exe) です。ASP.NET プロセス モデルを使用する場合、ホストは ASP.NET ワーカー プロセス (Aspnet_wp.exe) です。
ASP.NET が受けた要求は、HttpRuntime オブジェクトによって処理されます。HttpRuntime は、アプリケーションの作成と初期化を担当し、要求キューとスレッド プールを管理しつつ、入ってきた要求を適切なアプリケーションへ送ります。アプリケーションへ送られた要求は、パイプラインを通して渡されます。このパイプラインは、複数の HttpModule オブジェクトと 1 つの HttpHandler オブジェクトから成る、段階化されたイベント ベースの実行フレームワークです。このアーキテクチャを、図 6.1 で示します。
図 6.1 ASP.NET ランタイム インフラストラクチャ
HttpModule オブジェクトは、ASP.NET が提供する定義済みのイベントを処理することでパイプラインに参加します。これらのイベントには、BeginRequest、AuthenticateRequest、EndRequest などがあります。要求は、HttpModule オブジェクトのパイプラインを通って流れ、1 つの HttpHandler によって実行されます。イベントハンドラが完了したら、要求はパイプラインを通って戻り、クライアントへ送られます。
要求のライフ タイム全体を通して、コンテキストは公開されます。HttpContext オブジェクトは個々の要求や関連応答に関する情報をカプセル化します。
パフォーマンスとスケーラビリティに関する問題
ASP.NET アプリケーションのパフォーマンスとスケーラビリティに悪影響を及ぼし得る主な問題について、以下で一通り取り上げます。この章の以降の節では、ここで掲げる問題を予防または解決するための方策や技術情報を提示します。
-
リソース アフィニティ - リソース アフィニティは、サーバーの追加を妨げることや、CPU やメモリの追加によるメリットを小さくすることがあります。リソース アフィニティは、コードが特定のスレッド、CPU、コンポーネント、インスタンス、またはサーバーを必要とする場合に発生します。
-
過度のアロケーション - メモリを要求ごとに過度に割り当てるアプリケーションは、メモリを消費し、必要以上のガベージ コレクションを発生させます。ガベージ コレクションの回数が増えることにより、CPU 利用度は増加します。このような過度のアロケーションは、一時アロケーションを原因としている場合があります。例えば、タイトなループで += 演算子を使う、過度の文字列連結が原因となる場合もあります。
-
希少なリソースを共有しない - Dispose メソッドや Close メソッドを呼び出してデータベース コネクションなどのリソースを開放しないと、リソース不足につながりかねません。リソースを閉じるか破棄することにより、リソースの使用を効率化することができます。
-
処理のブロック - ASP.NET 要求を処理する 1 つのスレッドは、下位呼び出しの戻り値を待つ間、ブロックされて新たなユーザー 要求に対応できなくなります。所要時間の長いストアド プロシージャやリモート オブジェクトの呼び出しは、スレッドを長時間ブロックする場合があります。
-
スレッドの誤使用 - スレッドを要求ごとに作成すると、回避可能なスレッド初期化コストが発生します。また、シングル スレッド アパートメント (STA) COM オブジェクトを不適切に使うと、複数の要求がキューに入れられる場合があります。この場合、パフォーマンスは低下し、スケーラビリティ上の問題が発生します。
-
遅延バインディング - 遅延バインディングは、実行コードを特定し、ロードするために、実行時に追加的なインストラクションを要求します。対象コードがマネージ コードでもアンマネージ コードでも、これらのインストラクションは避けるべきです。
-
COM 相互運用の誤使用 - COM 相互運用は通常、非常に効率的です。しかし、多くの要因が、そのパフォーマンスに影響を及ぼします。これらの要因には、マネージ / アンマネージ境界やアパートメント境界をまたいで渡る、引数のサイズと型などがあります。アパートメント境界をまたぐと、大きな負担を伴うスレッド切り替えが必要となる場合があります。
-
大きなページ - ページ サイズは、ページ上のコントロールの数と型の影響を受けます。また、ページのレンダリングに使うデータと画像にも影響されます。ネットワークに多くのデータを送るほど、消費する帯域は大きくなります。高いレベルの帯域を消費するほど、ボトルネックは発生しやすくなります。
-
不適切なデータ キャッシュ - 静的データをキャッシュしない、アイテムを流し出すために過剰に多いデータをキャッシュする、アプリケーション全体で使われるデータでなくユーザー データをキャッシュする、頻繁に使われないアイテムをキャッシュする、といったアプローチは、システムのパフォーマンスとスケーラビリティを制限することになりかねません。
-
不適切な出力キャッシュ - 出力キャッシュを使用していない場合、または不適切に使用している場合、Web サーバーに回避可能な負担がかかる場合があります。
-
非効率なレンダリング - HTML コードやサーバー コードを散在させる、ページ ポストバックで不必要な初期化コードを実行する、データの遅延バインディング、といったアプローチは、大きなレンダリング オーバーヘッドの原因となり得ます。このオーバーヘッドにより、感覚的および実質的なページ パフォーマンスは低下しかねません。
設計上の考慮事項
高いパフォーマンスを備えた ASP.NET アプリケーションの構築は、設計時にパフォーマンスを考慮に入れれば、非常に簡単になります。プロジェクト開始時からパフォーマンス プランを作成するようにしてください。決して、パフォーマンスをアプリケーション構築後に対処すべき問題としてとらえないでください。また、反復的な開発プロセスをとり、反復の間に定期的な計測を織り込むようにしてください。
ベスト プラクティスに基づいた、以下の設計ガイドラインに従えば、高いパフォーマンスを備えた Web アプリケーションを作成できる可能性は、極めて高くなります。以下の設計ガイドラインを考慮してください。
-
セキュリティとパフォーマンスを考慮する。
-
アプリケーションを論理的に分割する。
-
アフィニティを評価する。
-
ラウンド トリップ数を減らす。
-
所要時間の長いタスクをブロックしない。
-
キャッシングを利用する。
-
不要な例外を避ける。
セキュリティとパフォーマンスを考慮する。
認証スキームの選択は、アプリケーションのパフォーマンスとスケーラビリティに影響を及ぼす場合があります。以下の問題について、考慮する必要があります。
詳細情報
詳細は、MSDN 上の "Performance Comparison: Security Design Choices" を参照してください。
アプリケーションを論理的に分割する。
レイヤリングにより、アプリケーション ロジックをプレゼンテーション層、ビジネス層、データ アクセス層の各論理階層に分けてください。これにより、コードの保守性を高めることができます。また、各論理階層のパフォーマンスを別々に監視し、最適化することが可能になります。明確な論理パーティションを設けることにより、アプリケーションのスケーリングのための選択肢も広がります。コードビハインド ファイルのコード量を減らすことを心掛け、保守性とスケーラビリティを向上させてください。
論理分割と物理展開を混同しないでください。論理分割により、プレゼンテーション ロジックとビジネス ロジックを同じサーバー上に配置し、Web ファームのサーバーをまたいで同一ロジックが複数存在する状態にするか、あるいはロジックを物理的に分かれたサーバーにインストールするかを決められるようになります。注意すべきポイントは、リモート呼び出しは遅延コストを生じさせ、遅延の程度はレイヤ間の距離が長いほど大きくなるということです。
例えば、プロセス内呼び出しが最も速く、同じコンピュータ内のプロセスをまたいだ呼び出し、リモート ネットワーク呼び出し、という順に続きます。可能な限り、論理パーティションで分けたロジック同士を、近接させてください。パフォーマンスを最適化するためには、ビジネス ロジックとデータ アクセス ロジックを、Web サーバー上のアプリケーションの Bin ディレクトリに配置すべきです。
これらを含む展開上の問題についての詳細は、この章で後述する「展開上の考慮事項」を参照してください。
アフィニティを評価する。
アフィニティにより、パフォーマンスを向上させることができます。しかし、アフィニティはスケーラビリティに悪影響を及ぼす場合があります。リソース アフィニティを発生させる主なコーディング手法には、以下などがあります。
-
インプロセス セッション状態を使う - サーバー アフィニティを避けるには、SQL Server データベースにおいて ASP.NET セッション状態をプロセス外で保守するか、リモート マシンで動作するアウトプロセス セッション状態サービスを使ってください。または、ステートレス アプリケーションを設計するか、セッション状態をクライアントに格納し、要求ごとにそれを渡すようにしてください。
-
コンピュータ独自の暗号化鍵を使う - データベースで、データの暗号化のためにコンピュータ独自の暗号化鍵を使うと、アプリケーションは Web ファームで作業できなくなります。これは、通常の暗号化データは、複数の Web サーバーからアクセス可能でなければならないためです。共有対称鍵の暗号化にコンピュータ独自鍵を使うのが、より望ましいアプローチです。この場合、暗号化データのデータベースへの格納には、共有対称鍵を使用します。
詳細情報
共有データベースでの、アフィニティを発生させないデータの暗号化および復号化の方法についての詳細は、MSDN 上の「Web アプリケーション セキュリティ強化: 脅威とその対策」の「セキュリティ保護されたデータ アクセスを構築する」 を参照してください。
ラウンド トリップ数を減らす。
ASP.NETの提供する以下のアプローチや機能を使い、Web サーバー-ブラウザ間、およびWeb サーバー-下位システム間のラウンド トリップ数を減らしてください。
-
HttpResponse.IsClientConnected - 要求の処理および負荷の大きいサーバー側処理の実行の前に、クライアントがまだ接続されているかを確認するのに、 HttpResponse.IsClientConnected プロパティを使うことを検討してください。ただし、IIS 5.0 では、この呼び出しはプロセス外へ発行しなければならない場合もあり、非常に大きな負担を伴うこともあります。このプロパティを使うなら、実質的なメリットを得られるかを計測してください。
-
キャッシング - アプリケーションが静的、またはほぼ静的なデータを取得、変換、レンダリングする場合、キャッシングによって余計なヒットを回避できます。
-
出力バッファリング - 可能な場合、出力をバッファすることにより、ラウンド トリップ数を減らしてください。このアプローチにより、サーバー側でのバッチ処理が可能になり、クライアントとのチャッティーな <処理規模が小さく対話数の多い> 通信を避けることができます。欠点は、ページのレンダリングは、完了するまでクライアント側から見られないという点です。対応策として、Response.Flush メソッドを使用できます。このメソッドは、出力をクライアント側のポイントまで送ります。バッファ機能が無効な遅いネットワークにつながっているクライアントは、サーバーの応答時間に影響を与えることに注意してください。これは、サーバーがクライアントからの肯定応答を待たなければならないためです。クライアントからの肯定応答は、クライアントがサーバーからすべてのコンテンツを受信した後に発行されます。
-
Server.Transfer - 可能な限り、Response.Redirect メソッドの代わりに Server.Transfer メソッドを使ってください。Response.Redirect は、応答ヘッダーをクライアントへ送ります。これにより、クライアントは新しい URLにより、再指定されたサーバーに新しい要求を送ります。Server.Transfer は、単純にサーバー側呼び出しをすることにより、この迂回を回避します。
Response.Redirect 呼び出しは、常に Server.Transfer 呼び出しに置き換え可能なわけではありません。これは、要求処理のハンドラ段階で、Server.Transfer は新しいハンドラを使用するためです。再指定中に認証チェックおよび承認チェックが必要となる場合は、Server.Transfer の代わりに Response.Redirect を使ってください。これら 2 つは、同等ではないためです。Response.Redirect 使用時は、ブール型の第 2 引数を受け入れる、オーバーロードしたメソッドを使うようにし、内部例外が発生しないように、false を渡してください。
また、Server.Transfer は、コントロールを同じアプリケーションのページへ移す場合にのみ使えることにも注意してください。他のアプリケーションのページへ移す場合は、Response.Redirect を使わなければなりません。
詳細情報
詳細は、Microsoft サポート技術情報の Knowledge Base の文書 312629 「PRB: Response.End、Response.Redirect、または Server.Transfer メソッドを使用すると ThreadAbortException が発生する」を参照してください。
所要時間の長いタスクをブロックしない。
所要時間の長い処理、またはブロック処理を行う場合、以下の非同期メカニズムを用い、Web サーバーが他の受信要求を処理できるようにしてください。
詳細情報
このメカニズムの実装方法についての詳細は、この章で後述する「スレッド処理に関するガイドライン」を参照してください。
キャッシングを利用する。
パフォーマンスに関連する設計上の考慮事項で最も重要なのは、おそらく、適切なキャッシング ストラテジを採用することです。ASP.NET が提供するキャッシング機能には、出力キャッシュ、部分ページ キャッシュ、キャッシュ API などがあります。これらの機能を活かして、アプリケーションを設計してください。
キャッシングは、データ アクセスやレンダリング 出力のコストを減らすために利用できます。ページがデータをどのように使用し、レンダリングするかを知っておくことにより、効率的なキャッシング ストラテジを設計できるようになります。キャッシングは、Web アプリケーションが常に、データベース、Web サービス、リモート アプリケーション サーバーなどのリモート リソースからのデータに依存している場合に、特に有効です。データベースに大きく依存するアプリケーションは、キャッシングによってデータベースへの負荷を小さくし、アプリケーションのスループットを向上させることで、メリットを得られる場合があります。原則として、キャッシングは同等の処理に比べて負担が小さいため、キャッシングを使うべきです。キャッシング ストラテジを設計する際は、以下を考慮してください。
-
作成や取得が高くつくデータまたは出力を特定する - 作成や取得が高くつくデータまたは出力をキャッシングすれば、データ取得のためのコストを小さくすることができます。データのキャッシングにより、データベース サーバーへの負荷は小さくなります。
-
揮発性を評価する - キャッシングを効果的なものにするためには、静的なデータや出力、または頻繁に変更されないデータや出力を対象にするべきです。キャッシュの対象にふさわしいデータの簡単な例としては、国、都道府県、郵便番号のリストなどが挙げられます。通常、頻繁に変更されるデータや出力は、キャッシングにあまり適していません。ただし、必要性の高さによっては、適用することも可能です。ユーザー データのキャッシングは、原則として、ASP.NETのセッション 状態 ストアなどのキャッシュに精通している場合にのみ、推奨されます。
-
使用頻度を評価する - 頻繁に使用するデータや出力をキャッシュすれば、パフォーマンス上およびスケーラビリティ上の大きなメリットを得られるかもしれません。これらのメリットを得られるのは、やはり、静的なデータや出力、または頻繁に変更されないデータや出力をキャッシュする場合です。例えば、定期的に変更され、頻繁に使用される高くつくデータはを適切にキャッシュすれば、パフォーマンスとスケーラビリティは大きく向上するかもしれません。使用頻度が更新頻度より高いデータは、キャッシング対象の候補となります。
-
揮発性データと不揮発性データを分ける - 操作支援やヘルプ システムなどの静的コンテンツをカプセル化し、揮発性の高いデータと別にするように、ユーザー コントロールを設計してください。これにより、静的データのキャッシュが可能となり、サーバーへの負荷を小さくすることができます。
-
正しいキャッシング メカニズムを選択する - データのキャッシングには、さまざまな方法があります。正しい方法は、シナリオによって異なります。通常、ユーザー独自データは、Session オブジェクトに格納されます。静的ページや、多くのユーザーに提供されるパーソナライズされていないページなどの一部の動的ページは、ASP.NET の出力キャッシュや応答キャッシュにより、キャッシュできます。ページの静的コンテンツは、出力キャッシュとユーザー コントロールの組み合わせにより、キャッシュできます。ASP.NETのキャッシング機能は、キャッシュの更新のための内蔵メカニズムを提供します。これは、アプリケーション状態、セッション状態、その他のキャッシング手法では、提供されていません。
不要な例外を避ける。
例外は、アプリケーションに大きなオーバーヘッドをもたらします。ロジック フローの制御に例外を使ってはなりません。可能な限り、例外を避けるようにコードを設計してください。例えば、ユーザー入力を検証し、例外の発生し得る既知の状況を確認してください。また、不要な処理を避けるため、早い段階で異常終了するようにコードを設計してください。
アプリケーションが例外を処理しない場合、例外はスタックで上向きに伝達され、最終的に ASP.NET の例外ハンドラで処理されることになります。例外処理ストラテジを設計する際は、以下を考慮してください。
-
例外を避けるようにコードを設計する - ユーザー入力を検証し、例外の発生し得る既知の状況を確認してください。例外を避けるようにコードを設計してください。
-
ロジック フローを制御するために例外を使わないようにする - 通常のアプリケーション ロジック フローを制御するために例外管理を使うことは避けてください。
-
すべての例外についてグローバル ハンドラに依存しないようにする - 例外が発生すると、ランタイムはスタックを操作し、探索します。ランタイムが例外ハンドラを求めてスタックを探索するほど、例外処理に伴う負担は大きくなります。
-
例外は発生場所の近くでキャッチし処理する - 可能な限り、例外は発生場所の近くでキャッチし、処理してください。これにより、スタックでの過剰かつ高くつく探索と操作を避けることができます。
-
処理できない例外をキャッチしない - コードが例外を処理できない場合、try / finally ブロックを使い、例外が発生しているかどうかにかかわらず、リソースを閉じるようにしてください。try / finally ブロックを使うと、リソースは例外発生時に finally ブロックでクリーンアップされ、例外を適切なハンドラに入れられるようになります。
-
高くつく作業を避けるために早い段階で異常終了する - 依存するタスクが異常終了した場合に、高くつく、または所要時間の長い作業を回避できるようにコードを設計してください。
-
管理者のために例外を詳しくログする - 例外ログメカニズムを実装し、管理者や開発者が問題を特定して是正できるように、例外に関する詳しい情報を記録してください。
-
ユーザーに対する例外の詳細情報の表示を避ける - ユーザーに対し、詳しい例外情報を表示しないようにし、セキュリティを保つと共にクライアントへ送るデータ量を抑えてください。
実装に関する考慮事項
アプリケーションの設計からアプリケーションの展開に移る際に、ASP.NET アプリケーションの技術詳細に注意を払ってください。ASP.NET パフォーマンスの主な指標には、応答時間、スループット速度、リソース管理などがあります。
応答時間は、ページ サイズを小さくする、サーバー コントロールへの依存度を小さくする、パッファを使ってクライアントとの不要な通信を減らす、といった方法で短縮することができます。リソースをキャッシュすることで、不必要な作業を回避できます。
スループットは、スレッドを効果的に使うことによって向上させることができます。スレッド プールをチューニングし、競合を減らすと共に、スレッドをブロックしないようにしてください。スレッドをブロックすると、利用可能なワーカー スレッドの数が減ってしまうためです。
不適切なリソース管理は、サーバーの CPU やメモリに対する負荷増加の原因となり得ます。リソース利用は、プールしたリソースを効果的に使う、開いたリソースを明示的に閉じる、または破棄する、文字列管理を効率的に行う、といった方法で向上させることができます。
ベスト プラクティスに基づく実装ガイドラインに従えば、適切に改良したコードと、適切に設定したアプリケーション プラットフォームを使って、アプリケーションのパフォーマンスを向上させることができます。以下の各節では、ASP.NET の機能とシナリオについてのパフォーマンス上の考慮事項を説明します。
スレッド処理についての説明
ASP.NET は、.NET スレッド プールのスレッドを使って要求を処理します。スレッド プールは、スレッド初期化コストを既に発生させたスレッド群を保守します。したがって、これらのスレッドは容易に再利用可能です。NET スレッド プールはまた、セルフ チューニングをします。CPU などについてリソース利用を監視するほか、新しいスレッドを加えたり、必要に応じてプールのサイズを小さくしたりします。通常、スレッドをマニュアルで作成して作業を実行するのは、避けるべきです。代わりに、スレッド プールのスレッドを使ってください。同時に、アプリケーションが長期にわたってブロックするような操作を実行しないようにすることは重要です。このような長期にわたるブロックは、スレッド プールの枯渇や HTTP 要求の拒否に直結する場合があります。
競合を減らすための方策
競合を減らすための方策は、ASP.NET スレッド プールのチューニングを始めるための、経験に基づいた適切な手法を提供するものです。以下の条件が当てはまる場合、表 6.1 で示す、Microsoft 製品グループ推奨の設定を用いることを検討してください。
- 利用可能な CPU がある。
- アプリケーションが、Web メソッドの呼び出しやファイル システムへのアクセスなどの I/O 依存処理を実行する。
-
ASP.NET Applications/Requests in Application Queue パフォーマンス カウンタが、キューに入った要求があることを示している。
表 6.1: 競合を減らすための推奨スレッド処理設定
| コンフィギュレーション設定 | デフォルト値 (.NET Framework 1.1) | 推奨値 |
| maxconnection | 2 | 12 * CPU 数 |
| maxIoThreads | 20 | 100 |
| maxWorkerThreads | 20 | 100 |
| minFreeThreads | 8 | 88 * CPU 数 |
| minLocalRequestFreeThreads | 4 | 76 * CPU 数 |
この問題に対応するためには、Machine.config ファイルの以下のアイテムを、設定する必要があります。以下の節で示す推奨変更を、単独でなく、設定をまたいで適用してください。各設定についての詳細説明は、第 17 章「.NET アプリケーション パフォーマンスのチューニング」の「スレッド プール属性」を参照してください。
-
maxconnection を 12 * CPU 数にセットする - この設定は、クライアントから初期化可能な外向きの HTTP 接続の最大数を管理するものです。この場合、ASP.NET がクライアントです。maxconnection は、12 * CPU 数にセットしてください。
-
maxIoThreads を 100 にセットする - この設定は、.NET スレッド プールの I/O スレッドの最大数を管理するものです。この数は、利用可能な CPU の数によって自動的に掛けられます。maxloThreads は、100 にセットしてください。
-
maxWorkerThreads を 100 にセットする - この設定は、スレッド プールのワーカー スレッドの最大数を管理するものです。この数は、利用可能な CPU の数によって自動的に掛けられます。maxWorkerThreads は、100 にセットしてください。
-
minFreeThreads を 88 * CPU 数にセットする - ワーカー プロセスは、スレッド プール内の利用可能なスレッド数がこの設定値を下回っている場合に、すべての受信要求をキューに入れます。この設定により、並行処理可能な要求の数を minFreeThreads から maxWorkerThreads までに効果的に制限できます。minFreeThreads は、88 * CPU 数にセットしてください。これにより、(maxWorkerThreads を 100 として) 並行処理要求数を 12 に制限します。
-
minLocalRequestFreeThreads を 76 * CPU 数にセットする - ワーカー プロセスは、スレッド プール内の利用可能なスレッド数がこの設定値を下回っている場合に、(Web アプリケーションが要求をローカル Web サービスに送っていれば) ローカル ホストからの要求をキューに入れます。この設定は minFreeThreads に似ていますが、ローカル コンピュータからのローカル ホスト要求のみに適用されます。minLocalRequestFreeThreads は、76 * CPU 数に設定してください。
メモ: この節で掲げた推奨値は、常に適用すべきものではありません。これらは、最初に試すための数値です。テストした上で、それぞれのシナリオに最適な設定を見つけてください。アプリケーションを新しいコンピュータへ移した場合は、新しい CPU 数に基づいて再計算し、再設定するようにしてください。
ASPX Web ページが Web サービスを要求ごとに複数回呼び出す場合は、以下の推奨事項を適用してください。
速い処理の求められるオペレーションには、受信要求の処理について、ASP.NET ランタイム を 12 スレッドに制限する、という推奨アプローチが最適です。この制限により、コンテキスト切り替え数も減らされます。アプリケーションが所要時間の長い呼び出しを実行する場合は、まず、「所要時間の長いタスクをブロックしない」の節で示した設計アプローチを検討してください。これを適用できない場合は、100 maxWorkerThreads から始め、minFreeThreads はデフォルトのままにしてください。このシナリオでは、要求はシリアル化されなくなります。次に、アプリケーションをテストしてみて、CPU 利用度が高く、コンテキスト切り替えが頻繁に発生している場合は、maxWorkerThreads を減らすか、minFreeThreads を増やしてテストしてください。
方策がうまくいっていれば、以下のようになります。
- CPU 利用度が上がる。
- ASP.NET Applications\Requests/Sec パフォーマンス カウンタによって表されるスループットが向上する。
- ASP.NET Applications/Requests in Application Queue パフォーマンス カウンタによって表されるアプリケーション キューが減る。
推奨設定によってアプリケーションのパフォーマンスが向上しない場合は、CPU 依存のシナリオかもしれません。スレッドを追加すると、スレッド コンテキスト切り替えは増加します。詳細は、第 17 章「.NET アプリケーション パフォーマンスのチューニング」の「ASP.NET のチューニング」を参照してください。
詳細情報
詳細は、Microsoft サポート技術情報の Knowledge Base 文書 821268 「PRB: ASP.NET アプリケーションから Web サービス要求を行うと、競合、パフォーマンスの低下、およびデッドロックが発生する」 を参照してください。
スレッド処理に関するガイドライン
ここでは、ASP.NET におけるスレッド処理の効率性を高めるのに役立つガイドラインを扱います。以下に、そのガイドラインを掲げます。
-
競合を減らすための方策を用いてスレッド プールをチューニングする。
-
バースト負荷に対してminIoThreads と minWorkerThreads を考慮する。
-
スレッドを要求ごとに作成しない。
-
スレッドをブロックしない。
-
追加的な並行作業がなければ非同期呼び出しを避ける。
競合を減らすための方策を用いてスレッド プールをチューニングする。
利用可能な CPU があって要求をキューに入れる場合、ASP.NET スレッド プールを設定してください。その方法についての詳細は、前述した「スレッド処理についての説明」の「競合を減らすための方策」を参照してください。「スレッド処理についての説明」で示しているのは、最初に試すための推奨設定です。
アプリケーションが共通言語ランタイム (CLR) スレッド プールを使っている場合、スレッド プールを適切にチューニングすることが重要です。正しくチューニングしなければ、競合問題、パフォーマンス上の問題、場合によってはデッドロックを経験することになるかもしれません。アプリケーションは、以下の条件が当てはまる場合、CLR スレッド プールを使っているかもしれません。
- アプリケーションが Web サービス呼び出しをする。
- アプリケーションが WebRequest または HttpWebRequest クラスを使って外向きの Web 要求をする。
- アプリケーションが、QueueUserWorkItem メソッドを呼び出して明示的に作業をスレッド プールに入れる。
詳細情報
詳細は、Microsoft サポート技術情報の Knowledge Base の文書 821268「PRB: ASP.NET アプリケーションから Web サービス要求を行うと、競合、パフォーマンスの低下、およびデッドロックが発生する」 を参照してください。
バースト負荷に対してminIoThreads と minWorkerThreads を考慮する。
アプリケーションが、長い休止期間をはさみながらバースト負荷を経験する場合、スレッド プールは、スレッドの最適レベルに達するのに必要な時間を得られないかもしれません。バースト負荷は、多数のユーザーが突然、同時にアプリケーションに接続する場合に発生します。minIoThreads 設定と minWorkerThreads 設定により、負荷条件に対し、それぞれ I/O スレッド数とワーカー スレッド数の下限を設定できます。
本稿執筆時点で、これらの設定には、サポートされた修正プログラムが必要となっています。詳細は、Microsoft サポート技術情報の Knowledge Base の以下の文書を参照してください。
スレッドを要求ごとに作成しない。
スレッドの作成は、マネージ リソースとアンマネージ リソースの両方の初期化を必要とする、大きな負担を伴う処理です。ASP.NET アプリケーションや Web サービスなどのサーバー ベースのアプリケーションでは、スレッドを要求ごとにマニュアルで作成しないようにするべきです。
CPU 依存でなく、呼び出しと並行して処理できる作業がある場合は、非同期呼び出しを検討してください。例として、ファイル読み出しやファイル書き込みを含むディスク I/O 依存の処理、他の Web メソッドの呼び出しを含むネットワーク I/O 依存処理などがあります。
.NET Framework の提供するインフラストラクチャを利用し、Beginsynchronous メソッドと Endsynchronous メソッド (synchronous は同期メソッド名を表す) によって非同期処理を実行できます。この非同期呼び出しパターンが適切で無い場合は、CLR スレッド プール のスレッドを使うことを検討してください。以下のコードでは、メッセージを、スレッド プールの別スレッドで動作させるために、キューに入れる方法を示しています。
WaitCallback methodTarget = new WaitCallback(myClass.UpdateCache);
bool isQueued = ThreadPool.QueueUserWorkItem(methodTarget);
スレッドをブロックしない。
現在の要求スレッドをブロックする ASP.NET ページから処理を実行すると、他の ASP.NET 要求の処理に利用可能なスレッド プール内のスレッドが、1 つ減ることになります。スレッドは、ブロックしないようにしてください。
追加的な並行作業がなければ非同期呼び出しを避ける。
Web アプリケーションからの非同期呼び出しは、呼び出し完了待機中に実行すべき追加的な並行作業がアプリケーションにあり、その呼び出しによって実行される作業が CPU 依存でない場合にのみ、行ってください。内部的には、非同期呼び出しは、スレッド プールのワーカー スレッドを使います。実質的に、追加スレッドを使っていることになります。
Web メソッドの呼び出しやファイル処理の実行などの非同期 I/O 呼び出しをするのと同時に、呼び出しをしたスレッドは開放され、別の非同期呼び出しや他の並行作業など、追加的な作業を実行できるようになります。その後、これらすべてのタスクの完了を待つことができます。CPU 依存でない複数の非同期呼び出しを実行し、それらを同時に処理することにより、スループットを向上させることができます。
詳細情報
ASP.NET におけるスレッド処理と非同期通信についての詳細は、"ASP.NET Pipeline: Use Threads and Build Asynchronous Handlers in Your Server-Side Web Code"を参照してください。
リソース管理
ページやコントロールからの不適切なリソース管理は、Web アプリケーションのパフォーマンスを低下させる主な原因の 1 つです。不適切なリソース管理は、CPU に大きな負荷をかけ、大量のメモ:リを消費しかねません。CPU 限界値 やメモリ限界値を超えると、サーバーへの負荷が下がるまで、アプリケーションはリサイクルされるか、ブロックされる場合があります。詳細は、第 3 章「アプリケーション パフォーマンスのための設計ガイドライン」の「リソース管理」を参照してください。以下のガイドラインを、効率的なリソース管理に役立ててください。
-
リソースをプールする。
-
開いたリソースについて明示的に Dispose または Close を呼び出す。
-
プールしたリソースをキャッシュまたはブロックしない。
-
アプリケーションのアロケーション パターンを知っておく。
-
リソースは遅く確保して早く手放す。
-
要求ごとに偽装しない。
リソースをプールする。
ADO.NETには、完全自動のデータベース コネクション プーリングが組み込まれており、特別なコーディングは必要ありません。データベースへの各アクセス要求に対し、同じ接続文字列を使うようにしてください。
プールしたリソースは、できる限り速やかに開放し、プールに戻してください。プールしたリソースをキャッシュすることや、プールしたリソースの保持中に長いブロック呼び出しをすることは、避けてください。その間、他のクライアントがそのリソースを使えないためです。また、複数の要求をまたいでオブジェクトを保持してはなりません。
開いたリソースについて明示的に Dispose または Close を呼び出す。
IDisposable インターフェイスを実装したオブジェクトを使う場合は、そのオブジェクトが提供していれば、Dispose メソッド、または Close メソッドを呼び出すようにしてください。Close メソッドまたは Dispose メソッドを呼び出さないと、オブジェクトは、クライアントが使用しなくなった後も、メモリに長い間残り続けることになります。これにより、クリーンアップは遅れ、メモリ使用量は増加します。明示的に閉じるべき共有リソースの例としては、データベース コネクションやファイルなどがあります。Try / finally ブロックの finally クローズは、オブジェクトの Close メソッドや Dispose メソッドを呼び出すのに適した場所です。以下は、Visual BasicR .NET による、そのコード例です。
Try
conn.Open()
...
Finally
If Not(conn Is Nothing) Then
conn.Close()
End If
End Try
Visual C#R では、using ブロックを使って破棄すべきリソースをラップすることができます。using ブロックが完了すると、using ステートメントのカッコ内に掲げたオブジェクトのDispose が呼び出されます。以下のコードでは、using ブロックを使い、破棄すべきリソースをラップしています。
SqlConnection conn = new SqlConnection(connString);
using (conn)
{
conn.Open();
. . .
} // ここで接続オブジェクト conn の Dispose を自動呼び出し.
詳細情報
詳細は、第 5 章「マネージ コード パフォーマンスの向上」の「Finalize と Dispose に関するガイドライン」を参照してください。また、第 12 章「ADO.NET パフォーマンスの向上」の「明示的に接続を閉じる」を参照してください。
プールしたリソースをキャッシュまたはブロックしない。
アプリケーションが、プールしたリソースを使っている場合、リソースを開放してプールに戻してください。プールしたリソースのキャッシュや、プールしたリソースからのブロック呼び出しにより、他のユーザーが利用可能なプールしたリソースは減ってしまいます。プールしたリソースには、データベース コネクション、ネットワーク コネクション、Enterprise Servicesのプールしたオブジェクトなどがあります。
アプリケーションのアロケーション パターンを知っておく。
不適切なメモリ アロケーション パターンにより、ガベージ コレクタがジェネレーション 2 のオブジェクトの回収に作業のほとんどを費やすようになってしまう場合があります。ジェネレーション 2 のオブジェクトの回収は、アプリケーション パフォーマンスの低下や、CPUへの負荷の増加につながります。
短時間内に多数の一時アロケーションを発生させるようなコーディングは、ガベージ コレクタへの負担を増加させます。例えば、タイトなループ内で += 演算子を使って多数の文字列連結操作を実行する場合や、あらゆる要求に String.Split を使う場合、ガベージ コレクタへの負担は増加しかねません。これらの処理はすべて、隠れたオブジェクト (一時アロケーション) を発生させます。CLR プロファイラや System Monitor などのツールを使い、アプリケーションのアロケーション パターンをよく把握してください。
詳細情報
詳細は、このガイドの「How To 情報」にある「CLR プロファイラの使用方法」を参照してください。また、第 15 章「.NET アプリケーション パフォーマンスの計測」の「CLR とマネージ コード」を参照してください。
ガベージ コレクションとジェネレーションのメカニズムについての詳細は、第 5 章「マネージ コード パフォーマンスの向上」の「ガベージ コレクションについての説明」を参照してください。
リソースは遅く確保して早く手放す。
重要で限りのある共有リソースは、必要となる直前に開き、できる限り速やかに開放してください。これらの共有リソースの例としては、データベース コネクション、ネットワーク コネクション、トランザクションなどが挙げられます。
要求ごとに偽装しない。
Web サーバーにおいて、呼び出し元を特定し、必要なら承認するようにしてください。システム リソースやアプリケーションをまたぐリソースには、Web アプリケーション プロセスのID、または固定サービス アカウントを使ってアクセスしてください。システム リソースとは、イベント ログなどのリソースをいいます。アプリケーションをまたぐリソースとは、データベースなどのリソースをいいます。要求ごとの偽装を避けることにより、セキュリティ オーバーヘッドを最小限に抑え、リソース プーリングを最大化することができます。
メモ: 偽装自体が、パフォーマンス上の問題の原因となるわけではありません。しかし、多くの場合で、偽装は効率的なリソースのプールを妨げます。これは、パフォーマンス上およびスケーラビリティ上の問題の原因として、よく見られるものです。
ページ
ASP.NET ページと、コードビハインドのページ ロジックの効率性は、Web アプリケーションの全体的なパフォーマンスを大きく左右します。以下のガイドラインは、個々の .aspx ファイル、および .ascx ファイルの開発に関連しています。
-
ページ サイズを小さくする。
-
バッファリングを有効にする。
-
Page.IsPostBack を使って冗長的な処理をなくす。
-
ページのコンテンツを分割してキャッシングを効率化すると共にレンダリングを減らす。
-
ページがバッチ コンパイルされるようにする。
-
デバッグを false に設定する。
-
負荷の大きいループを最適化する。
-
Response.Redirect の代わりに Server.Transfer の使用を検討する。
-
クライアント側検証を用いる。
ページ サイズを小さくする。
大きなサイズのページを処理すると、CPU 負荷やネットワーク帯域消費は増加し、クライアントに対する応答時間は長くなります。複数のタスクをカバーする大きなページのデザインや作成は、特に各要求に対して実行するタスクが通常は数個しかない場合は、避けてください。可能な場合は、ページを論理的に分割してください。
以下のうち一つ、またはすべてに従い、ページサイズを小さくしてください。
- ページで静的スクリプトを含むスクリプトを使い、クライアントが、以後の要求のためにこれらのスクリプトをキャッシュできるようにする。
<script language=jscript src="scripts\myscript.js">
- クライアントに応答を送る前に、空白を作るタブやスペースなどを削除する。空白を削ると、ページ サイズは大幅に小さくなります。以下は、空白を含んでいるテーブルの例です。
// 空白ありで
<table>
<tr>
<td>hello</td>
<td>world</td>
</tr>
</table>
以下は、空白のないコードの例です。
// 空白なしで
<table>
<tr><td>hello</td><td>world</td></tr>
</table>
ノートパッドを使ってこれらのテーブルを別々のテキストファイルに保存し、各ファイルのサイズを確認してみてください。2 つ目のテーブルは、空白をなくしただけで数バイトを節約していることが分かります。1,000 行のテーブルなら、空白をなくすだけで応答時間を短縮できます。インターネット シナリオでは、空白をなくしても大きな効果は得られないかもしれません。しかし、速度の遅いクライアントを含むインターネット シナリオなら、空白をなくすことによって応答時間を大幅に短縮できる場合もあります。また、HTTP 圧縮を検討することもできます。ただし、HTTP 圧縮は CPU 利用度に影響を与えます。
常にこのような方法でページを設計できるとは限りません。従って、Internet Server API (ISAPI) フィルタ または HttpModule オブジェクトを使うのが、空白をなくすための最も効果的な方法です。ISAPI フィルタは、HttpModule よりも高速です。ただし、ISAPI フィルタは作成が難しい上に、CPU 利用度を増加させます。また、IIS 圧縮を検討可能な場合もあります。IIS 圧縮は、メタベース エントリを使って追加できます。
- ページ サイズの調整にはその他、以下の方法があります。
- ビュー ステートを、必要でなければ無効にする。詳細は、この章で後述する「ビュー ステート」を参照してください。
- グラフィックの使用を制限し、圧縮グラフィックの使用を検討する。
- クライアントに同じ書式設定ディレクティブを繰り返し送らないようにするため、カスケーディング スタイル シート (CSS) の使用を検討する。
- 長いコントロール名は、特に DataGrid コントロール または Repeater コントロールで繰り返しのあるものについては、避ける。コントロール名は、独自の HTML ID 名を生成するために使われます。10 文字のコントロール名は、ネストされた、繰り返しのあるコントロールの中で使うと、簡単に 30 文字から 40 文字になってしまう場合があります。
メモ: ASP.NET プロセス モデルにおいて、ASP.NET ワーカー プロセスによるクライアントへの応答は最初、31 キロバイト (KB) のチャンクで、IIS を介して送られます。これは、.NET Framework 1.1 に適用されていますが、今後のバージョンでは変わるかもしれません。ASP.NET が IIS を介して 31 KB のチャンクを送る頻度が増えるほど、ページの動作は遅くなります。ページをブラウズし、ソースを表示し、ファイルをディスクにセーブすることにより ASP.NET がページに要求するチャンク数を、決めることができます。チャンク数を決めるには、ページ サイズを 31 で割ってください。
詳細情報
IIS 圧縮についての詳細は、Microsoft サポート技術情報の Knowledge Base の文書 322603 「HOW TO: IIS の ASPX 圧縮を有効にします」 を参照してください。
バッファリングを有効にする。
バッファリングはデフォルトで有効となっているため、ASP.NET はサーバーで作業をバッチ化し、クライアントと不要な通信をしないようにします。このアプローチの欠点は、遅いページのレンダリングは、完了するまでクライアント側から見られないという点です。この状況での次善策として、Response.Flush の使用があります。Response.Flushは、出力をクライアント側のポイントまで送ります。バッファ機能が無効な、遅いネットワークにつながっているクライアントは、サーバーの応答時間に影響を与えます。これは、サーバーがクライアントからの肯定応答を待たなければならないためです。最初の送信時にヘッダーを送っているため、待機を後回しにすることはできません。
バッファリングが無効になっている場合は、以下の方法で有効にできます。
- ページでプログラムによって有効にする。
// Response.Buffer は後方互換性のために利用可能; 使用しない
Response.BufferOutput = true;
- @Page ディレクティブを使い、ページ レベルで有効にする。
<%@ Page Buffer = "true" %>
- Web.config ファイルまたは Machine.config ファイルの <pages> 要素を使い、アプリケーション レベルまたはコンピュータ レベルで有効にする。
<pages buffer="true" ...>
ASP.NET プロセス モデルを使って ASP.NET アプリケーションを動作させる場合、バッファリングを有効にすることは、さらに重要になります。ASP.NET ワーカー プロセスはまず、応答バッファの形で IIS に応答します。これらの応答バッファのサイズは、31 KBです。IIS は、応答バッファを受信すると、実際の応答をクライアントへ返送します。バッファリングが無効になっていると、ASP.NET は 31 KBのバッファ全体を使う代わりに、バッファへ数個の文字を送ることしかできなくなります。この場合、ASP.NET でも IIS でも、追加的な CPU 処理が発生することになります。また、IIS プロセスにおけるメモリ消費が、大幅に増加してしまう場合もあります。
Page.IsPostBack を使って冗長的な処理をなくす。
Page.IsPostBack プロパティを使い、ページが最初にロードされたときにのみ、ページ初期化を実行し、クライアント ポストバックに応答して実行しないようにしてください。以下のコードは、Page.IsPostBack プロパティの使い方を示したものです。
if (Page.IsPostBack == false) {
// 初期化ロジック
} else {
// クライアント ポストバック ロジック
}
ページのコンテンツを分割してキャッシングを効率化すると共にレンダリングを減らす。
ページのコンテンツを分割し、キャッシュしやすくしてください。ページのコンテンツを分割すると、それを取得、表示、キャッシュする上での選択肢が増えます。ユーザー コントロールを使い、操作アイテム、メニュー、広告、著作権表示、ページ ヘッダー、ページ フッターなどの静的コンテンツを分割できます。また、動的コンテンツやユーザーの独自コンテンツについても、キャッシュ時の柔軟性を最大限に高められるよう、分割するべきです。
詳細情報
詳細は、この章で後述する「部分ページまたはフラグメント キャッシュ」を参照してください。
ページがバッチ コンパイルされるようにする。
プロセスにロードされるアセンブリの数が増えるにつれ、仮想アドレス空間が断片化していくことがあります。この場合は、メモリ不足状態に陥りやすくなります。プロセスに多数のアセンブリをロードしないように、ASP.NET は、同じディレクトリ内のすべてのページを、1 つのアセンブリにコンパイルしようとします。このコンパイルは、そのディレクトリ内のページに対する最初の要求があったときに発生します。以下のアプローチにより、バッチ コンパイルされないアセンブリの数が少なくなるようにしてください。
- 複数の言語を同じディレクトリ内に混在させない。C# や Visual Basic .NET などの複数の言語が、同じディレクトリ内のページで使われていると、ASP.NET はアセンブリを言語ごとに分けてコンパイルします。
- コンテンツの更新によって追加アセンブリがロードされないようにする。詳細は、この章で後述する「展開上の考慮事項」を参照してください。
- 次節で説明するように、ページ レベルおよび Web.config ファイルで、debug 属性を false に設定する。
デバッグを false に設定する。
debug が true に設定されていると、以下の状況が発生してしまいます。
- ページがバッチ コンパイルされない。
- ページがタイムアウトしない。Web サービス エラーなどの問題が発生したとき、Web サーバーが要求をキューに入れ始め、応答しなくなってしまう場合があります。
- Temporary ASP.NET Files フォルダで追加的なファイルが生成される。
- 生成コードに System.Diagnostics.DebuggableAttribute 属性が追加される。これにより、生成コード情報に対する追加的なトラックが発生し、やはり確実な最適化を妨げられてしまいます。
パフォーマンス テストやアプリケーションの生産開始の前に、Web.config ファイルやページ レベルで debug が false に設定されていることを確認してください。ページ レベルでは、debug はデフォルトで false に設定されています。開発期間中にこの属性を設定しなければならない場合は、以下のコードで示すように、Web.config ファイル レベルで設定しておくべきです。
<compilation debug="false" ... />
以下は、ページ レベルで debug を false に設定する方法です。
<%@ Page debug="false" ... %>
メモ: よく見られるミスに、開発中にページ レベルでこの属性を設定し、アプリケーションの生産開始前に設定し直すのを忘れてしまう、というものがあります。
負荷の大きいループを最適化する。
いかなるアプリケーションでも、負荷の大きいループは、パフォーマンス上の問題の原因となり得ます。ループ内のコードに関連するオーバーヘッドを減らすには、以下の推奨事項に従うべきです。
- フィールドやプロパティへの反復的なアクセスを避ける。
- ループ内のコードを最適化する。
- 頻繁に呼び出すコードをループ内にコピーする。
- 再帰処理をループ処理に置き換える。
- パフォーマンス重視のコード パスでは ForEach の代わりに For を使う。
詳細情報
この節で示した推奨事項についての詳細は、第 5 章「マネージ コード パフォーマンスの向上」の「反復処理とループ処理」を参照してください。
Response.Redirect の代わりに Server.Transfer の使用を検討する。
Response.Redirect は、新しい URL によるサーバーへの新しい要求の送信をクライアントに求めるメタタグを、クライアントへ送ります。Server.Transfer を使うと、サーバー側呼び出しにより、この迂回的なアプローチを回避できます。Server.Transfer 使用時は、ブラウザで表示される URL は変わらず、負荷テストツールは、ページ サイズを誤って報告するかもしれません。これは、同じ URL について、異なるページがレンダリングされるためです。
Server.Transfer メソッド、Response.Redirect メソッド、Response.End メソッドはすべて、内部で Response.End を呼び出すため、ThreadAbortException 例外を発生させます。Response.End を呼び出すと、この例外が発生します。Response.End の内部呼び出しを避けるため、オーバーロードしたメソッドを使い、第 2 引数として false を渡すことを検討してください。
詳細情報
詳細は、Microsoft サポート技術情報の Knowledge Base 文書 312629 「PRB: Response.End、Response.Redirect、または Server.Transfer メソッドを使用すると ThreadAbortException が発生する」 を参照してください。
クライアント側検証を用いる。
データの事前検証により、ユーザー要求の処理に必要なラウンド トリップ数を減らすことができます。ASP.NET では、検証コントロールを使い、ユーザー入力のクライアント側検証を実装できます。
メモ: セキュリティ上の理由により、サーバー側検証も使うようにしてください。
詳細情報
検証コントロールについての詳細は、以下を参照してください。
サーバー コントロール
サーバー コントロールを利用し、よく使う機能のカプセル化と再利用を実現できます。簡潔なプログラミング抽象化をもたらすサーバー コントロールは、ASP.NET アプリケーション構築のための推奨方法とされています。サーバー コントロールを適切に使えば、出力 キャッシュとコードの保守性を向上させることができます。パフォーマンスの最適化のために見直すべき主な範囲は、ビュー ステートとコントロール構成です。サーバー コントロール開発時は、以下のガイドラインに従ってください。
-
サーバー コントロールにおけるビュー ステートの使用状態を確認する。
-
適切な場合はユーザー コントロールを使う。
-
コントロールを階層化しすぎないようにする。
サーバー コントロールにおけるビュー ステートの使用状態を確認する。
ビュー ステートは、サーバーでシリアル化され、シリアル化解除されます。CPU サイクルを節約するには、アプリケーションで使用するビュー ステートの数を減らしてください。不必要なビュー ステートは、無効にします。以下のうち、1 つでも当てはまる場合は、ビュー ステートを無効にしてください。
- ユーザー入力の無い読み出し専用ページを表示している。
- サーバーにポストバックしないページを表示している。
- ポストバック データを確認することなく、ポストバックごとにサーバー コントロールを再構築している。
詳細情報
ビュー ステートについての詳細は、この章で後述する「ビュー ステート」を参照してください。
適切な場合はユーザー コントロールを使う。
HTTP プロトコルはステートレスです。しかし、サーバーコントロールは、ビュー ステートによってページ要求間の状態情報を管理する、豊かなプログラミング モデルを提供します。サーバー コントロールは、自身と子コントロールを確立するために、固定量の処理を要求します。この結果、サーバー コントロールは、HTML コントロール、場合によっては静的テキストと比べても、高くついてしまいます。サーバー コントロールが高くついてしまうシナリオの例を、以下に掲げます。
-
狭帯域での大きなペイロード - ページ内のコントロール数が多いほど、ネットワーク ペイロードは大きくなります。従って、複数コントロールにより、クライアントへ送られた応答に対する TTLB (最終バイトまでの時間) と TTFB (先頭バイトまでの時間) は減ります。クライアントが低速のダイアルアップ接続を使用しているシナリオなど、クライアント-サーバー間の帯域幅が限られている場合、大きなビュー ステート ペイロードを伴うページは、パフォーマンスに大きな悪影響を及ぼし得ます。
-
ビュー ステート オーバーロード - ビュー ステートは、サーバーでシリアル化、およびシリアル化解除されます。CPU にかかる負荷は、ビュー ステートのサイズに比例して大きくなります。ビュー ステートを使うサーバー コントロールに、プログラミング時にシリアル化可能なオブジェクトをビュー ステート プロパティに加えるのは、簡単なことです。しかし、これにより、オーバーヘッドは増加します。また、計算済みデータの格納や、一般データの複数コピーの格納などの手法も、不要なオーバーヘッドの増加を招きます。
-
複合コントロールまたは多数コントロール - DataGrid などの複合コントロールを含むページは、ビュー ステートのフットプリント <メモリ占有スペース> を増加させる場合があります。多数のサーバー コントロールを含むページも、ビュー ステートによるメモリ使用量を増加させる場合があります。可能な場合は、この節で後述する、別のアプローチを選択してください。
さまざまな機能を伴う対話を必要としていないなら、サーバー コントロールを、提供したいユーザー インターフェイスをインライン化したものに置き換えてください。以下の状況では、サーバー コントロールの使用を避けることを検討すべきです。
- ポストバックをまたいで状態情報を保持する必要がない。
- コントロールに現れるデータが静的データである。例えば、ラベルは静的データです。
- サーバー側のコントロールへプログラム的にアクセスする必要がない。
- コントロールが読み出し専用データを表示している。
- ポストバック処理中にコントロールを必要としない。
サーバー コントロールに代わるものには、シンプルなレンダリング、HTML 要素、Response.Write 呼び出しのインライン化、インライン アングル ブラケット (<% %>) などがあります。パフォーマンスとの間で、トレードオフを図ることが大切です。オーバーヘッドが許容範囲内で、アプリケーションがパフォーマンス目標値を満たしているなら、過剰な最適化は避けてください。
コントロールを階層化しすぎないようにする。
コントロールをあまりにも階層化しすぎると、サーバー コントロールとその子コントロールを作成するコストは、大きくなってしまいます。過剰な階層化は、インライン コントロールを使う別の設計アプローチや、サーバーコントロールの階層化の程度を抑えることによって回避可能な、余計な処理を招きます。これは、コンテナ内に追加的な子コントロールを生成させる、Repeater、DataList、DataGrid などのリスト コントロールを使っている場合、特に重要です。
例えば、以下の Repeater コントロールについて、考慮してください。
<asp:repeater id=r runat=server>
<itemtemplate>
<asp:label runat=server><%# Container.DataItem %><br></asp:label>
</itemtemplate>
</asp:repeater>
データ ソースに 50 のアイテムがあると想定すると、Repeater コントロールを含むページのトレースが有効な場合、ページは実質的に 200 を超えるコントロールを含むことになります。
表 6.2: Repeater コントロール階層の一部
| コントロール ID | 型 |
| Repeater | System.Web.UI.WebControls.Repeater |
| repeater:_ctl0 | System.Web.UI.WebControls.RepeaterItem |
| repeater_ctl0:_ctl1 | System.Web.UI.LiteralControl |
| repeater_ctl0:_ctl0 | System.Web.UI.WebControls.Label |
| repeater_ctl0:_ctl2 | System.Web.UI.LiteralControl |
| repeater:_ctl49 | System.Web.UI.WebControls.RepeaterItem |
| repeater_ctl49:_ctl1 | System.Web.UI.LiteralControl |
| repeater_ctl49:_ctl0 | System.Web.UI.WebControls.Label |
| repeater_ctl49:_ctl2 | System.Web.UI.LiteralControl |
ASP.NET のリスト コントロールは、さまざまなシナリオに対応できるように作られており、特定のシナリオには最適化されていません。パフォーマンス重視のシナリオでは、以下のうち、いずれかのアプローチを選択することができます。
あまり複雑でないデータを表示したいなら、Response.Write を呼び出して自らレンダリングすることも可能です。例えば、以下のコードでは、この節で前述したのと同じ出力が得られます。
for(int i=0;i<datasource.Count;i++)
{
Response.Write(datasource[i] + "<br>");
}
より複雑なデータを表示したい場合は、レンダリングのためにカスタム コントロールを作成することができます。例えば、以下のカスタム コントロールでは、この節で前述したのと同じ出力が得られます。
public class MyControl : Control
{
private IList _dataSource;
public IList DataSource
{
get {return _dataSource;}
set {_dataSource=value;}
}
protected override void Render(HtmlTextWriter writer)
{
for(int i=0;i<_dataSource.Count;i++)
{
writer.WriteLine(_dataSource[i] + "<br>");
}
}
}
詳細情報
サーバー コントロールについての全般的な基礎情報については、Microsoft サポート技術情報の Knowledge Base 文書 306459 「INFO: ASP.NET サーバー コントロールの概要」
データ連結
データ連結もまた、非効率的に用いると、パフォーマンス上の問題を招くことが多いアプローチの 1 つです。データ連結を用いる場合は、以下の推奨事項を考慮してください。
-
Page.DataBind の使用を避ける。
-
DataBinder.Eval の呼び出しを最小限に抑える。
Page.DataBind の使用を避ける。
Page.DataBind の呼び出しにより、ページ レベルのメソッドが呼ばれます。続いてこのメソッドが、データ連結をサポートするページの各コントロールの、DataBind メソッドを呼び出します。ページ レベルの DataBind を呼び出す代わりに、特定コントロールの DataBind を呼び出してください。この両方のアプローチのコード例を、以下で示しています。
以下のコードでは、ページ レベルの DataBind が呼び出されます。この DataBind が、今度は各コントロールの DataBind を呼び出します。
以下のコードでは、特定コントロールの DataBind が呼び出されます。
yourServerControl.DataBind();
DataBinder.Eval の呼び出しを最小限に抑える。
DataBinder.Eval メソッドは、リフレクションを使用し、受け取った引数を調べて結果を返します。100 行 10 列のテーブルなら、各列で DataBinder.Eval を使うと、DataBinder.Eval を 1,000 回呼び出すことになります。このシナリオでは、DataBinder.Eval の使用頻度は、1,000 倍となります。データ連結中の DataBinder.Eval の使用を制限すると、ページ パフォーマンスは大きく向上します。DataBinder.Eval を使った、Repeater コントロール内の ItemTemplate 要素について考慮してください。
<ItemTemplate>
<tr>
<td><%# DataBinder.Eval(Container.DataItem,"field1") %></td>
<td><%# DataBinder.Eval(Container.DataItem,"field2") %></td>
</tr>
</ItemTemplate>
このシナリオで DataBinder.Eval を使わない手法もあります。 その例を、以下に掲げます。
詳細情報
データ連結の詳細は、Microsoft サポート技術情報の Knowledge Base の以下の文書を参照してください。
キャッシングについての説明
キャッシングにより、冗長的な作業を避けることができます。キャッシングを適切に使えば、不要なデータベース検索などの、負荷の大きい処理を回避できます。また、待ち時間の短縮にもつながります。
ASP.NET キャッシュは、ASP.NET アプリケーションに提供される、シンプルで拡張性のあるメモリ内キャッシング サービスです。これは、時間ベースの期限切れ機能を提供すると共に、外部ファイル、ディレクトリなどのキャッシュ キーへの依存性をトラックします。また、キャッシュ内のアイテムが期限切れとなったときに、コールバック関数を呼び出すメカニズムも備えています。キャッシュは、LRU (最小使用頻度) アルゴリズム、設定済みメモリ制限、キャッシュ内のアイテムの CacheItemPriority 列挙値に基づき、アイテムを自動的に削除します。キャッシュ データは、アプリケーションまたはワーカー プロセスのリサイクル時にも、消滅します。
ASP.NET が提供するキャッシング技法は、以下の 3 つです。
-
キャッシュ API
-
出力キャッシュ
-
部分ページ / フラグメント キャッシュ
これらのキャッシング技法について、以下で簡単に説明します。
キャッシュ API
キャッシュ API を使用し、複数ユーザーによって共有 / アクセスされる、アプリケーションにまたがるデータを、プログラム的にキャッシュすべきです。キャッシュ API は、ユーザーに提示する前に、何らかの操作を施す必要のあるデータの置き場所としても適しています。これらのデータの例としては、文字列、配列、コレクション、データ セットなどが挙げられます。
API キャッシュの使用が望ましい、代表的なシナリオには、以下などがあります。
-
ニュース見出し - ほとんどの場合で、ニュース見出しはリアルタイムでは更新されません。10分から20分遅れて更新されるのが、一般的です。複数のユーザーによって共有され、それほど頻繁には更新されないため、多くの場合でキャッシュ API の対象となります。
-
製品カタログ - 通常、製品カタログのデータは、規定の間隔で更新され、アプリケーションをまたいで共有され、クライアントへのコンテンツ送信前に操作されます。このため、製品カタログも、多くの場合でキャッシュ API の対象となります。
以下の状況では、キャッシュ API の使用を避けるべきです。
- キャッシュしようとするデータが、ユーザーの独自データである。この場合は、代わりにセッション状態の使用を検討してください。
- データがリアルタイムで更新される。
- アプリケーションが既に完成しており、コード ベースの更新が望ましくない。この場合は、出力キャッシュの使用を検討してください。
キャッシュ API により、外部条件に依存するキャッシュへの、アイテムの挿入が可能になります。キャッシュ アイテムは、外部条件の変更時に、キャッシュから自動的に除去されます。この機能は、CacheDependency クラスのインスタンスを受け入れる、Cache.Insert メソッドの第 3 引数を使うことによって利用できます。CacheDependency クラスは、さまざまな依存シナリオをサポートする、8 種類のコンストラクタを備えています。これらのコンストラクタには、ファイル ベース、時間ベース、優先順位ベースの依存性が、既存依存性に基づく依存性と共に含まれています。
キャッシュ データを提供する前に、コードを動作させることも可能です。例えば、キャッシュ データを特定の顧客に提供したくても、他の顧客にはリアルタイム更新したデータを提供したくない場合もあるでしょう。このタイプのロジックも、HttpCachePolicy.AddValidationCallback メソッドを使えば実行できます。
出力キャッシュ
出力キャッシュにより、ページ全体のコンテンツを、一定期間内にキャッシュできるようになります。出力キャッシュは、クエリ文字列、ヘッダー、userAgent 文字列に基づく、さまざまな種類のページのキャッシングを可能にします。また、プロキシ、サーバー、クライアントなど、コンテンツをキャッシュすべき場所を判断可能になります。キャッシュ API を使った場合と同様に、データ取得に要する時間は短縮されます。また、コンテンツのレンダリングに要する時間も、短縮されます。要求ことにビューを更新する必要がなく、ユーザーの独自データが含まれていないなら、動的に生成されるページでは、出力キャッシュを利用可能にすべきです。
出力キャッシュが望ましい、代表的なシナリオには、以下などがあります。
-
訪問頻度の高いページ - アプリケーションが完成した後でも、訪問頻度の高いページを特定し、可能な場合はそれらのページで出力キャッシュを利用可能にすれば、アプリケーション全体のパフォーマンスを向上させることができます。
-
レポート - 種類の少ないレポートは、ページへのアクセスのたびにデータの取得や処理を行わないようすることで時間を節約できるため、多くの場合で出力キャッシュによるメリットを得られます。
以下の状況では、出力キャッシュの使用を避けてください。
- ページのデータにプログラム的にアクセスする必要がある。この場合は、代わりにキャッシュ API の使用を検討してください。
- ページの種類が多くなりすぎた。
- 静的データ、動的データ、ユーザー独自データが、ページに混在している。この場合は、フラグメント キャッシングの使用を検討してください。
- ビューごとにリフレッシュしなければならないコンテンツが、ページに含まれている。
部分ページ / フラグメント キャッシュ
部分ページ / フラグメント キャッシュは、出力キャッシュの派生アプローチです。これに含まれている追加属性により、ユーザー コントロール (.ascx ファイル) のプロパティに基づくバリエーションのキャッシュが可能になります。
フラグメント キャッシュは、ユーザー コントロールの使用により、@OutputCache ディレクティブと共に実装されます。ページのコンテンツ全体をキャッシュしたい場合にフラグメント キャッシュを使用するのは、実用的ではありません。ページに静的データ、動的データ、ユーザー独自データが混在している場合は、ユーザー コントロールを作成し、ページを論理パーティションで区切ってください。これらのユーザー コントロールは、メイン ページから独立してキャッシュ可能で、キャッシングによって処理時間の短縮とパフォーマンスの向上を実現できます。
フラグメント キャッシュが望ましい、代表的なシナリオには、以下などがあります。
-
操作メニュー - ユーザー独自ではない操作メニューは、一般的に要求ごとにレンダリングされ、多くの場合で静的なため、フラグメント キャッシュの非常に有力な候補となります。
-
ヘッダーとフッター - ヘッダーとフッターは、基本的に要求ごとに再生する必要のない、静的コンテンツであるため、フラグメント キャッシュの有力候補となります。
以下の状況では、フラグメント キャッシュの使用を避けるべきです。
- ページの種類が多くなりすぎた。
- キャッシュしたユーザー コントロールに、ビューごとにリフレッシュしなければならないコンテンツが含まれている。
アプリケーションが複数のページ上で同じコントロールを使っている場合、ユーザー コントロール @OutputCache ディレクティブの Shared 属性を true に設定し、ページが同じインスタンスを共有するようにしてください。これにより、かなりのメモリ スペースを節約できます。
キャッシングに関するガイドライン
キャッシング ストラテジを設計する際には、以下のガイドラインに従ってください。
-
ページで静的データと動的データを分ける。
-
メモリ制限を設定する。
-
適切なデータをキャッシュする。
-
キャッシュを適切にリフレッシュする。
-
適切なデータ形式でキャッシュする。
-
比較的静的なページのキャッシュには出力キャッシュを用いる。
-
適切なキャッシュ場所を選択する。
-
選択的キャッシュには VaryBy 属性を使う。
-
Windows Server 2003 ではカーネル キャッシュを使う。
ページで静的データと動的データを分ける
部分ページ キャッシュにより、ユーザー コントロールを使ってページの一部分だけをキャッシュすることができます。ユーザー コントロールを使い、ページを分割してください。例えば、静的情報、動的情報、ユーザー独自情報の混在した、以下のようなシンプルなコードを見てください。
[main.aspx]
<html>
<body>
<table>
<tr><td colspan=3>Application Header ? Welcome John Smith</td></tr>
<tr><td>Menu</td><td>Dynamic Content</td><td>Advertisments</td></tr>
<tr><td colspan=3>Application Footer</td></tr>
</table>
</html>
このページを、以下のコードによって分割し、キャッシュすることができます。
[main.aspx]
<%@ Regi
ster TagPrefix="app" TagName="header" src="header.ascx" %>
<%@ Register TagPrefix="app" TagName="menu" src="menu.ascx" %>
<%@ Register TagPrefix="app" TagName="advertisements" src="advertisements.ascx" %>
<%@ Register TagPrefix="app" TagName="footer" src="footer.ascx" %>
<html>
<body>
<table>
<tr><td colspan=3><app:header runat=server /></td></tr>
<tr><td><app:menu runat=server /></td><td>Dynamic
Content</td><td><app:advertisements runat=server /></td></tr>
<tr><td colspan=3><app:footer runat=server /></td></tr>
</table>
</html>
[header.ascx]
<%@Control %>
Application Header ? Welcome <% GetName() %>
[menu.ascx]
<%@Control %>
<%@ OutputCache Duration="30" VaryByParam="none" %>
Menu
[advertisements.ascx]
<%@Control %>
<%@ OutputCache Duration="30" VaryByParam="none" %>
Advertisements
[footer.ascx]
<%@Control %>
<%@ OutputCache Duration="60" VaryByParam="none" %>
Footer
上記のサンプルで示したようにコードを区分けすることにより、ページの選択部分をキャッシュし、処理時間とレンダリング時間を短縮することができます。
メモリ制限を設定する
メモリ制限の設定とチューニングは、キャッシュの最適化に不可欠です。メモリ消費量が設定したメモリ制限の 20 パーセント未満になると、ASP.NET キャッシュは、まずLRU アルゴリズムと、アイテムに割り当てられた CacheItemPriority 列挙値に基づき、キャッシュを小さくし始めます。メモリ制限の設定が高すぎると、予期しないプロセスのリサイクルが発生しかねません。また、アプリケーションで、メモリ不足例外が発生する場合もあります。逆にメモリ制限の設定が低すぎると、ガベージ コレクションの実行に要する時間が増加し、全体的なパフォーマンスが低下することになります。
実証的な検証により、プライベート バイトが 800 メガバイト (MB) を越えると、メモリ不足例外を受ける可能性が高くなることが分かっています。ただし、プライベート バイトをいつ増やすか、または減らすかについて判断する際に注意すべきことは、800 MB というのは、NET Framework 1.0 のみに関連しているということです。.NET Framework 1.1 を備えている場合や、/3 GB スイッチを使っている場合は、1,800 MB まで増やせます。
ASP.NET プロセス モデルを使用している場合、Machine.config ファイルのメモリ制限を、以下のように設定します。
<processModel memoryLimit="50">
この値は、ワーカー プロセスに消費することを認める、物理メモリのパーセンテージを管理します。この値を超えると、プロセスはリサイクルされます。上記のコード サンプルでは、サーバーに 2 ギガバイト (GB) の RAM があるとすれば、利用可能な物理 RAM の総量が、RAM 全体の 50 パーセント未満、つまり 1GB 未満に落ちると、プロセスはリサイクルされます。別の言い方をすれば、ワーカー プロセスが使うメモリが 1 GB 未満になると、プロセスはリサイクルされます。ワーカー プロセス メモリは、process パフォーマンス カウンタ オブジェクトと、private bytes カウンタを使って監視します。
詳細情報
メモリ制限のチューニング方法、および /3GB スイッチについての詳細は、第 17 章「.NET アプリケーション パフォーマンスのチューニング」の「メモリ制限を設定する」と「/3GB スイッチ」を参照してください。
適切なデータをキャッシュする
適切なデータをキャッシュするのは、重要なことです。不適切なデータをキャッシュすると、パフォーマンスを向上させるどころか、逆に低下させることになりかねません。
アプリケーション全体で使われるデータや、複数ユーザーに使われるデータをキャッシュしてください。また、静的データや、作成や取得に大きな負担を伴うデータをキャッシュしてください。取得に大きな負担を伴い、定期的に変更されるデータを適切に管理すれば、パフォーマンスとスケーラビリティを向上させることができます。データ量の大きいサイトでは、データを数秒間キャッシュしてだけでも、パフォーマンスは大きく向上し得ます。データ連結に最適化したシリアル化を用いたデータ セットやカスタム クラスもまた、キャッシングの有力な候補となります。使用頻度が更新頻度より高いデータも、キャッシングの有力な候補です。
データベース コネクションなどの、希少な共有リソースをキャッシュしないでください。キャッシュすれば、競合が発生するためです。また、DataReader オブジェクトは、ベースとなる接続をオープンに保つため、キャッシュに格納しないようにしてください。これらのリソースは、プールすべきです。要求をまたぐユーザーごとのデータについては、キャッシュせずにセッション状態を使用してください。データが要求の独自データで、同じ要求についてデータベースに繰り返しアクセスする代わりに、その要求のライフ タイムの間、そのデータを格納し、渡す必要がある場合は、データを <b>HttpContext</b>.Current.Cache オブジェクトに格納することを検討してください。
キャッシュを適切にリフレッシュする
データを 10 分ごとに更新するなら、キャッシュも 10 分ごとに更新する必要がある、というわけではありません。サービス レベル契約を満たすのに必要なデータの更新頻度を見極めてください。頻繁に変わるデータのためにキャッシュを再取得しないようにしてください。頻繁に変わるデータは、キャッシングの対象として好ましくありません。
適切なデータ形式でキャッシュする。
レンダリングされた出力をキャッシュしたい場合は、出力キャッシュまたはフラグメント キャッシュを用いるべきです。レンダリングされた出力がアプリケーションの他の場所でも使われる場合は、その格納にはキャッシュ API を使ってください。データを操作する必要があるなら、キャッシュ API を使い、そのデータを格納してください。例えば、データをコンボ ボックスに結び付けたい場合、取得したデータを、キャッシュする前に ArrayList オブジェクトに変換してください。
比較的静的なページのキャッシュには出力キャッシュを用いる
ページが、複数のユーザー要求の間で比較的静的な場合は、ページ出力キャッシュを使い、ページ全体を一定期間キャッシュすることを検討してください。キャッシュ期間は、ページのデータの性質に基づいて指定します。動的ページであっても、常に要求ごとに再構築しなければならないわけではありません。例えば、作成に大きな負担を伴う Web ベースのレポートを、一定期間キャッシュすることが可能な場合もあります。動的ページを 1、2 分間キャッシュしただけでも、データ量の多いページでは、パフォーマンスは大幅に向上し得ます。
キャッシュ アイテムを、期限切れになる前に消去する必要がある場合は、HttpResponse.RemoveOutputCacheItem メソッドを使用できます。以下のコードで示すように、このメソッドは、消去したいページへの絶対パスを受けます。
HttpResponse.RemoveOutputCacheItem("/Test/Test.aspx");
ここで注意すべきなのは、キャッシュは Web ファーム全体で共有されないため、パスはサーバーによって異なるという点です。また、ユーザー コントロールからは使用できません。
メモ: ASP.NET の次のバージョン (ASP.NET 2.0) は、データベース キャッシュ依存性をサポートする見込みです。実装されれば、データベースでデータ変更があったときに、アイテムを消去できるようになります。
適切なキャッシュ場所を選択する
@OutputCache ディレクティブにより、Location 属性を使ってページのキャッシュ場所を指定できるようになります。Location 属性で選択可能な値は、以下の通りです。
-
Any - これがデフォルト値です。出力キャッシュは、要求を発行したブラウザ クライアント、プロキシ サーバー、要求処理にかかわっているその他のサーバー、または要求を処理するサーバーに配置可能です。
-
Client - 出力キャッシュは、要求を発行したブラウザ クライアントに置かれます。
-
DownStream - 出力キャッシュは、元のサーバーを除く、あらゆるHTTP 1.1 キャッシュ可能デバイスに格納できます。これらのデバイスには、プロキシ サーバーや要求元のクライアントも含まれます。
-
None - 出力キャッシュは、その要求ページについて無効となります。
-
Server - 出力キャッシュは、要求が処理された Web サーバーに置かれます。
-
ServerAndClient - 出力キャッシュは、元のサーバー、または要求元クライアントにのみ、格納可能です。プロキシ サーバーは応答をキャッシュできません。
クライアントかプロキシ サーバーが応答をキャッシュすることが確実でない場合は、Location 属性を Any、Server、ServerAndClient のいずれかに設定するべきです。その他に設定すると、下位に利用可能なキャッシュが無ければ、出力キャッシュのメリットを得られなくなります。
メモ: Location 属性はユーザー コントロールに適用されません。
選択的キャッシュには VaryBy 属性を使う
VaryBy 属性により、同じページの異なるバージョンをキャッシュすることが可能になります。ASP.NET は、4 つの VaryBy 属性を提供しています。
-
VaryByParam - クエリ文字列値に基づき、ページの異なるバージョンが格納されます。
-
VaryByHeader - 指定ヘッダー値に基づき、ページの異なるバージョンが格納されます。
-
VaryByCustom - ブラウザの種類とメジャー バージョンに基づき、ページの異なるバージョンが格納されます。さらに、カスタム文字列を定義することにより、出力キャッシュを拡張できます。
-
VaryByControl - ユーザー コントロールのプロパティ値に基づき、ページの異なるバージョンが格納されます。これは、ユーザー コントロールのみに適用可能です。
VaryBy 属性は、キャッシュするデータを特定します。以下のコードで、VaryBy 属性の使い方を示します。
<%@ OutputCache Duration="30" VaryByParam="a" %>
上記コードの設定により、以下のページについて、同じバージョンをキャッシュすることになります。
- http://localhost/cache.aspx?a=1
- http://localhost/cache.aspx?a=1&b=1
- http://localhost/cache.aspx?a=1&b=2
VaryByParam 属性に b を加えると、1 つのバージョンでなく、異なる 3 つのバージョンが格納されることになります。キャッシュされるページのバリエーション数を知っておくのは、重要なことです。2 つの変数 (a と b) があり、a に5 種類の組み合わせ、b に10 種類の組み合わせがあるなら、キャッシュされるページのバリエーションの総数は、以下の式を使って計算できます。
(MAX a × MAX b) + (MAX a + MAX b) = 65 種類
VaryBy 属性を使う場合、バリエーションが多いほど Web サーバーでのメモリ消費量は増加するため、バリエーション数を制限するようにしてください。
Windows Server 2003 ではカーネル キャッシュを使う
Windows Server 2003 および IIS 6.0 は、カーネル キャッシュを提供しています。ASP.NET ページは、IIS 6.0 の カーネル キャッシュにより、自動的にメリットを得ることができます。カーネル キャッシュは、パフォーマンスの大幅な向上をもたらします。これは、キャッシュ 応答に対する要求が、ユーザー モードへの切り替えを経ずに提供されるためです。
詳細情報
詳細は、この章で後述する「IIS 6.0 に関する考慮事項」の「カーネル モード キャッシュ」を参照してください。
詳細情報
キャッシング全般についての詳細は、Knowledge Base の以下の記事を参照してください。
プログラム的なキャッシングについての詳細は、Caching Architecture Guide for .NET Framework Applications の “Understanding Caching Technologies” にある “Using Programmatic Caching”を参照してください。
状態管理
Web アプリケーションでの状態管理には、固有の問題がつきまといます。この問題は、Web ファームに展開された Web アプリケーションで、顕著に見られます。状態情報の格納場所と格納方法に関する選択は、アプリケーションのパフォーマンスとスケーラビリティに大きな影響を及ぼします。状態情報には、さまざまな種類があります。
-
アプリケーション状態 - アプリケーション状態は、すべてのクライアントがアプリケーション全体で使う状態情報の格納に使われます。アプリケーション状態の使用は、サーバー アフィニティを発生させるため、スケーラビリティに影響します。Web シナリオにおいて、アプリケーション状態を変えたときに、サーバーをまたいで変更を複写するメカニズムはありません。そのため、同じユーザーからのその後の要求が別のサーバーへ行くと、その変更は適用されません。アプリケーション状態のデータは、以下のサンプルで示すように、キー / 値ペアを使って格納してください。
Application["YourGlobalState"] = somevalue;
-
セッション状態 - セッション状態は、サーバーにおけるユーザーごとの状態情報を格納するために使われます。この状態情報は、ファーム内の Web サーバーをまたいで、セッション クッキー または URL.ASP.NET セッション状態スケールを使ってトラックされます。
-
ビュー ステート - ビュー ステートは、ページごとの状態情報を格納するために使われます。この状態情報は、HTTP POST 要求 / 応答のたびに流されます。
-
その他 - 状態管理にはその他、クライアント クッキー、クエリ文字列、Hidden フィールドなどの手法があります。
アプリケーション状態、セッション状態、ビュー ステートのそれぞれについてのガイドラインは、この章で後述します。以下は、状態管理全般についての、広範な問題に対応するためのガイドラインです。
- 可能な限りクライアントでシンプルな状態情報を格納する。
- シリアル化コストを考慮する。
可能な限りクライアントでシンプルな状態情報を格納する
個人データなどと違って機密性の低い、軽量なユーザー独自の状態情報を格納する場合は、クッキー、クエリ文字列、隠れたコントロールなどを使ってください。これらは、セキュリティ性の高い情報の格納には、使用しないでください。情報の読み出しや操作が、容易にできてしまうためです。
- クライアント クッキー - クライアント クッキーはサーバーで作成され、クライアント ブラウザへ送られて格納されます。これらは、ドメインに固有のもので、そのセキュリティは完全ではありません。ブラウザからのその後のすべての要求には、これらのクッキーが含まれるようになります。サーバーのコードは、これらを検査し、変更することができます。クッキーに入れることのできるデータの最大量は、4 KB です。
- クエリ文字列 - クエリ文字列は、URL に付加されるデータです。データはクリア テキストで、文字列の全長には制限があります。このデータは、ユーザーによって容易に操作可能です。そのため、認証や検証を経ることなく、クエリ引数に基づいて、機密性の高いデータの取得や表示をしてはなりません。 ただし、匿名の Web サイトでは、それほど問題になりません。
- Hidden コントロール - ページのHidden コントロールは、要求と応答で行き来する状態情報を格納します。
詳細情報
これらの状態管理技法とセキュリティとの関係についての詳細は、MSDN 上の「Web アプリケーション セキュリティ強化: 脅威とその対策」の「セキュリティ保護された ASP.NET ページとコントロールを構築する」 を参照してください。
シリアル化コストを考慮する。
状態情報をシリアル化する必要がある場合は、シリアル化コストを考慮してください。例えば、リモートの状態ストアに状態情報を格納するために、シリアル化が必要となるかもしれません。この場合、絶対に必要なものだけを格納し、複雑なオブジェクトよりもシンプルな型を優先し、シリアル化による影響を減らすようにしてください。
アプリケーション状態
アプリケーション状態は、アプリケーション全体で使われる静的情報の格納のために使われます。ASP.NET がアプリケーション状態をサポートしているのは、主にActive Server Pages (ASP) テクノロジとの互換性を確保し、ASP.NET への移植を容易にするためにです。
アプリケーション状態を使用する場合、以下のガイドラインをアプリケーションの最適化に役立ててください。
-
アプリケーション状態の格納には Applicaton オブジェクトでなく静的プロパティを使う。
-
静的な読み出し専用データの共有にはアプリケーション状態を使う。
-
STA COM オブジェクトをアプリケーション状態に格納しない。
アプリケーション状態の格納には Applicaton オブジェクトでなく静的プロパティを使う
データは、Application オブジェクトでなく、アプリケーション クラスの静的メンバに格納すべきです。Application ディレクトリのアイテムよりも静的変数の方が速くアクセスできるため、これによってパフォーマンスは向上します。以下は、単純化したコード例です。
<%
private static string[] _states[];
private static object _lock = new object();
public static string[] States
{
get {return _states;}
}
public static void PopulateStates()
{
// スレッド セーフとすること
if(_states == null)
{
lock(_lock)
{
// 状態情報を取得...
}
}
}
public void Application_OnStart(object sender, EventArgs e)
{
PopulateStates();
}
%>
静的な読み出し専用データの共有にはアプリケーション状態を使う
アプリケーション状態は、アプリケーション全体で利用され、サーバーに固有のものです。読み書き可能なデータも格納できますが、サーバー アフィニティを避けるためには、呼び出し専用データのみを格納すべきです。Cache オブジェクトの使用を検討してください。Cache オブジェクトは、読み出し専用データよりも理想的です。
STA COM オブジェクトをアプリケーション状態に格納しない
STA COM オブジェクトをアプリケーション状態に格納すると、アプリケーションのボトルネックとなります。アプリケーションは、コンポーネントへのアクセスをシングル スレッドで実行するためです。アプリケーション状態には、STA COM を格納しないようにしてください。
詳細情報
アプリケーション状態についての詳細は、Microsoft サポート技術情報の Knowledge Base 文書 312607 「INFO: ASP.NET のアプリケーション インスタンス、アプリケーション イベント、およびアプリケーション状態」を参照してください。
セッション状態
ASP.NETでセッション状態が必要な場合、3 つのセッション状態モードから選択可能です。以下で示すように、選択するモードによってパフォーマンスとスケーラビリティは変わってきます。
-
InProc - インプロセス ストアにより、セッション状態へのアクセスは最も速くなります。このモードでは、状態情報は ASP.NET プロセスのマネージ メモリ内で保持されるため、シリアル化コストやマーシャリング コストは発生しません。ASP.NET プロセスは、Windows 2000 Server の Aspnet_wp.exe ファイルと、Windows Server 2003 の W3wp.exe ファイルです。プロセスがリサイクルされると、状態データは失われます。ただし、プロセスのリサイクルは、アプリケーションに影響する場合、IIS 6 では無効にすることができます。インプロセス ストアは、複数のワーカー プロセスと共に利用できないため、アプリケーションのスケーラビリティを制限します。例えば、インプロセス ストアは、Web ファーム展開や Web ガーデン展開を妨げます。また、大型セッションや同時セッションが多くなると、メモリ不足が発生しかねません。
-
StateServer - ローカル Web サーバーや、Web ファームのすべてのWeb サーバーからアクセス可能なリモート サーバーに、Microsoft Win32? のセッション状態サービス (StateServer) をインストールできます。このアプローチにより、スケーラビリティは向上しますが、パフォーマンスはインプロセス プロバイダの場合に比べて落ちます。これは、状態ストアとの間で状態情報を行き来させる際に、シリアル化とマーシャリングが必要になるためです。
-
SQL Server - Microsoft SQL Server は、スケーラビリティに富み、簡単に利用可能なソリューションを提供します。SQL Server は、大量のセッション状態に最適なソリューションです。シリアル化コストとマーシャリング コストは、セッション状態サービスの場合と同じです。しかし、全体的なパフォーマンスは、わずかに落ちてしまいます。SQL サーバーは、フェールオーバーのためのクラスタ化を提供します。ただし、セッション状態のデフォルト設定では、クラスタ化はサポートされていません。有効にするには、コンフィギュレーションを変更する必要があります。また、セッション データは、一時的でないテーブルに格納しなければなりません。
詳細は、Microsoft サポート技術情報の Knowledge Base 文書 323262 「フェールオーバー クラスタ内の ASP.NET セッション状態 SQL Server モード を使用する方法」を参照してください。
状態ストアの選択
インプロセス セッション状態ストアは、パフォーマンスとスケーラビリティの大幅な向上をもたらします。しかし、データ量の多い Web アプリケーションのほとんどは、Web ファームで動作します。スケール アウトを可能にするためには、セッション状態サービスか、SQL Server 状態ストアのいずれかを選択しなければなりません。このとき、付随するネットワーク待ち時間とシリアル化の影響を考慮すると共に、それらを計測し、アプリケーションがパフォーマンス目標値を満たすことを確認する必要があります。以下の情報を、状態ストアの選択に役立ててください。
- 単体の Web サーバー - 単体の Web サーバーを備えているとき、セッション状態パフォーマンスを最適化したいとき、そして同時セッションの数がそれほど多くなく、限られているときは、インプロセス セッション状態ストアを使ってください。セッションの再構築が高くつくときや、ASP.NET 再起動時に持続性が必要な場合は、セッション状態サービスを使ってください。また、信頼性が最も重要な場合は、SQL Server 状態ストア を使ってください。
- Web ファーム - ローカル Web サーバーでは、インプロセス セッション状態ストアを使うことや、セッション状態サービスを動作させることは、避けるようにしてください。これらは、サーバー アフィニティを発生させます。インターネット プロトコル (IP) アフィニティを使い、同じクライアントからのその後の要求を、同じサーバーが処理するようにすることができます。しかし、インターネット サービス プロバイダ (ISP) がリバース プロキシを使っていると、このアプローチによって問題が発生する場合があります。Web ファーム シナリオでは、リモート セッション状態サービスか SQL Server を使ってください。
- StateServer vs. SQLServer - SQL Server データベースを備えていない場合は、リモート セッション状態サービスを使ってください。エンタープライズ アプリケーションやデータ量の多い Web アプリケーションでは、SQL Serverを使ってください。リモートのセッション状態サービスと Web サーバーがファイアウォールによって隔離されている場合は、ポートを開く必要があります。デフォルト ポートは、42424 ポートです。ポートは、以下のレジストリ キーによって変更できます。
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state \Parameters
セッション状態パフォーマンスを最適化するには、以下のガイドラインに従ってください。:
-
基本型を優先してシリアル化コストを減らす。
-
使わなければセッション状態を無効にする。
-
セッション状態に STA COM オブジェクトを格納しないようにする。
-
可能な場合は ReadOnly 属性を使う。
基本型を優先してシリアル化コストを減らす
StateServer または SQLServer のアウトプロセス セッション状態ストアを使うと、シリアル化オーバーヘッドが発生します。オブジェクト グラフがシンプルなほど、シリアル化は速くなるはずです。シリアル化コストを最小限に抑えるために、Int、Byte、Decimal、String、DateTime、TimeSpan、Guid、IntPtr、UintPrt などの基本型を使ってください。ASP.NETは、基本型のシリアル化には、内部の最適化したシリアル化メソッドを使います。複雑な型は、比較的遅い BinaryFormatter オブジェクトによってシリアル化されます。複雑な型については、Serializable 属性を使うか、ISerializable インターフェイスを実装することができます。このインターフェイスを使うと、より正確な制御が可能になり、シリアル化速度が向上する場合もあります。
シリアル化の対象を、最小限に抑えてください。不要ならシリアル化を無効にし、シリアル化可能クラスのうち、除外したいフィールドには NonSerialized 属性を付けてください。ISerializable インターフェイスを使い、シリアル化プロセスを制御してください。
メモ: ISerializable インターフェイスは、最後の手段としてのみ実装するべきです。.NET Framework の今後のバージョンが提供する新しいフォーマッタや、シリアル化を提供するフレームワークの改善は、このアプローチを採用してしまうと、利用できなくなります。NonSerialized 属性を使うようにしてください。
詳細情報
詳細は、このガイドの「How To 情報」にある「シリアル化パフォーマンスを向上させる方法」を参照してください。
使わなければセッション状態を無効にする
セッション状態を使わないのなら、無効にし、ASP.NET が実行する追加的なセッション処理をなくしてください。シンプルな状態情報をクライアントで格納し、それを要求ごとにサーバーへ渡す場合、セッション状態は必要となりません。また、以下で示すように、サーバーの全アプリケーション、特定のアプリケーション、または個々のページについて、セッション状態を無効にすることができます。
セッション状態に STA COM オブジェクトを格納しないようにする
セッション状態に STA COM オブジェクトを格納すると、スレッド アフィニティが発生します。スレッド アフィニティは、パフォーマンスとスケーラビリティに大きな悪影響をもたらします。セッション状態で STA COM オブジェクトを使う場合は、必ず @ Page ディレクティブの AspCompat 属性をセットしてください。
詳細情報
詳細は、この章で後述する「COM 相互運用」を参照してください。
可能な場合は ReadOnly 属性を使う
内部でセッション状態を使うページ要求は、セッション データの管理のために ReaderWriterLock オブジェクトを使用します。この場合、ロックがかかっていなければ、同時に複数読み出しをすることが可能になります。書き込み側がセッション状態更新のためにロックを取得すると、読み出し要求はすべてブロックされます。通常、各要求についてデータベースへ 2 つの呼び出しが送られます。1 つ目の呼び出しは、データベースへの接続を確保し、セッションをロック状態とし、ページを実行します。2 つ目の呼び出しは、あらゆる変更について書き込み、セッションへのロックを解除します。セッション データを読み出すだけのページについては、以下のサンプルで示すように、EnableSessionState を ReadOnly にセットすることを検討してください。
<%@ Page EnableSessionState="ReadOnly" . . .%>
EnableSessionState を ReadOnly にセットすると、フレームを使っている場合に、特にメリットを得られます。フレーム使用時は、ReaderWriterLock が使われるため、デフォルト設定ではページの実行はシリアル化されます。EnableSessionState を ReadOnly にセットすると、ブロックを避け、データベースへの呼び出しを減らすことができます。前述したように、構成ファイルでセッションを無効にし、ページ単位で ReadOnly 属性を設定するのも、1 つの方法です。
詳細情報
セッション状態についての詳細は、MSDN 上の “Underpinnings of the Session State Implementation in ASP.NET”を参照してください。
セッション状態についての情報は他にも、Microsoft サポート技術情報の Knowledge Base の以下の文書で参照できます。
ビュー ステート
ビュー ステートは主に、サーバー コントロールが、データを自身へポストバックするページのみで、状態情報を保持するために使います。情報はクライアントへ渡され、_VIEWSTATE と呼ばれる特定の隠れた変数で読み出されます。ASP.NET は、あらゆるシリアル化可能な型のビュー ステートへの格納を容易にしています。しかし、この機能は簡単に誤用され、パフォーマンス低下の原因となります。ビュー ステートは、それを必要としないページにとっては、不要なオーバーヘッドとなります。ビュー ステートが大きくなるにつれて、パフォーマンスは以下のようにして影響を受けます。
- ビュー ステートのシリアル化とシリアル化解除が、CPU サイクルの増加につながる。
- 大きくなったページのダウンロードにかかる時間が長くなる。
- 非常に大きいビュー ステートは、ガベージ コレクションの効率性にも影響を及ぼし得る。
大量のビュー ステートの送信は、アプリケーションのパフォーマンスに大きな影響を与えかねません。Web クライアントが遅いダイアルアップ接続を使っている場合、パフォーマンスの低下度はさらに著しくなります。ビュー ステートを扱う場合は、さまざまな帯域条件でテストすることを検討してください。また、以下の推奨事項に従い、アプリケーションによるビュー ステートの使用を最適化してください。
- 必要なければビュー ステートを無効にする。
- ビュー ステートに格納するオブジェクト数を最小化する。
- ビュー ステートのサイズを調べる。
必要なければビュー ステートを無効にする
ASP.NET では、ビュー ステートはデフォルトで有効となっています。必要ない場合は、これを無効にしてください。例えば、ページが出力のみの場合や、データを要求ごとに明示的に再ロードする場合は、ビュー ステートは必要ありません。その他、以下の状況では、ビュー ステートは必要ありません。
-
ページがポストバックしない - ページが情報を自身にポストバックしない場合、ページが出力のみに使われる場合、およびページが応答に依存しない場合は、ビュー ステートは必要ありません。
-
サーバー コントロール イベントを処理しない - サーバー コントロールがイベントを処理しない場合、およびサーバー コントロールが動的、もしくはデータに依存するプロパティ値を持っていないか、プロパティ値が要求ごとにセットされる場合は、ビュー ステートは必要ありません。
-
ページがリフレッシュされるたびにコントロールを再取得する - 古いデータを無視する場合、およびページがリフレッシュされるたびにサーバー コントロールを再取得する場合は、ビュー ステートは必要ありません。
各レベルでビュー ステートを無効にするには、さまざまな方法があります。
ビュー ステートに格納するオブジェクト数を最小化する
ビュー ステートに入れるオブジェクト数が増加するにつれ、ビュー ステート ディクショナリのサイズは大きくなり、オブジェクト インスタンスのシリアル化とシリアル化解除に必要な処理時間も長くなります。ビュー ステートにオブジェクトを格納する場合は、以下のガイドラインに従ってください。
- ビュー ステートは、文字列、整数、ブール値などのシリアル化可能な基本型、およびこれらの基本型を含む、配列、ArrayLists、Hashtables などのオブジェクトに最適化されています。上記以外の型をビュー ステートに格納する場合、ASP.NETは、内部で関連する型変換を実行しようとします。型変換ができない場合、これよりも大きな負担を伴う、バイナリ シリアライザが使われることになります。
- ビュー ステートのサイズは、オブジェクトのサイズに直接的に比例します。大きなオブジェクトは、格納しないようにしてください。
ビュー ステートのサイズを調べる
ページのトレースを可能にすることにより、各コントロールのビュー ステートのサイズを監視できるようになります。各コントロールのビュー ステートのサイズは、トレース出力のコントロール ツリー セクションの最左列に表示されます。この情報を参考にし、ビュー ステートの量を減らせるコントロールや、ビュー ステートを無効にできるコントロールがあるかを調べてください。
詳細情報
関連情報は、MSDN 上の “Taking a Bite out of ASP.NET ViewState”を参照してください。
HTTP モジュール
HTTP モジュールは、ASP.NET パイプライン通過中の HTTP 要求 / 応答メッセージに対する事前処理および事後処理を可能にするものです。HTTP モジュールは通常、認証、承認、ログ、マシン レベルのエラー処理のために使われます。HTTP モジュールは、あらゆる要求に対して動作します。そのため、HTTP モジュールによるすべての処理は、登録場所に応じてアプリケーションかコンピュータのいずれかについて、全体にわたる影響を及ぼします。
HTTP モジュールを開発する場合は、以下を考慮してください。
- パイプライン コードでは所要時間の長い呼び出しやブロック呼び出しを避ける。
- 非同期イベントを検討する。
パイプライン コードでは所要時間の長い呼び出しやブロック呼び出しを避ける
以下の理由により、HTTP モジュールに所要時間の長いコードを置かないようにしてください。
- ASP.NET ページは、同期処理される。
- HTTP モジュールは通常、同期イベントを使う。
所要時間の長い呼び出しやブロック呼び出しは、ASP.NET で動作可能な並行要求数を減らします。
非同期イベントを検討する
各同期イベントについて、非同期バージョンが存在します。非同期イベントは非同期作業の間、要求を論理的にブロックしてしまいますが、ASP.NET スレッドはブロックしません。
詳細情報
HTTP モジュールについての詳細は、MSDN Magazine article, “ASP.NET Pipeline: Use Threads and Build Asynchronous Handlers in Your Server-Side Web Code”を参照してください。
その他、Microsoft サポート技術情報の Knowledge Base の文書 307985 「INFO: ASP.NET の HTTP モジュールと HTTP ハンドラの概要」 を参照してください。
文字列管理
出力時に文字列の連結が必要となることは、よくあります。文字列連結は、メモリの一時アロケーションとその後のコレクションを必要とする、大きな負担を伴う処理です。そのため、文字列連結の実行は、最小限に抑えるべきです。データのレンダリングのためにページで文字列を連結するための一般的な方法として、以下の 3 つが挙げられます。
-
+= 演算子を使う - アペンド数が既知の場合、+= 演算子を使います。
-
StringBuilder - アペンド数が未知の場合は、StringBuilder オブジェクトを使います。StringBuffer は、再利用可能なバッファとして扱ってください。
-
Response.Write <% %> - Response.Write メソッドを使います。出力を最も速くブラウザへ返すことのできる方法の 1 つです。
上記のそれぞれについてパフォーマンスを計測してからアプローチを選ぶのが、最も効果的です。アプリケーションが一時バッファに大きく依存している場合は、文字配列またはバイト配列のための再利用可能なバッファ プールを実装することを検討してください。
文字列の管理には、以下のガイドラインを役立ててください。
-
出力の書式設定には Response.Write を使う。
-
StringBuilder を一時バッファとして使う。
-
カスタム コントロール作成時には HttpTextWriter を使う。
出力の書式設定には Response.Write を使う
可能な場合、ページ レイアウトの書式設定のための文字列連結に、ループを使わないでください。代わりに、Response.Write を使うことを検討してください。このアプローチでは、ASP.NET 応答バッファに出力が書き込まれます。データ セットまたは XML ドキュメントについてループを行っている場合、Response.Write を使うのが効率的なアプローチです。これは、クライアントに書き込む前に += 演算子を使って文字列を連結するよりも、効率的です。Response.Write は、再利用可能なバッファに、内部で文字列を加えます。そのため、メモリのアロケーションやクリーンアップによるパフォーマンス オーバーヘッドを、避けることができます。
StringBuilder を一時バッファとして使う
多くの場合で、Response.Write は適用できません。例えば、ログ ファイルへの書き込みや XML ドキュメントの作成に文字列の作成が必要となる場合もあるでしょう。このような状況では、StringBuilder オブジェクトを、データ保持のための一時バッファとして使ってください。StringBuilder オブジェクトについて、初期キャパシティ設定をいろいろ変えてパフォーマンスを計測してみてください。
カスタム コントロール作成時には HttpTextWriter を使う
カスタム コントロール作成時は、Render、RenderChildren、RenderControl の各メソッドが、HtmlTextWriter オブジェクトへのアクセスを提供します。HtmlTextWriter は、Response.Write と同じ再利用可能なバッファに書き込みます。Response.Write の場合と同様に、HtmlTextWriter により、メモリのアロケーションやクリーンアップによるパフォーマンス オーバーヘッドを避けることができます。
詳細情報
文字列についての詳細は、第 5 章「マネージ コード パフォーマンスの向上」の「文字列処理」を参照してください。
アプリケーションが非効率的な文字列連結のために一時メモリ割り当てを過剰に行っているかを調べるためには、第 15 章「.NET アプリケーション パフォーマンスの計測」の「CLR とマネージ コード」にある「メモリ」で述べる、パフォーマンス カウンタを使ってください。問題元の特定には、CLR プロファイラを利用してください。詳細は、このガイドの「How To 情報」にある「CLR プロファイラの使用方法」を参照してください。
例外管理
例外は、大きな負担を伴います。例外の原因を知っておき、例外を避け、発生しても効率的に処理するコードを作成することにより、アプリケーションのパフォーマンスとスケーラビリティを大幅に向上させることができます。例外処理の設計と実装の際には、以下のガイドラインに従い、パフォーマンスを最適化してください。
- Global.asax エラー ハンドラを実装する。
- アプリケーションの例外を監視する。
- 破棄可能なリソースで try / finally を使う。
- 例外を避けるコードを作成する。
- タイムアウト時間を短めに設定する。
Global.asax エラー ハンドラを実装する
例外管理の第一歩は、Global.asax ファイル、またはコードビハインド ファイルでの グローバル エラー ハンドラの実装です。グローバル エラー ハンドラの実装により、アプリケーション内の処理されない例外は、すべてトラップされます。ハンドラ内では、最低限、データベース、Windows イベント ログ、ログ ファイルなどのデータ ストアに、以下の情報をログするべきです。
- エラーが発生したページ
- 呼び出しスタック情報
- 例外名とメッセージ
エラー ロジックの処理には、Global.asax ファイル、または以下のサンプルで示すように、コードビハインドで Application_Error イベントを使ってください。
public void Application_Error(object s, EventArgs ev)
{
StringBuilder message = new StringBuilder();
if (Server != null) {
Exception e;
for (e = Server.GetLastError(); e != null; e = e.InnerException)
{
message.AppendFormat("{0}: {1}{2}",
e.GetType().FullName,
e.Message,
e.StackTrace);
}
// 例外情報と内部例外情報をログ
}
}
詳細情報
詳細は、MSDN 上の “Rich Custom Error Handling with ASP.NET” を参照してください。
また、Microsoft サポート技術情報の Knowledge Base の文書 306355 「HOW TO: Visual C# .NET を使用して ASP.NET のカスタム エラー報告ページを作成する方法」 (http://support.microsoft.com/default.aspx?scid=kb;ja;306355) を参照してください。
アプリケーションの例外を監視する
アプリケーションで発生する例外の数を減らすためには、例外を効率的に監視する必要があります。そのためには、以下を実行すべきです。
- 例外処理コードを実装しているなら、例外ログを定期的に見直す。
- .NET CLR Exceptions パフォーマンス モニタ オブジェクトの # of Exceps Thrown / sec カウンタを監視する。この値は、1 秒当たりの平均要求数の 5 パーセント未満であるべきです。
破棄可能なリソースで try / finally を使う
例外発生時にリソースがクリーンアップされるようにするには、try / finally ブロックを使ってください。リソースは、finally クローズで閉じます。try / finally ブロックにより、例外発生時でもリソースが破棄されるようにしてください。以下のは、そのコード例です。
try
{
conn.Open();
...
}
finally
{
if(null!=conn)
conn.close;
}
例外を避けるコードを作成する
例外を避けるために用いることができる一般的なアプローチを、以下に掲げます。
-
null 値をチェックする - オブジェクトを null に設定できる場合は、例外をスローする前に null となっていないかを確認してください。ビュー ステート、セッション状態、アプリケーション状態、またはキャッシュ オブジェクトやクエリ文字列からアイテムを取得し、フィールド変数を作ると、オブジェクトが null となることがよくあります。例えば、セッション状態情報へのアクセスに、以下のコードを使わないでください。
try {
loginid = Session["loginid"].ToString();
}
catch(Exception ex) {
Response.Redirect("login.aspx", false);
}
セッション状態情報へのアクセスには、代わりに以下のコードを使ってください。
if(Session["loginid"]!=null)
loginid = Session["loginid"].ToString();
else
Response.Redirect("login.aspx", false);
-
ロジックの制御に例外を使わない - 例外は、あくまでも例外です。開かないデータベース コネクションは例外です。一方、パスワードを打ち間違えたユーザーは、処理が必要な状況にあるだけです。例えば、ユーザーのログインに使う、以下の関数プロトタイプを見てください。
public void Login(string UserName, string Password) {}
ログインの呼び出しには、以下のコードが使われます。
try
{
Login(userName,password);
}
catch (InvalidUserNameException ex)
{...}
catch (InvalidPasswordException ex)
{...}
とり得る値の列挙型を作り、その列挙型を返すために Login メソッドを以下のように変えた方が、効果的です。
public enum LoginResult
{
Success,InvalidUserName, InvalidPassword, AccountLockedOut
}
public LoginResult Login(string UserName, string Password) {}
Login の呼び出しには、以下のコードが使われます。
LoginResult result = Login(userName,password)
switch(result)
{
case Success:
. . .
case InvalidUserName:
. . .
case InvalidPassword:
}
-
Response.End の内部呼び出しをしないようにする - Server.Transfer、Response.Redirect、Response.End の各メソッドは、例外を発生させます。各メソッドは、内部で Response.End を呼び出します。Response.End が呼び出されると、ThreadAbortException 例外が発生します。Response.Redirect を使用する場合は、オーバーロードしたメソッドを使い、第 2 引数として false を渡すことにより、Response.End の内部呼び出しが発生しないようにしてください。
詳細は、Microsoft サポート技術情報の Knowledge Base の文書 312629 「PRB: Response.End、Response.Redirect、または Server.Transfer メソッドを使用すると ThreadAbortException が発生する」 を参照してください。
-
処理できない例外をキャッチしない - コードが例外を処理できない場合は、try / finally ブロックを用い、例外が発生するかどうかにかかわらず、リソースを閉じるようにしてください。修復を試みることができない場合、例外はキャッチしないでください。例外は、その例外状況を処理できる、適切なハンドラへ伝達するようにしてください。
タイムアウト時間を短めに設定する
ページ タイムアウト時間をあまりにも長めに設定すると、アプリケーションの一部の動作速度が遅い場合に、問題が生じ得ます。具体的には、以下の問題が発生する場合があります。
- プラウザが応答しなくなる。
- 受信要求がキューに入り始める。
- 要求キューが制限に達すると、IIS が要求を拒否する。
- ASP.NET が応答しなくなる。
デフォルトのページ タイムアウト時間は、90 秒です。各シナリオに合わせて、この値を変えることができます。
ASP.NET フロントエンド アプリケーションが、リモート Web サービスを呼び出す場合について、考えてみてください。リモート Web サービスは、続いてメインフレーム データベースを呼び出します。このメインフレームへの呼び出しが、何らかの理由でブロックされ始めると、フロントエンド ASP.NET ページは、バックエンド呼び出しがタイムアウトするか、ページ タイムアウト制限を越えるまで、待機し続けます。この結果、現在の要求はタイムアウトし、ASP.NET は受信要求をキューに入れ始めます。これらの要求もまた、タイムアウトすることになるかもしれません。これらの要求のタイムアウト時間を 90 秒より短くした方が、効率は上がります。また、それにより、ユーザーの使い勝手も向上します。
ほとんどのインターネット / イントラネット シナリオでは、タイムアウト制限は 30 秒が妥当です。ホームページなどのトラフィック量の多いページでは、タイムアウト制限をさらに短くすべき場合もあるでしょう。レポート ページなど、アプリケーションが生成するまでに長い時間を要するページの場合、アプリケーションの場合は、タイムアウト制限を長めに設定してください。
詳細情報
例外処理についての詳細は、MSDN ライブラリの以下の記事を参照してください。
各種タイムアウト引数とその設定方法についての詳細は、第 17 章「.NET アプリケーションパフォーマンスのチューニング」の「タイムアウト時間を短めに設定する」を参照してください。
COM 相互運用
ASP.NET からCOM オブジェクトを呼び出すと、パフォーマンスが影響を受ける場合があります。これは、スレッド処理の問題、データ型のマーシャリング、マネージ コードとアンマネージ コードの境界をまたいだ切り替え、などに対処しなければならなくなるためです。ASP.NETは、マルチスレッド アパートメント (MTA) スレッドで動作するため、STA COM コンポーネントとの共同作業では、特に問題が生じ得ます。
以下のガイドラインを、COM 相互運用パフォーマンスの向上に役立ててください。
-
STA COM オブジェクトの呼び出しには ASPCOMPAT を使う。
-
セッション状態やアプリケーション状態に COM ブジェクトを格納しない。
-
セッション状態に STA オブジェクトを格納しない。
-
ページ コンストラクタで STA オブジェクトを作成しない。
-
標準的な ASP Server.CreateObject を事前バインディングで補う。
STA COM オブジェクトの呼び出しには ASPCOMPAT を使う
Visual Basic 6.0 コンポーネントなどの STA オブジェクトを ASP.NET ページから呼び出す場合は、ページ レベルの ASPCOMPAT 属性を使ってください。ページのイベントが、デフォルトの MTA スレッドでなく、STA スレッド プールのスレッドを使って動作すべきことを表示するには、ASPCOMPAT 属性を以下のように使います。
<%@ Page ASPCOMPAT="true" language="c#" %>
STA オブジェクト呼び出しは、STA スレッドを必要とします。ASPCOMPAT 属性を使わない場合、すべての STA オブジェクト呼び出しは、ホスト STA スレッドでシリアル化され、重大なボトルネックとなり得ます。
セッション状態やアプリケーション状態に COM ブジェクトを格納しない
セッション状態やアプリケーション状態などの状態コンテナに、COM オブジェクトを格納しないようにしてください。COM オブジェクトはシリアル化できません。シングル サーバー展開で呼び出すことは可能ですが、アフィニティとシリアル化の問題により、アプリケーションは Web ファームへ移されると、作業できなくなります。
セッション状態に STA オブジェクトを格納しない
セッション状態に STA オブジェクトを格納することは、技術的には可能ですが、スレッド アフィニティの問題を引き起こすため、避けてください。格納すると、STA オブジェクトへの要求は、オブジェクトを作ったのと同じスレッドで動作しなければならず、ユーザー数が増えると、たちまちこれがボトルネックとなります。
詳細情報
詳細は、Microsoft サポート技術情報の Knowledge Base の文書 817005 「FIX: ASPCompat モードでセッション状態をスレッドにバインドした場合の深刻なパフォーマンスの問題」 を参照してください。
ページ コンストラクタで STA オブジェクトを作成しない
ページ コンストラクタで STA オブジェクトを作成しないでください。ホスト STAへのスレッド切り替えと、すべての呼び出しのシリアル化が発生することになるためです。ASPCOMPAT 属性により、onload や button_click などのページ イベントに STA スレッド プールのスレッドを使うようにすることは可能ですが、コンストラクタなどのページの他の部分は、MTA スレッドによって動作します。
標準的な ASP Server.CreateObject を事前バインディングで補う
遅延バインディングは、COM クラスの場合でも、メソッドを名前で実行する場合でも、対象コードを見つけるための特別な指示が必要とします。Server.CreateObject、Activator.CreateInstance、MethodInfo.Invoke などのメソッドにより、コードは遅延バインディングを実行できます。ASP コードを移植する場合、new キーワードを使って事前バインディングを行ってください。
以下のサンプルでは、事前バインディングを行っています。new キーワードを使い、標準的な ActiveX? Data Objects (ADO) 接続を作成しています。
<%@ Import namespace="ADODB" %>
<%@ Assembly name="ADODB" %>
... Connection con = new Connection();
以下のサンプルでは、遅延バインディングを用いています。<object> タグと class 属性を使い、ADO 接続オブジェクトを作成しています。ADODB.Connection は、名前空間とクラス名を示しています。次に出てくる ADODB は、アセンブリ名です。
<object id="con" runat="server" class="ADODB.Connection, ADODB" />
以下のサンプルでも、遅延バインディングを用いています。ここでは、GetType を使い、型を取得しています。これは ASP.NET の提供する、オーバーロードした Server.CreateObject メソッドに渡されます。
con = Server.CreateObject(Type.GetType("ADODB.Connection, ADODB"));
これらのコード サンプルを動作させるには、Visual Studio? .NET の Microsoft ActiveX Data Objects X.X Library に参照を加えてください。X.X には、使用したいバージョンのナンバーが入ります。このアプローチにより、存在すれば相互運用アセンブリが持ち出され、存在しなければ自動的に生成されます。Visual Studio. NET を使っていなければ、TlbImp.exe ファイルを使って相互運用アセンブリを生成させてください。生成した相互運用アセンブリは、アプリケーションの Bin ディレクトリにコピーしてください。
詳細情報
COM 相互運用のパフォーマンスと問題についての詳細は、第 7 章「相互運用パフォーマンスの向上」を参照してください。その他、Microsoft サポート技術情報の Knowledge Base の以下の文書を参照してください。
データ アクセス
ASP.NET アプリケーションのほとんどすべては、何らかの形でデータ アクセスを使っています。アプリケーション要求の大半は、データベースのデータを対象とします。このため、データ アクセスは通常、パフォーマンスの向上を図る上で最も注意すべきポイントです。
以下のガイドラインに従い、データ アクセスを向上させてください。
-
大きな結果セットにはページングを用いる。
-
速く効率的な遅延バインディングのために DataReader 使う。
-
ユーザーがデータを過剰に要求しないようにする。
-
データのキャッシングを検討する。
大きな結果セットにはページングを用いる
大きなクエリ結果セットをページングすることにより、アプリケーションのパフォーマンスは大幅に向上し得ます。大きな結果セットがある場合、ページングを実装し、以下を実現してください。
- データペースでのバックエンドの作業を減らす。
- クライアントへ送られるデータのサイズを小さくする。
- クライアントの作業を制限する。
さまざまなページング ソリューションが利用可能です。各ソリューションは、特定のシナリオに固有の問題を解決します。以下では、これらのソリューションについて簡単に説明します。実装についての詳細は、このガイドの「How To 情報」にある「.NET アプリケーションにおけるレコードのページング方法」を参照してください。
比較的素早く簡単に利用できるソリューションは、DataGrid オブジェクトの提供する、自動ページングです。ただし、このソリューションは、インクリメントする一意の列数を備えるテーブルでのみ、利用できます。大きなテーブルには、適していません。ページングをカスタマイズするには、AllowPaging プロパティと AllowCustomPaging プロパティを true にセットし、PageSize プロパティと VirtualItemCount プロパティをセットします。これにより、StartIndex (最終ブラウズ行) プロパティと NextIndex (StartIndex + PageSize) プロパティが計算されます。StartIndex 値と NextIndex 値は、ID 列が要求ページを取得し、表示する範囲として使用されます。このソリューションでは、データをキャッシュできません。ネットワークの関連レコードのみを取得します。
インクリメントする一意の列数を備えていないテーブルについては、さまざまなソリューションが利用可能です。クラスタ化したインデックスを備え、サーバー側で特別なコーディングを必要としないテーブルについては、subquery を使い、スタートからのスキップ行数をトラックしてください。その結果のレコードについて、TOP キーワードを <pagesize> 要素と共に使い、行の次のページを取得してください。ネットワークから、関連ページ レコードのみが取得されます。その他のソリューションでは、Table データ型かグローバル一時テーブルを追加的な IDENTITY 列と共に使い、クエリ結果を格納します。この IDENTITY 列は、取得して表示する行の範囲を制限するために使われます。この場合、サーバー側でのコーディングが必要となります。
詳細情報
ページングとその実装方法についての詳細は、このガイドの「How To 情報」にある「.NET アプリケーションにおけるレコードのページング方法」を参照してください。また、Microsoft サポート技術情報の Knowledge Base の文書 318131 「HOW TO: パフォーマンスを改善するために、クエリ結果を介してページ」を参照してください。
高速かつ効率的なデータ結合のために DataReader 使う
データをキャッシュする必要がない場合、読み出し専用データ表示している場合、およびデータをできる限り速くコントロールに読み込む必要がある場合は、DataReader オブジェクトを使ってください。DataReader は、読み出し専用データを、前方のみを参照して取得する場合に最適です。データを DataSet オブジェクトにロードしてから、DataSet をコントロールに結合すると、データは 2 度移動することになります。また、このメソッドの場合、DataSet のコンストラクト時に、DataReader に比べて大きなコストが発生します。
さらに、DataReader を使うと、型指定の特別メソッドによるデータの取得が可能になり、パフォーマンスは向上します。
ユーザーがデータを過剰に要求しないようにする
ユーザーに、消費可能な量を超えるデータの要求および取得を認めると、アプリケーション リソースに不要な負担がかかることになります。この結果、CPU 利用度とメモリ消費量は増加し、応答時間は長くなります。この状況は、接続速度の遅いクライアントに、特に当てはまります。使い勝手の面で考えても、数千行のデータを 1 ユニットで見ることを望むユーザーは、ほとんどいません。
以下のうちのいずれかにより、ユーザーが取得可能なデータ量を制限してください。
- ページングを実装する。詳細は、このガイドの「How To 情報」にある「.NET アプリケーションにおけるレコードのページング方法」を参照してください。
- マスター / 詳細フォームをデザインする。各データについての情報を全てユーザーに与える代わりに、ユーザーが自分にとって関心のあるデータだと認識するのに十分な量の情報のみを、表示してください。そのデータを選択し、詳細を取得することを、ユーザーに許可してください。
- ユーザーがデータをフィルタできるようにする。
データのキャッシングを検討する
かなり静的で、取得に大きな負担を伴う、アプリケーション全体で使われるデータについては、ASP.NET キャッシュに格納することを検討してください。
詳細情報
データ アクセスについての詳細は、第 12 章「ADO.NET パフォーマンスの向上」を参照してください。
セキュリティに関する考慮事項
多くの場合で、セキュリティとパフォーマンスは、設計トレードオフを最も考慮すべき組み合わせです。これは、セキュリティ メカニズムを追加すると、パフォーマンスに悪影響が及ぶことが多いためです。しかし、不要、無効、または害のあるトラフィックをフィルタにかけることや、Web サーバーに到達することを認められる要求の数を抑えることにより、サーバー負荷を軽減できます。不要なトラフィックを早めにブロックするほど、多くの処理オーバーヘッドを回避できます。以下の推奨事項に従ってください。
-
不要な Web サーバー トラフィックを抑える。
-
匿名アクセスでは認証を無効にする。
-
ユーザー入力をクライアントで検証する。
-
要求ごとの偽装を避ける。
-
機密性の高いデータのキャッシングを避ける。
-
保護コンテンツと非保護コンテンツを分ける。
-
SSL は要求するページにのみ使う。
-
ナビゲーションには絶対パスを使う。
-
SSL 処理のオフロードのために SSL ハードウェアの使用を検討する。
-
SSL セッション期限切れが起きないように SSL タイムアウトをチューニングする。
不要な Web サーバー トラフィックを抑える
Web サーバーへのトラフィックを抑え、不要な処理を避けてください。例えば、ファイアウォールで無効な要求をブロックし、Web サーバーにかかる負荷を制限してください。これに加え、以下を行ってください。
- サポートしていない拡張子を、IIS の 404.dll ファイルにマップする。
- UrlScan フィルタを使い、許可する命令と URL 要求を制御する。制御の対象となり得る命令には、Get、Post、SOAPなどがあります。
- IIS ログを見直す。許可していないトラフィックでログが一杯になっている場合は、ファイアウォールでのそのトラフィックをブロックするか、リバース プロキシを使ってトラフィックをフィルタにかけることを検討してください。
匿名アクセスでは認証を無効にする
認証アクセスが必要なページと、匿名アクセスをサポートするページを分けてください。不要な認証オーバーヘッドを避けるために、匿名ページを含むディレクトリの Web.config ファイルで、認証モードを None にセットしてください。以下のコードで、Web.config ファイルでの認証モードの設定方法を示します。
<authentication mode="None" />
ユーザー入力をクライアントで検証する
サーバーへのトラフィックの不要な増加を回避するために、クライアント側検証を用いることを検討してください。ただし、クライアント側検証は容易にパスできるため、これのみに頼らないでください。セキュリティ上の理由により、各クライアント側検証に対し、同等のサーバー側検証を実装すべきです。
要求ごとの偽装を避ける
要求ごとの偽装により、オリジナル呼び出し元の ID を使ってデータベースにアクセスすると、アプリケーションのスケーラビリティは、大幅に制限されます。要求ごとの偽装により、データベース コネクション プーリングは、効果的に使用できなくなってしまいます。信頼済みサブシステム モデルの方が望ましく、拡張性にも富んでいます。このモデルでは、固定サービス アカウントを使ってデータベースにアクセスします。また、オリジナル呼び出し元の ID を求められた場合は、やはり固定サービス アカウントにより、アプリケーション レベルでの ID を渡します。例えば、ストアド プロシージャ引数によってオリジナル呼び出し元の ID を渡す場合もあるでしょう。
詳細情報
信頼済みサブシステム モデルについての詳細は、MSDN 上の セキュリティ保護された ASP.NET アプリケーションの構築 : 認証、認定、および通信のセキュリティ保護」の 第 3 章「認証と認定」 を参照してください。
機密性の高いデータのキャッシングを避ける
機密性の高いデータは、キャッシュする代わりに、必要時に取得してください。アプリケーション パフォーマンスの計測時に、要求ごとのデータ取得に非常に大きなコストがかかっていることが判明した場合は、データの暗号化、キャッシュ、取得、暗号化解除に要するコストを計測してください。要求ごとのデータ取得に伴うコストが、暗号化と暗号化解除のコストを上回っている場合は、暗号データをキャッシュすることを検討してください。
保護コンテンツと非保護コンテンツを分ける
Web サイトのフォルダ構造を設計する際には、どこからでもアクセス可能なエリアと、認証アクセスと セキュア ソケット レイヤ (SSL) が必要となる制限エリアを、明確に分けてください。アプリケーションの仮想ルート フォルダの下で別のサブフォルダを用い、フォーム ログオン ページ、チェックアウト ページなどの、HTTPS によって保護すべき機密性の高い情報をユーザーから受ける、制限ページを確保します。これにより、サイト全体にパフォーマンス オーバーヘッドを及ぼすことなく、特定のページについてのみ HTTPS を使用できるようになります。
SSL は要求するページにのみ使う
SSL の使用には、大きな負担が伴います。SSL は、要求するページにのみ、使用するようにしてください。これらのページには、クレジットカード番号やパスワードを受け付けるページなど、機密性の高いデータを取得または保管するページなどがあります。以下の場合にのみ、SSL を使ってください。
- ページ データを暗号化したい。
- データが、希望のサーバーに確実に送られるようにしたい。
SSL を使わなければならないページについては、以下のガイドラインに従ってください。
- ページ サイズをできる限り小さくする。
- ファイル サイズの大きいグラフィックの使用を避ける。グラフィックを使う場合は、ファイルサイズが小さく、解像度が低いものを選んでください。または、非保護サイトのグラフィックを使ってください。ただし、この場合、Web ブラウザはダイアログ ボックスを表示し、非保護サイトのコンテンツを表示することについて、ユーザーに確認をとります。
ナビゲーションには絶対パスを使う
リダイレクトを使った HTTP-HTTPS 間のナビゲーションでは、対象ページでなく、現在ページのプロトコルが使われます。HTTPS を使ったサイトから非保護サイトへ、相対パス (..\publicpage.aspx) によってダイレクトすると、これらのパブリックなページは、HTTPS プロトコルを使って提供されることになります。これにより、不要なオーバーヘッドが発生します。これを避けるには、リダイレクトの際に、相対パスでなく、http://yourserver/publicpage.aspx のような絶対パスを使ってください。これは、HTTP を使うページから、HTTPS を使うページへ移る場合についても同じです。以下のコードでは、HTTP を使うページから、HTTPS を使うページへのリダイレクトの作成方法を示しています。
string serverName = HttpUtility.UrlEncode(Request.ServerVariables["SERVER_NAME"]);
string vdirName = Request.ApplicationPath;
Response.Redirect("https://" + serverName + vdirName + "/Restricted/Login.aspx",
false);
SSL 処理のオフロードのために SSL ハードウェアの使用を検討する
SSL 処理について、ハードウェア ソリューションを検討してください。ハードウェア アクセラレータを使ってロード バランサで SSL セッションを終了させると、一般的にパフォーマンスは向上します。このアプローチは、ユーザーの多いサイトでは、特に有効です。
SSL セッション期限切れが起きないように SSL タイムアウトをチューニングする
SSL ハードウェアを使っていない場合、ServerCacheTimer プロパティをチューニングし、ブラウザ クライアントとの SSL ハンドシェイク を再び交わさなくて済むようにしてください。SSL 使用時にリソース使用度が最も高くなるのは、初期ハンドシェイク中です。この時、公開鍵と秘密鍵を使った非対称の暗号化が実施されます。セキュリティ保護されたセッション鍵が生成され、交付されると、アプリケーション データの暗号化には、より高速な対称暗号化が用いられます。
SSL 接続を監視し、タイムアウト時間を長くすべきと判断すれば、ServerCacheTime レジストリのエントリ値を増加させてください。
詳細情報
ServerCacheTime 値の変更方法についての詳細は、Microsoft サポート技術情報の Knowledge Base の文書 247658 「IIS: SSL サーバーとクライアントのキャッシュ要素の設定方法」を参照してください。
詳細情報
セキュリティ関連のパフォーマンスに関する考慮事項についての詳細は、以下を参照してください。
- MSDN ライブラリの「.NET Framework 開発者ガイド」の「暗号サービス」
- MSDN 上の “Performance Comparison: Security Design Choices”
IIS 6.0 に関する考慮事項
Microsoft Windows Server 2003 の IIS 6.0 アーキテクチャは、Windows 2000 Server の IIS 5.0 アーキテクチャとは異なります。IIS 6.0 では、複数の Web アプリケーションをホストするためのマルチ処理が可能になっています。そのアーキテクチャを、図 6.2 で示します。
図 6.2 IIS 6.0 アーキテクチャ
IIS 6.0 には、カーネルに実装された、新しい HTTP リスナー (HTTP.Sys) が含まれています。要求は、ASP.NET アプリケーションと Web サービスをホストとする、複数のワーカー プロセス インスタンス (W3wp.exe) の 1 つに送られます。
プロセス モデル
Windows 2000 の場合と Windows 2003の場合での ASP.NET アーキテクチャの主な違いは、Windows 2003 では、Web アプリケーションをホストするために、別々の IIS ワーカー プロセスを使用できるという点です。
デフォルトでは、IIS ワーカー プロセス インスタンスは、NT Authority\NetworkService アカウントを使って動作します。このアカウントは、ネットワーク上のコンピュータ アカウントとして働く、最低限の特権しか持たないローカル アカウントです。Network Service アカウントのコンテキストで動作する Web アプリケーションは、コンピュータの資格情報を、認証のためにリモート サーバーに提供します。
また、IIS 6.0 は、IIS 5.0 ASP.NET ワーカー プロセス モデルをサポートするための、後方互換性を備えています。
カーネル モード キャッシュ
アプリケーションを Windows Server 2003 上に展開する場合、ASP.NET ページは、自動的に IIS 6.0 カーネル キャッシュのメリットを受けることができます。カーネル キャッシュは、HTTP.sys カーネル モード デバイス ドライバによって管理されます。このドライバは、すべての HTTP 要求を処理します。キャッシュ 応答への要求は、ユーザー モードへの切り替えを経ることなく供給されるため、カーネル モード キャッシュによってパフォーマンスは大幅に向上し得ます。
Machine.config ファイルの以下のデフォルト設定により、動的に生成された ASP.NET ページは、その下で掲げる制限の下で、カーネル モード キャッシュを利用できます。
<HttpRuntime enableKernelOutputCache="true" . . ./>
動的に生成された ASP.NET ページは、以下の制限の下で、自動的にキャッシュされます。
- ページは、HTTP GET 要求によって取得しなければならない。HTTP POST 要求への応答は、カーネルにキャッシュされません。
- 応答がキャッシュされるとき、クエリ文字列は無視される。 http://contoso.com/myapp.aspx?id=1234 に対する要求をカーネルにキャッシュしたい場合、http://contoso.com/myapp.aspx に対する要求は、クエリ文字列に関係なく、すべてキャッシュから供給されます。
- ページは、例外ポリシーを備えていなければならない。つまり、ページは、Expires ヘッダーを備えていなければなりません。
- ページに VaryByParams があってはならない。
- ページに VaryByHeaders があってはならない。
- ページにセキュリティ制限があってはならない。つまり、要求は匿名でなければならず、認証を要求してはなりません。HTTP.sys ドライバは、匿名応答のみをキャッシュします。
- カーネル キャッシュを認識しない、W3wp.exe ファイル インスタンスについて設定したフィルタがあってはならない。
詳細情報
IIS 6.0 と カーネル キャッシュについての詳細は、IIS 6.0 Resource Kit を参照してください。
Web ガーデン
デフォルトでは、ASP.NET は、利用可能な CPU をすべて使います。Web ガーデン モードでは、ASP.NET は各 CPU につき、1 つのプロセスを作成します。各プロセスは、1 つの CPUに対し、アフィニティを発生させます。Web ガーデンにより、信頼性と堅牢性を備えるレイヤが、1 つ追加されます。あるプロセスがクラッシュしても、他のプロセスが受信要求を処理できるようになります。
Web ガーデンは、以下のシナリオで、特に効力を発揮します。
- アプリケーションが頻繁に STA オブジェクトを使う。
- アプリケーションが、プロセス数の制限を受けるリソース プールにアクセスする。例えば、 1 つのプロセスが、特定数のリソースしか使用できない場合です。
アプリケーションにおける Web ガーデンの効力を判断するには、パフォーマンス テストを行ない、Web ガーデンを使った場合と使わなかった場合の結果を、比較してみてください。通常、上記のようなシナリオでは、CPUを 4 つ、または 8 つ備えたサーバーの方が、大きなメリットを得られます。
メモ: Web ガーデン モードでは、インプロセス セッション状態ストアなど、プロセス アフィニティを発生させるアプローチを用いないでください。
IIS 6.0 vs. ASP.NET プロセス モデル
デフォルトでは、IIS 6.0 で ASP.NET プロセス モデルは利用可能となっていません。Web ガーデンを利用可能にすると、ガベージ コレクタのパフォーマンスに悪影響が及ぶ場合があります。これは、1 つの CPU に縛られていても、サーバー タイプのガベージ コレクタが使われることになるためです。ASP.NET プロセス モデルのデメリットは、各 CPU につき 1 つのワーカー プロセスが作られる点です。CPU ごとにワーカー プロセスが存在するため、追加的なシステム リソースが消費されます。
IIS 6.0 で Web ガーデンを利用可能にする
インターネット インフォメーション サービス (IIS) マネージャ 管理ツールを使って、IIS 6.0 でASP.NET プロセス モデルを利用可能にできます。これを行うには、以下のステップに従ってください。
- Web ガーデンを有効にしたいアプリケーション プールを右クリックし、続いてプロパティをクリックする。
- パフォーマンス タブをクリックする。
- Web ガーデン セクションで、使いたいワーカー プロセス数を指定する。
ASP.NET プロセス モデルで Web ガーデンを利用可能にする
Machine.config ファイルの <processModel> セクションで、webGarden 属性を true にセットし、cpuMask 属性を次のように設定します。
<processModel webGarden="true" cpuMask="0xffffffff" />
cpuMask 属性を設定する
マルチプロセッサ サーバーにおいて、ASP.NET プロセスを動作させる CPUを、cpuMask 属性で指定します。デフォルトでは、すべての CPU が利用可能で、ASP.NET により、各 CPU につき 1 つの プロセスが作成されます。webGarden 属性を false にセットすると、cpuMask 属性は無視され、1 つのワーカー プロセスのみが動作することになります。cpuMask 属性の値は、ASP.NET スレッドを動作させるCPUを示す、ビット バターンを指定します。表 6.3 は、この例を示したものです。
表 6.3: プロセッサ マスク ビット パターン
| CPU | 16進値 | ビット パターン | 結果 |
| 2 | 0x3 | 11 | 2 プロセス、CPU 0 と 1 を使用 |
| 4 | 0xF | 1111 | 4 プロセス、CPU 0、1、2、3 を使用 |
| 4 | 0xC | 1100 | 2 プロセス、CPU 2 と 3 を使用 |
| 4 | 0xD | 1101 | 3 プロセス、CPU 0、2、3 を使用 |
| 8 | 0xFF | 11111111 | 8 プロセス、CPU 0、1、2、3、4、5、6、7 を使用 |
| 8 | 0xF0 | 11110000 | 4 プロセス、CPU 4、5、6、7 を使用 |
詳細情報
ASP.NET Web ガーデンの使用方法についての詳細は、Microsoft サポート技術情報の Knowledge Base の文書 815156 「どのように特定のプロセッサにマルチプロセッサ システムの ASP.NET を制限するには」を参照してください。
ガベージ コレクタ設定フラグ
デフォルトでは、マルチプロセッサ サーバーを使っている場合、サーバー GC がロードされます。シングル プロセッサ サーバーを使っている場合は、ワークステーション GC がロードされます。本稿執筆時点で、.NET Framework version 1.1 サービス パック 1 (SP1) は、ASP.NET がサーバー GC と ワークステーション GC のいずれをロードするかを設定できるスイッチを提供しています。このスイッチを使えば、マルチプロセス サーバーでも、ワークステーション GC をロードするように、ASP.NET の設定を変えることができます。
Workstation GC を使うべき場合
マルチプロセッサ コンピュータ上の Windows Server 2003 で、分離した複数のワーカー プロセスをホストしている場合、ワークステーション GC と非並行モードを使ってください。
サーバー GC は、スループット、メモリ消費、マルチプロセッサ スケーラビリティについて最適化されています。それでも、マルチプロセッサ サーバー上で動作する Windows Server 2003 でワークステーション GC を使うと、場合によっては、ワーカー プロセスごとのメモリ消費量が著しく減ると共に、ホスト可能な分離ワーカー プロセス数が大幅に増加します。並行ガベージ コレクションを無効にすることにより、ホスト可能な分離ワーカー プロセス数は、さらに増加します。
ワークステーション GC への切り替え設定
ワークステーション GC を使用するように ASP.NET を設定するには、以下のコンフィギュレーションを Aspnet.config ファイルに追加します。Aspnet.config ファイルは、Aspnet_isapi.dll ファイルと同じディレクトリ内にあります。
<configuration>
<runtime>
<gcServer enabled="false"/>
<gcConcurrent enabled="false"/>
</runtime>
</configuration>
詳細情報
ガベージ コレクション全般およびサーバー GC とワークステーション GC についての詳細は、第 5 章「マネージ コード パフォーマンスの向上」の「ガベージ コレクションについての説明」を参照してください。
展開上の考慮事項
物理的展開は、アプリケーションのパフォーマンス特性およびスケーラビリティ特性を決める上で、重要な役割を果たします。リモート中間層を導入する特別な理由がなければ、Web アプリケーションのプレゼンテーション層、ビジネス層、データ アクセス層は、Web サーバー上に展開すべきです。唯一のリモート ホップは、データペースへのホップのみとすべきです。ここでは、以下に掲げる展開上の主な考慮事項について説明します。
-
不要なプロセス ホップを避ける。
-
リモート中間層によるパフォーマンスへの影響を理解する。
-
HTTP パイプラインを短くする。
-
メモリ制限を設定する。
-
トレースとデバッグを無効にする。
-
コンテンツの更新によって追加的なアセンブリがロードされないようにする。
-
負荷の大きい状況では XCOPY を避ける。
-
ページのプリコンパイルを検討する。
-
Web ガーデン設定を考慮する。
-
HTTP 圧縮の使用を検討する。
-
周辺キャッシュの使用を検討する。
不要なプロセス ホップを避ける
プロセス ホップは、マシン ホップほど高くつきませんが、可能な限り避けるべきです。プロセス ホップは、プロセス間通信 (IPC) とマーシャリングを必要とするため、追加的なオーバーヘッドを発生させます。例えば、Enterprise Services を使うソリューションでは、Enterprise Services を中間リモート層に置く必要がある場合を除き、可能ならライブラリ アプリケーションを使ってください。
リモート中間層によるパフォーマンスへの影響を理解する
可能な場合、プロセス間通信やコンピュータ間通信によるオーバーヘッドを避けてください。ビジネス要件によってリモート中間層の使用が必須の場合を除き、プレゼンテーション、ビジネス、データ アクセスの各ロジックは、Web サーバーに置いてください。ビジネス アセンブリとデータ アクセス アセンブリは、アプリケーションの Bin ディレクトリに展開してください。ただし、以下のような理由により、リモート中間層が必要となる場合もあります。
- インターネット向け Web アプリケーションと、その他の内部エンタープライズ アプリケーションの間で、ビジネス ロジックを共有したい。
- スケール アウト要件と障害耐性要件により、中間層クラスタまたは負荷分散サーバーの使用を義務付けられている。
- 会社のセキュリティ ポリシーにより、Web サーバーへのビジネス ロジックの展開を禁止されている。
リモート中間層を使用してアプリケーションを展開する必要がある場合、同一環境での計測およびテストが可能となるように、その必要性を、早い段階から認識してください。
HTTP パイプラインを短くする
HTTP パイプライン シーケンスは、Machine.config ファイルの設定によって決められます。不要なモジュールは、コメントに入れてください。例えば、フォーム認証を使用しない場合は、Machine.config ファイルのフォーム認証用エントリをコメントに入れるか、特定のアプリケーションについて、Machine.config ファイルのエントリを明示的に削除してください。以下のサンプルでは、エントリをコメント アウトする方法を示しています。
<httpModules>
<!-- <add name="FormsAuthentication"
type="System.Web.Security.FormsAuthenticationModule"/> -->
</httpModules>
以下の Web.config ファイル サンプルでは、特定のアプリケーションについてエントリを削除する方法を示しています。
<httpModules>
<remove name="FormsAuthentication" />
</httpModules>
使用していない HTTP モジュールを使っているアプリケーションが、Web サーバーに他にもある場合は、アプリケーションの Web.config ファイルから HTTP モジュールを削除してください。この場合は、Machine.config ファイルの HTTP モジュールをコメント アウトするのではなく、上記のようにしてください。
メモリ制限を設定する
アプリケーションを展開する前に、メモリ制限を設定してください。これにより、ASP.NET キャッシュのパフォーマンスとサーバーの安定性を最適化することができます。
詳細情報
詳細は、この章の「キャッシングに関するガイドライン」にある「メモリ制限を設定する」を参照してください。
トレースとデバッグを無効にする
アプリケーションを展開する前に、トレースとデバッグを無効にしてください。トレースとデバッグは、パフォーマンス上の問題を発生させる場合があります。アプリケーションが生産段階に入っている間にトレースとデバッグを行うのは、好ましくありません
以下のようにして、Machine.config ファイルと Web.config ファイルでトレースとデバッグを無効にしてください。
<configuration>
<system.web>
<trace enabled="false" pageOutput="false" />
<compilation debug="false" />
</system.web>
</configuration>
メモ: 個々のページでも、トレースとデバッグが無効になっていることを検証すべき場合も、あるかもしれません。個々のページの設定は、Web.config ファイルの設定より優先されます。
詳細情報
詳細は、Microsoft サポート技術情報の Knowledge Base 文書 815157 「HOW TO: ASP.NET アプリケーションのデバッグを使用しないでください」 を参照してください。
コンテンツの更新によって追加的なアセンブリがロードされないようにする
.aspx ページや .ascx ページを更新してアプリケーションを再起動しないと、問題が発生する場合があります。以下のシナリオについて、考えてみてください。ディレクトリに、以下のような 5 つのページがあるとします。
\mydir
Page1.aspx
Page2.aspx
Page3.aspx
Page4.aspx
Page5.aspx
Mydir ディレクトリのページが最初に要求されると、以下で示すように、ディレクトリ内のすべてのページが、1 つのアセンブリにコンパイルされます。
Assembly1.dll {page1.aspx, page2.aspx, page3.aspx, page4.aspx}
Page1.aspx が更新されると、Page1.aspx のために新しいアセンブリが 1 つ作成されます。これにより、以下で示すように、2 つのアセンブリができます。
Assembly1.dll {page1.aspx, page2.aspx, page3.aspx, page4.aspx, page5.aspx}
Assembly2.dll {page1.aspx}
Page2.aspx が更新されると、Page2.aspx のために新しいアセンブリが 1 つ作成されます。これにより、以下で示すように、3 つのアセンブリができます。
Assembly1.dll {page1.aspx, page2.aspx, page3.aspx, page4.aspx, page5.aspx}
Assembly2.dll {page1.aspx}
Assembly3.dll {page2.aspx}
このような問題により、複数のアセンブリが生成されるのを防ぐには、コンテンツ更新時に以下のステップに従ってください。
- Web サーバーをローテーションから外す。
- IIS を再起動する。
- Temporary ASP.NET Files フォルダ内のファイルをすべて削除する。
- 各ディレクトリについて 1 ページを要求し、各ディレクトリがバッチ コンパイルされることを確認する。
- Web サーバーをローテーションに戻す。
コンテンツの更新に対するこのアプローチにより、他の問題も解決します。バッチ コンパイルの完了前にサーバーがローテーションに戻されると、一部のページが、1 つのアセンブリとしてコンパイルされる場合があります。バッチ コンパイルされているのと同じディレクトリのページのバッチ コンパイル中に別の要求が出されると、そのページは、1 つのアセンブリとしてコンパイルされます。Web サーバーをローテーションから外し、後で戻すことにより、この問題を避けることができます。
負荷の大きい状況では XCOPY を避ける
XCOPY 展開では、アプリケーションや IIS を閉じる必要がないため、展開は容易になります。しかし、生産環境では、サーバーをローテーションから外し、IIS を閉じ、XCOPY 更新を実行してから、IIS を再起動し、サーバーをローテーションへ戻すべきです。
負荷の高い状況では、この手順に従うことは、特に重要です。例えば、仮想ディレクトリの 50 のファイルをコピーし、各ファイルのコピーに 100 ミリ秒を要する場合、ファイル全体をコピーするのに 5 秒かかります。この間、アプリケーションのアプリケーション ドメインは、アンロードとロードを複数回繰り返すかもしれません。また、一定のファイルは、XCOPY プロセス (Xcopy.exe) によってブロックされる場合があります。あるファイルを XCOPY プロセスがロックした場合、ワーカー プロセスとコンパイラは、そのファイルにアクセスできません。
更新のために XCOPY 展開を使いたければ、.NET Framework version 1.1 には、waitChangeNotification 設定と maxWaitChangeNotification 設定が含まれています。これらの設定を使い、ここで説明した XCOPY に関する問題を解消できます。
メモ: これらの設定は、.NET Framework version 1.0 の修正プログラムでも使用可能です。詳細は、Microsoft サポート技術情報の Knowledge Base 文書 810281 「エラー メッセージ: ほかのプロセスが使用しているため、AssemblyName を Access がファイルすることができません」を参照してください。
waitChangeNotification の設定値は、最も大きいファイルをコピーするのに XCOPY が要する、総時間に基づいて決めるべきです。maxWaitChangeNotification 設定の値は、全てのファイルをコピーするのに XCOPY が要する総時間プラス少しの予備時間に基づいて決めるべきです。
詳細情報
詳細は、Microsoft サポート技術情報の Knowledge Base の以下の文書を参照してください。
ページのプリコンパイルを検討する
ユーザーが ASP.NET ファイルのバッチ コンパイルをしなくて済むようにするためには、各ディレクトについて 1 ページに 1 つの要求を送り、Web サーバーをローテーションに戻す前に、プロセッサが再びアイドル状態となるまで待機します。これにより、バッチ コンパイルを開始できるようになります。その結果、ユーザーの体感パフォーマンスは向上し、要求の処理と同時にディレクトリのバッチ コンパイルを実行する負担は軽減されます。
Web ガーデン設定を考慮する
アプリケーションが STA オブジェクトを頻繁に使う場合や、アプリケーションがプロセス数の制限を受けるリソース プールにアクセスする場合は、Web ガーデンの使用を検討してください。
アプリケーションにおける Web ガーデンの効力を判断するには、パフォーマンス テストを行い、Web ガーデンを使った場合と使わなかった場合で、結果を比べてみてください。
HTTP 圧縮の使用を検討する
ほとんどのブラウザ、および IIS は、HTTP 圧縮をサポートしています。クライアントが狭帯域接続で Web サーバーにアクセスする場合、通常は HTTP 圧縮によってパフォーマンスが向上します。
詳細情報
HTTP 圧縮についての詳細、および IIS で圧縮を有効にする方法については、MSDN 上の “Using HTTP Compression (IIS 6.0)”を参照してください。
周辺キャッシュの使用を検討する
周辺ネットワークは、インターネットや、他の大型ネットワークからのアクセスを制御することにより、イントラネットを外部侵入から保護します。周辺ネットワークは、プロキシ サーバー、パケット フィルタ、ゲートウェイなどの、2 つ以上のネットワークの間に境界を設けるシステムの組み合わせで成り立っています。
周辺ネットワークにプロキシ サーバーが含まれている場合、プロキシ サーバーでのキャッシングを可能にし、パフォーマンスを向上させることを検討してください。
まとめ
この章では、ASP.NET アプリケーションの開発中に遭遇することの多い落とし穴やボトルネックについて説明しました。そして、これらの問題を回避し、克服するために必要なステップを示しました。この章で掲げたガイダンスやアドバイスに従えば、パフォーマンスの非常に高い ASP.NET アプリケーションを構築することができます。
種類にかかわらず、パフォーマンスの高いアプリケーションを構築するには、パフォーマンスについて、初めから考慮する必要があります。物理的な展開環境によって課されかねない制約を考慮に入れた上で、健全なアーキテクチャと設計を組み立てなければなりません。開発中は、ベスト プラクティスに基づくアプローチを採用する必要があります。また、アプリケーションが、既定のパフォーマンス目標値を満たして動作することを保証するため、パフォーマンスを継続的に計測しなければなりません。計測は、ライフ サイクルを通して続けるべきです。最後に、展開時には、アプリケーションが動作する環境に合わせた設定を考慮しなければなりません。
補足資料
詳細は、以下の資料を参照してください。
- 印刷可能なチェックリストについては、このガイドの「チェックリスト」にある「チェックリスト: ASP.NET パフォーマンス」 を参照してください。
- 第 4 章「パフォーマンスとスケーラビリティのための .NET アプリケーションのアーキテクチャと設計のレビュー」
- 第 13 章「コード レビュー: .NET アプリケーション パフォーマンス」の「ASP.NET」
- 第 15 章「.NET アプリケーション パフォーマンスの計測」の「ASP.NET」
- 第 16 章「.NET アプリケーション パフォーマンスのテスト」
- 第 17 章「.NET アプリケーション パフォーマンスのチューニング」の「ASP.NET のチューニング」
IIS 6.0 についての詳細は、以下の資料を参照してください。