July 2011

Volume 26 Number 07

この記事は機械翻訳されたものです。

MVC フィルター - MVC アプリケーションにパフォーマンス カウンターを簡単に追加する (機械翻訳)

Ben Grover | July 2011

エンタープライズ Web アプリケーションを通常動作には、監視ホストの追加のコードと、アプリケーションの動作が含まれます。この記事では、私は、私モデル-ビュー-コント ローラー (MVC) フィルターを使っていますをきれいにし、さまざまな方法でアプリケーション全体に広がっていた繰り返し、混乱のコードを置換する方法を説明します。

運用担当マネージャーは多くの場合を Microsoft Operations Manager (Web サイト (またはサービス) の状態を監視し、パフォーマンス カウンターを使用してしきい値に基づくアラームをトリガーする MOM) を設定します。これらのアラームは、低下の経験、Web サイトを迅速に発見されることを確認できます。

問題コード

私は (ASP を使用します。 関連プロジェクトで働いています。ネットの MVC フレームワーク) の要件が Web ページと運用チームを支援するために Web サービスのパフォーマンス カウンターを追加します。運用チームが各ページにカウンターが必要です: 要求の待機時間、失敗率 1 要求の総数。

問題は、常にこのような要件の実装で発生するようであります。私は以前のコーディングのマイルス トーンの一環として追加した他の勤勉な開発者からのこれらのカウンターの現在の実装で探し始めた。私は失望しました。私は確信している、あるすべてがある-、コードとうんざりを見て。私が見るものでしたか?繰り返しを振りかけたコードメソッド、ちょうどいくつかの変更するには、変数名をここにあります。私は現在の実装で幸せではなかった。

私を尻込みしたコードで見ることができます図 1

図 1コードをした私のうんざり

public ActionResult AccountProfileInformation()
{
  try
  {
    totalRequestsAccountInfoCounter.Increment();
    // Start counter for latency
    long startTime = Stopwatch.GetTimestamp();
 
    // Perform some operations action here
    long stopTime = Stopwatch.GetTimestamp();
    latencyAccountInfoCounter.IncrementBy((stopTime - startTime) / 
      Stopwatch.Frequency);
    latencyAccountInfoBaseCounter.Increment();
  }
  catch (Exception e)
  {
    failureAccountInfoCounterCounter.Increment();
  }
  return View();
}

このコードを見て、私の各プロジェクトのアクション メソッドから削除したいのですが。 知っていた。この種のパターンの場合、パフォーマンスを監視するために、すべての余分なコードのための真のメソッドのコードが表示する非常に困難です。私は、アクション メソッドのごみのないように、このコードをリファクターする巧妙な方法を探していた。MVC のフィルターを入力します。

MVC フィルター

MVC フィルターはカスタム属性をアクションに置く方法 (コント ローラー) の一般的な機能を追加します。MVC フィルター処理前と処理後の動作を追加できます。組み込みの MVC フィルターのリストはここにあります:bit.ly/jSaD5N。私は OutputCache などの組み込みのフィルターの使用していたが、私は MVC フィルター多くの隠された力を私は決してにタップしていたことは知っていました (続きを読むにフィルター属性についてを参照してくださいbit.ly/kMPBYB)。

従って、私は自分自身を考え始めた: 場合すべてこれらのパフォーマンス カウンターは MVC フィルター属性のロジックのカプセル化できませんでしたか?アイデアが生まれました !要件、次の操作を上記各パフォーマンス カウンターの可能性があります。

  1. 1 秒あたりの要求の総数
    1. IActionFilter は、2 つのメソッドを実装する: OnActionExecuting と OnActionExecuted
    2. OnActionExecuting のカウンターをインクリメントします。
  2. 遅延を要求します。
    1. IResultFilter は、2 つのメソッドを実装する: OnResultExecuting と OnResultExecuted
    2. OnActionExecuting で、timer を起動し、OnResultExecuted 中の待機時間を記録
  3. 故障率
    1. IExceptionFilter は、OnException メソッドを実装します。

プロセスに示しています図 2

MVC フィルター処理パイプライン

図 2MVC フィルター処理パイプライン

示すようには、各フィルターの使用について説明します図 2

IActionFilterOnActionExecuting (行 2 a) を実行する、アクション メソッドを実行する前に。OnActionExecuted (行 2 b) には、アクション メソッドの実行後、しかし、結果が実行される前に実行されます。

IResultFilterOnResultExecuting (行 4 a) を実行 (ビューのレンダリングなど) はアクションの結果を実行する前に。アクションが実行された後は、OnResultExecuted (行 4 b) を実行します。

IExceptionFilter OnException (には示されていません 図 2明確にするため) 例外スロー (未処理し、あるときに) を実行します。

IAuthorizationFilter OnAuthorization (に含まれていない 図 2、またこの資料で使用) の承認が必要なときに呼び出されます。

カウンターを管理します。

このパフォーマンス カウンターのフィルター属性を使用する場合は、しかし、私は問題を有する: とすればパフォーマンス カウンター (操作ごとに) これらのフィルターの各に実行時にですか?私は、個別のフィルター属性クラスの各アクションが望んでいません。この場合は、ハードコードするをパフォーマンス カウンター名属性で必要があります。ソリューションの実装に必要なクラス名の数の爆発になります。私は私は最初、マイクロソフトで働いていた際、技術を反映しました。NET Framework: 反射 (しゃれ意図 !)。リフレクションは、MVC フレームワークによっても大きく活用されます。ここでの詳細を学ぶことができます:bit.ly/iPHdHz

私の考えは 2 つのクラスを作成するでした。

  1. WebCounterAttribute
    1. インタ フェース (IExceptionFilter、IActionFilter、IResultFilter) の実装、MVC のフィルターします。
    2. WebCounterManager に格納されているインクリメント カウンター
  2. WebCounterManager
    1. 各 MVC アクションから、WebCounterAttributes を読み込み、リフレクション コードを実装します。
    2. パフォーマンス カウンター オブジェクトの参照を容易にするためにマップを保存します。
    3. マップに格納されているカウンターをインクリメントするメソッドを提供します。

設計の実装

これらのクラスを持つ、私は WebCounterAttribute のパフォーマンス カウンターを実装するには、ここで示すように思いましたアクション メソッドのそれぞれに飾ることができます。

 

public sealed class WebCounterAttribute : FilterAttribute, IActionFilter, IExceptionFilter, IResultFilter
{
  /// Interface implementations not shown
}

サンプルの操作方法はここにあります。

[WebCounter("Contoso Site", "AccountProfileInformation")]
public ActionResult AccountProfileInformation()
{
  // Some model loading
  return View();
}

私はリフレクションを使用して、Application_Start メソッドでこれらの属性を読み取り、カウンター各これらのアクションを示すように作成し、図 3。(カウンターは、システム セットアップ プログラムでは、登録されているが、カウンタのインスタンスをコードで作成されるを注意してください)。

図 3アセンブリ上を反映して

/// <summary>
/// This method reflects over the given assembly(ies) in a given path 
/// and creates the base operations required  perf counters
/// </summary>
/// <param name="assemblyPath"></param>
/// <param name="assemblyFilter"></param>
public void Create(string assemblyPath, string assemblyFilter)
{
  counterMap = new Dictionary<string, PerformanceCounter>();
            
  foreach (string assemblyName in Directory.EnumerateFileSystemEntries(
    assemblyPath, assemblyFilter)) 
  {
    Type[] allTypes = Assembly.LoadFrom(assemblyName).GetTypes();
 
    foreach (Type t in allTypes)
    {
      if (typeof(IController).IsAssignableFrom(t))
      {
        MemberInfo[] infos = Type.GetType(t.AssemblyQualifiedName).GetMembers();
 
        foreach (MemberInfo memberInfo in infos)
        {
          foreach (object info in memberInfo.GetCustomAttributes(
            typeof(WebCounterAttribute), true))
          {
            WebCounterAttribute webPerfCounter = info as WebCounterAttribute;
            string category = webPerfCounter.Category;
            string instance = webPerfCounter.Instance;
            // Create total rollup instances, if they don't exist
            foreach (string type in CounterTypeNames)
            {
              if (!counterMap.ContainsKey(KeyBuilder(Total, type)))
              {
                counterMap.Add(KeyBuilder(Total, type), 
                  CreateInstance(category, type, Total));
              }
            }
            // Create performance counters
            foreach (string type in CounterTypeNames)
            {
              counterMap.Add(KeyBuilder(instance, type), 
                CreateInstance(category, type, instance));
            }
          }
        }
      }
    }
  }
}

マップが表示されます重要な行があります。

 

(counterMap.Add(KeyBuilder(instance, type), CreateInstance(category, type, instance));),

WebCounterAttribute カウンター タイプなど、アクションの特定のインスタンスの間のマッピングを作成し、作成した PerformanceCounter インスタンスにマップします。

私は、このマッピングを使用して、PerformanceCounter インスタンスを検索 (およびそれをインクリメントするには) 私は、WebCounterAttribute のインスタンスを有効にするコードを書くことができる (を参照してください図 4)。

図 4 WebCounterManager RecordLatency

/// <summary>
/// Record the latency for a given instance name
/// </summary>
/// <param name="instance"></param>
/// <param name="latency"></param>
public void RecordLatency(string instance, long latency)
{
  if (counterMap.ContainsKey(KeyBuilder(instance,   
    CounterTypeNames[(int)CounterTypes.AverageLatency]))
    && counterMap.ContainsKey(KeyBuilder(instance,   
    CounterTypeNames[(int)CounterTypes.AverageLatencyBase])))
  {
    counterMap[KeyBuilder(instance, 
      CounterTypeNames[(int)CounterTypes.AverageLatency])].IncrementBy(latency);
    counterMap[KeyBuilder(Total, 
      CounterTypeNames[(int)CounterTypes.AverageLatency])].IncrementBy(latency);
    counterMap[KeyBuilder(instance, 
      CounterTypeNames[(int)CounterTypes.AverageLatencyBase])].Increment();
    counterMap[KeyBuilder(Total, 
      CounterTypeNames[(int)CounterTypes.AverageLatencyBase])].Increment();
  }
}

これらのフィルターを実行すると、[パフォーマンス カウンター データを記録できます。たとえば、図 5、パフォーマンス遅延を記録の実装を参照してください。

図 5WebCounterAttribute WebCounterManager RecordLatency を呼び出す

/// <summary>
/// This method occurs when the result has been executed (this is just 
/// before the response is returned). 
/// This method records the latency from request begin to response return.
/// </summary>
/// <param name="filterContext"></param>
public void  OnResultExecuted(ResultExecutedContext filterContext)
{
  // Stop counter for latency
  long time = Stopwatch.GetTimestamp() - startTime;
  WebCounterManager countManager = GetWebCounterManager(filterContext.HttpContext);
  if (countManager != null)
  {
    countManager.RecordLatency(Instance, time);
    ...
  }
}
private WebCounterManager GetWebCounterManager(HttpContextBase context)
{
  WebCounterManager manager =  
    context.Application[WebCounterManager.WebCounterManagerApplicationKey] 
    as WebCounterManager;
  return manager;
}

私は、アプリケーションの状態から、WebCounterManager を得ているこの呼び出しに注意してくださいよ。 これが機能するために、あなたの global.asax.cs にコードを追加する必要があります。

WebCounterManager webCounterMgr = new WebCounterManager();
webCounterMgr.Create(Server.Map("~/bin"), "*.dll");
Application[WebCounterManager.WebCounterManagerApplicationKey] = webCounterMgr;

 

まとめると、MVC フィルター、エレガントなソリューションを大きく繰り返されるコード パターンを提供します。彼らを助けるクリーナーと管理が容易に記述したコードは共通のコードをリファクタリングします。明らかに、バランスの優雅さと実装の容易さの間に打たれる必要があります。私の場合では、約 50 の Web ページにパフォーマンス カウンターを追加いた。コードの読みやすさと明確さの面での節約は間違いなく追加の努力の価値があった。

MVC フィルターでパフォーマンス カウンターを処理しているかどうか、ログや監査、無限の可能性を実装必要なロジックを見つけるので、目障りな、されることがなく動作を追加するに最適な方法です。

Ben Grover は microsoft のレドモンド、ワシントン州、ここで彼は働いている exchange Lync に windows からの複数のチームのプログラマー

この記事のレビュー、技術スタッフに感謝:Eilon リプトン