1

現在、同時ユーザーに苦労しているWebサイトがあります。

プロジェクトの非常に高レベルな背景は次のとおりです。

  • 従来の ASP.NET MVC 3 プロジェクト (.NET 4)
  • コアコードの大幅な書き直しはできません
  • 実行に最も時間がかかる主なエントリ ポイントは、コントローラーのSubmitSearchアクションです。Search応答までの平均時間は 5 ~ 10 秒です。

したがって、2 番目のポイントで概説するように、このプロジェクトで大きなセクションを書き直すことにあまり時間をかけたくありません。ただし、同時接続ユーザーを増やしたいと考えています。より多くの作業が必要になるため、他の変更やパフォーマンスの向上は考えていません。

私たちが見ているのは、より多くの人がSubmitSearchにアクセスするにつれて、一般的に Web サイトの速度が低下するということです。これは、すべての IIS スレッドが検索の実行中にロックされていることが原因である可能性が最も高いです。

通常の CLR スレッドでアクションを実装AsyncControllerして実行することを検討しています。SubmitSearch実装方法は次のとおりです。

SubmitSearchこれが元の方法であると仮定します。

/// <summary>
/// Submits a search for execution.
/// </summary>
/// <param name="searchData">The search data</param>
/// <returns></returns>
public virtual ActionResult SubmitSearch(SearchFormModel searchData)
{
    //our search code
}

変換したいと思っていた最も簡単な方法AsyncControllerは、単純に次のようにすることです。

/// <summary>
/// Submits a search for execution.
/// </summary>
/// <param name="searchData">The search data</param>
/// <returns></returns>
protected virtual ActionResult SubmitSearch(SearchFormModel searchData)
{
    //our search code
}

/// <summary>
/// Asynchronous Search entry point
/// </summary>
/// <param name="searchData"></param>
public void SubmitSearchAsync(SearchFormModel searchData)
{
    AsyncManager.OutstandingOperations.Increment();
    System.Threading.Tasks.Task.Factory.StartNew(() =>
    {
        ActionResult result = SubmitSearch(searchData);
        AsyncManager.Parameters["result"] = result;
        AsyncManager.OutstandingOperations.Decrement();
    });

    return;
}

/// <summary>
/// Called when the asynchronous search has completed
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
public ActionResult SubmitSearchCompleted(ActionResult result)
{
    //Just return the action result
    return result;
}

もちろん、これは機能しませんでした。これは、コード全体で を参照HttpContext.Currentしているnullためです。

そのため、次の方法でこれを行うことを望んでいましたSubmitSearchAsync

/// <summary>
/// Asynchronous Search entry point
/// </summary>
/// <param name="searchData"></param>
public void SubmitSearchAsync(SearchFormModel searchData)
{
    AsyncManager.OutstandingOperations.Increment();
    System.Threading.Tasks.Task.Factory.StartNew(() =>
    {
        ActionResult result = null;
        AsyncManager.Sync(() =>
        {
            result = SubmitSearch(searchData);
        });

        AsyncManager.Parameters["result"] = result;
        AsyncManager.OutstandingOperations.Decrement();
    });

    return;
}

これにより、問題が修正されます。

だからここに私の懸念があります:メソッド
の実行をラップすると、SubmitSearchこのAsyncManager.Syncモデルを使用する目的が無効になりますか? つまり、AsyncManager.Syncメソッド内にいるときは、IIS スレッドに戻るので、振り出しに戻るのでしょうか?

ありがとう

4

5 に答える 5

6

SubmitSearchメソッドでの実行をラップすると、AsyncManager.Syncこのモデルを使用する目的が無効になりますか? つまり、AsyncManager.Syncメソッド内にいるときは、IIS スレッドに戻るので、振り出しに戻るのでしょうか?

多かれ少なかれ、はい。しかし残念ながら、あなたの場合、使用すると非同期コントローラーを使用する目的Task.Factory.StartNew も無効になります。あなたが使おうとしているアプローチでは、勝つことはできません。

IIS スレッド、 によって開始されたスレッドThreadPool.QueueUserWorkItem、およびTaskスレッドはすべて、同じスレッド プールから取得されます。

非同期コントローラーの利点を得るには、真の非同期メソッドが必要です。つまり、Stream.ReadAsyncWebRequest.GetResponseAsyncなどのメソッドです。これらの特別な名前のメソッドは、ハードウェア割り込みを使用して別のスレッド プールで動作する通常のスレッドではなく、I/O 完了ポートを使用します。

私はこれについてずっと前に私の答えで書いた: Using ThreadPool.QueueUserWorkItem in ASP.NET in a high traffic scenario . タスクと awaiter は非常に便利ですが、.NET スレッド プールの基本的なダイナミクスを変更するものではありません。

注意すべきことの 1 つは、TaskCreationOptions.LongRunningを作成するときに指定できるオプションがあることですTask。これは基本的に、タスクが多くの待機を行うことをフレームワークに通知し、理論的には TPL はスケジュールを回避しようとします。スレッドプールで。実際には、これはトラフィックの多いサイトではおそらくあまり実用的ではありません。理由は次のとおりです。

  1. フレームワークは、実際にはスレッド プールを使用しないことを保証しません。これは実装の詳細であり、オプションは提供する単なるヒントです。

  2. プールを回避したとしても、スレッドを使用する必要があります。これは、new Thread文字通りではないにしても、少なくとも効果的に使用するのと同じです。これが意味するのは、大量のコンテキスト切り替えであり、パフォーマンスが完全に低下し、そもそもスレッド プールが存在する主な理由です。

「検索」コマンドは明らかにある種の I/O を意味します。これは、古いスタイルの/であっても、おそらくどこかで使用できる実際の非同期メソッドがあることを意味します。ここには近道はなく、簡単な修正もありません。実際に非同期になるようにコードを再構築する必要があります。BeginXyzEndXyz

.NET フレームワークは、内部で何が起こっているかを調べてTask、魔法のように割り込みに変換することができません。I/O 完了ポートを認識している特定のメソッドを直接参照しない限り、I/O 完了ポートを使用することはできません。

次に取り組む Web またはミドルウェア アプリケーションでは、これを事前に検討し、疫病のような同期 I/O を回避するようにしてください。

于 2013-11-09T03:33:16.100 に答える
0

私たちが見ているのは、より多くの人が SubmitSearch をヒットするにつれて、Web サイトは一般的に遅くなるということです。これは、すべての IIS スレッドが検索の実行中にロックされていることが原因である可能性が最も高いです。

スレッドがロックされていた場合、スローダウンにはなりませんが、おそらく http エラーが返されました。速度低下の原因となる並列ヒットの数を尋ねてもよろしいですか? .Net4 のスレッドプールは非常に大きいです。また、検索に 10 秒かかる場合は、データベースが重い負荷をかけていることを意味します。DB のパフォーマンスを確認します。サイトの他の部分も DB に依存している場合、DB を集中的に使用する複数の検索を並行して実行すると、アプリケーションの速度が低下します。

なんらかの理由でデータベースを監視できない/監視したくない場合は、簡単なテストを行います: データベース検索呼び出しを X 秒間スリープ呼び出しに変更します (この場合は約 10 秒)。次に、並行リクエストを実行して、サイトの応答性が低下するかどうかを確認します。リクエストスレッド番号は同じであるため、それが理由である場合、同じ効果があるはずです。

于 2013-11-09T11:31:39.757 に答える
0

サーバーの速度が低下する原因は多数あります。スレッドについてのみ説明すると、スレッドはそれぞれ最低 1/4 のメモリを消費します。つまり、スレッド プールから引き出されるスレッドが多いほど、より多くのメモリが消費されます。これは、サーバーの速度低下を引き起こす問題の 1 つです。

サーバーからの応答に 10 秒以上かかる場合は、非同期の使用を検討してください。あなたのコードのように、SubmitSearchAsync 関数を非同期的に作成すると、スレッドのブロックが回避され、スレッドが解放されてスレッド プールに戻されます。ただし、提供されたコードのように、SubmitSearchAsync アクションからリクエストを受け取ると、スレッド プールからスレッドが引き出され、その本体が実行されます。

SubmitSearch は同期アクションであり、実装が完了するまで待機し、実装が完了するまでスレッドをブロックします。つまり、1 つのスレッドを解放しましたが、別のスレッドもブロックしました。非同期スレッドからコードを同期する必要がある場合は、AsyncManager.Sync メソッドを使用します。しかし、あなたの場合、 AsyncManager.Sync はあまり役に立たないかもしれません。私は2つの可能な解決策を提案します:

1) 手動でスレッドを生成する:

public virtual ActionResult SubmitSearch(SearchFormModel searchData){
    new Thread(() => { 
        //your search code
    }).Start();
}

この場合、検索コードに時間がかかる可能性がありますが、検索の実行はプールの一部ではないスレッドで行われます。

2) 並列処理を使用して SubmitSearch 関数を非同期的に変更します。

protected virtual async Task<ActionResult> SubmitSearch(SearchFormModel searchData){
   // Make your search code using Parallel task like below.
  var task1 = DoingTask1Async(searchData);
  var task2 = DoingTask2Async(searchData)  
  await Task.WhenAll(task1,task2);
}

上記の提案とは別に、キャンセル トークンの使用を検討してください。これにより、スレッドの使用量がさらに削減されます。

それが役に立てば幸い。

于 2013-11-09T02:39:57.190 に答える