39

問題:

ASP.NET サイトでセッションを使用すると、複数の要求をほぼ同時にロードすると、劇的な遅延 (500 ミリ秒の倍数) が発生します。

より具体的には、私の問題

私たちのウェブサイトは、SessionId のみにセッションを使用します。このキーを使用して、ユーザー情報などを含む db テーブルを検索します。これは最小限のセッション データを格納するため、適切な設計のように思われました。残念ながら、セッションに何も保存しないと SessionId が変更されるため、Session["KeepId"] = 1;. これは、SessionId が変更されないようにするのに十分です。

一見関係のないことですが、このサイトはコントローラー アクションを通じてカスタマイズされた製品画像を提供します。このアクションは、必要に応じて画像を生成し、キャッシュされた画像にリダイレクトします。これは、1 つの Web ページに画像やその他のリソースが含まれ、ASP.NET パイプラインを介して数十の要求が送信されることを意味します。

何らかの理由で、(a) 同時に複数のリクエストを送信し、(b) なんらかの方法でセッションが関与している場合、約 1/2 の時間でランダムな 500 ミリ秒の遅延が発生します。場合によっては、これらの遅延は 1000 ミリ秒以上になり、常に最大 500 ミリ秒の間隔で増加します。リクエストが多いほど、待ち時間が長くなります。数十の画像を含むページでは、画像によっては 10 秒以上待機することがあります。

問題の再現方法:

  1. 空の ASP.NET MVC Web アプリケーションを作成する
  2. 空のコントローラー アクションを作成します。

    public class HomeController : Controller
    {
      public ActionResult Test()
      {
        return new EmptyResult();
      }
    }
    
  3. そのアクションにヒットするいくつかの img タグを含む test.html ページを作成します。

    <img src="Home/Test?1" />
    <img src="Home/Test?2" />
    <img src="Home/Test?3" />
    <img src="Home/Test?4" />
    
  4. ページを実行して、firebug で高速な読み込み時間を確認します。

    各画像はかなり高速にロードされます

  5. 次のいずれかを実行します (どちらも同じ結果になります)。

    • 空の Session_Start ハンドラーを Global.asax.cs に追加します。

      public void Session_Start() { }
      
    • 何かをセッションに入れる

      public class HomeController : Controller
      {
        public ActionResult Test()
        {
          HttpContext.Current.Session["Test"] = 1;
          return new EmptyResult();
        }
      }
      
  6. ページを再度実行すると、応答がときどき/ランダムに遅延することに注意してください。

    一部のリクエストで長時間の遅延が発生する

私がこれまでに知っていること

私の質問

セッションを使用してこれらの遅延を回避するにはどうすればよいですか? 誰も修正を知っていますか?

修正がない場合は、セッションをバイパスして Cookie を直接使用する必要があると思います (セッションは Cookie を使用します)。

4

4 に答える 4

23

Gats が述べたように、問題は ASP.NET がセッションをロックするため、同じセッションに対する各要求を連続して実行する必要があることです。

厄介な部分は、(例から) 5 つの要求すべてを連続して実行した場合、約 40 ミリ秒かかることです。ASP.NET のロックでは、1000 ミリ秒を超えます。ASP.NET は、「セッションが使用中の場合は、500 ミリ秒スリープしてから再試行してください」と言っているようです。

InProc の代わりに StateServer または SqlServer を使用しても役に立ちません。ASP.NET は依然としてセッションをロックします。

いくつかの異なる修正があります。最終的に最初のものを使用しました。

代わりに Cookie を使用する

Cookie はすべてのリクエストのヘッダーで送信されるため、軽量に保ち、機密情報を避ける必要があります。そうは言っても、セッションはデフォルトで Cookie を使用して、ASPNET_SessionId 文字列を保存することで誰が誰であるかを記憶します。私が必要としていたのはその ID だけだったので、ASP.NET のセッション ロックが Cookie 内の ID の単なるラッパーである場合に耐える理由はありません。

したがって、セッションを完全に回避し、代わりに GUID を Cookie に保存します。Cookie はロックされないため、遅延は修正されます。

MVC 属性を使用する

セッションを使用できますが、セッションを読み取り専用にすることで、特定のリクエストのロックを回避できます。

MVC 3 アプリケーションの場合、コントローラーでこの属性を使用して、セッションを読み取り専用にします (特定のアクションでは機能しません)。

[SessionState(SessionStateBehavior.ReadOnly)]

一部のルートのセッションを無効にする

MVC ルーティングを介してセッションを無効にすることもできますが、少し複雑です。

特定のページでセッションを無効にする

WebForms の場合、特定の aspx ページのセッションを無効にすることができます。

于 2011-12-02T16:01:29.730 に答える
13

ASP.NET フレームワークを調べました。セッション状態が管理されるクラスは、ここで定義されています: http://referencesource.microsoft.com/#System.Web/State/SessionStateModule.cs,114

これがどのように機能するかです(簡略化されたコード):

LOCKED_ITEM_POLLING_INTERVAL = 500ms;
LOCKED_ITEM_POLLING_DELTA = 250ms;

bool GetSessionStateItem() {
    item = sessionStore.GetItem(out locked);

    if (item == null && locked) {
        PollLockedSession();
        return false;
    }
    return true;
}

void PollLockedSession() {
    if (timer == null) {
        timer = CreateTimer(PollLockedSessionCallback, LOCKED_ITEM_POLLING_INTERVAL);
    }
}

void PollLockedSessionCallback() {
    if (DateTime.UtcNow - lastPollCompleted >= LOCKED_ITEM_POLLING_DELTA) {             
          isCompleted = GetSessionStateItem();
          lastPollCompleted = DateTime.UtcNow;

          if(isCompleted) {
              ResetPollTimer();
          }
    } 
}

概要 : セッション アイテムが別のスレッドによってロックされているために取得できない場合、タイマーが作成されます。セッションを定期的にプールして、アイテムを再度取得しようとします (デフォルトでは 500 ミリ秒ごと)。アイテムが正常に取得されると、タイマーはクリアされます。さらに、GetSessionStateItem() 呼び出しの間に所定の遅延があることを確認するためのチェックがあります ( LOCKED_ITEM_POLLING_DELTA= デフォルトでは 250 ミリ秒)。


レジストリに次のキーを作成することで、のデフォルト値を変更できLOCKED_ITEM_POLLING_INTERVALます (これは、マシン上で実行されているすべての Web サイトに影響します)。

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\ASP.NET
SessionStateLockedItemPollInterval (this is a DWORD)

別の方法 (ハック) は、リフレクションによって値を変更することです。

Type type = typeof(SessionStateModule);
FieldInfo fieldInfo = type.GetField("LOCKED_ITEM_POLLING_INTERVAL",
   BindingFlags.NonPublic | BindingFlags.Static);
fieldInfo.SetValue(null, 100); //100ms

免責事項 : この値を下げた場合の正確な結果は不明です。サーバーでスレッドの競合が増加したり、潜在的なデッドロックが発生したりする可能性があります...より良い解決策は、セッション状態の使用を避けるかSessionStateBehavior.ReadOnly、他のユーザーが提案した属性でコントローラーを装飾することです。

于 2015-09-18T08:06:42.183 に答える
2

ASP.NET でセッションを高速化する方法はいくつかあります。まず、いくつかの重要なポイント:

  1. セッションはスレッド セーフです。つまり、セッションにアクセスする必要がある同時クライアント リクエストには適していません。セッションタイプのデータにアクセスするために非同期プロセスが必要な場合は、キャッシュやデータベースなどの他の形式の一時ストレージを使用できます (非同期進行状況追跡にはこれが必要です)。そうしないと、ユーザーのセッション情報がリクエストのコンテキストでロックされ、変更による問題が防止されます。したがって、リクエスト 1 が正常である理由は、次のリクエストに遅延があります。PSこれは、セッション内のデータの整合性を保証するために絶対に必要です... eコマースのユーザー注文のように。

  2. セッションはサーバー上でシリアライズされます。Cookie はセッションを維持するために使用されますが、これは別の種類の Cookie であるため、パフォーマンスに大きな違いは期待できません。

したがって、セッション リクエストを高速化するための私のお気に入りの方法は、常に状態サーバーを使用することです。これにより、進行中の問題が取り除かれ、テスト中にサイトに再度ログインしなくてもプロジェクトを再構築できることも意味します。また、単純な負荷分散シナリオでセッションを可能にします。 http://msdn.microsoft.com/en-us/library/ms972429.aspx

PS私は、技術的にアウトプロセス状態のサービスが遅くなるはずであることを追加する必要があります。私の経験では、開発がより高速で簡単ですが、それは使用方法に依存すると思います。

于 2011-12-01T22:27:14.330 に答える