複数の長時間実行オペレーションによるASMX Webサービスの最適化
-
05-07-2019 - |
質問
DoLookup()関数を持つC#を使用してASP.NET Webサービスを作成しています。 DoLookup()関数の呼び出しごとに、リモートサイトの別のWebサービスとローカルデータベースの2つのクエリを実行するコードが必要です。結果をコンパイルし、DoLookupメソッドへの応答として返す前に、両方のクエリを完了する必要があります。私が対処している問題は、応答時間とWebサーバー上のリソース使用量の両方の面で、これを可能な限り効率的にしたいということです。 1時間あたり最大数千のクエリが予想されます。ここに私がこれまでに持っているものの大まかなC#のような概要があります:
public class SomeService : System.Web.Services.WebService
{
public SomeResponse DoLookup()
{
// Do the lookup at the remote web service and get the response
WebResponse wr = RemoteProvider.DoRemoteLookup();
// Do the lookup at the local database and get the response
DBResponse dbr = DoDatabaseLookup();
SomeResponse resp = new SomeResponse( wr, dbr);
return resp;
}
}
上記のコードはすべてを順番に実行し、優れた動作をしますが、今ではよりスケーラブルにしたいです。 DoRemoteLookup()関数を非同期的に呼び出すことができること(RemoteProviderにはBeginRemoteLookup / EndRemoteLookupメソッドがあります)、およびBeginExecuteNonQuery / EndExecuteNonQueryメソッドを使用してデータベースルックアップを非同期的に実行できることを知っています。
(最終的に)私の質問は次のとおりです。リモートWebサービスルックアップとデータベースルックアップの両方を別々のスレッドで同時に起動し、応答を返す前に両方が完了したことを確認するにはどうすればよいですか?
両方のリクエストを別々のスレッドで実行したい理由は、両方とも潜在的に長い応答時間(1または2秒)があり、Webサーバーのリソースを解放して他のリクエストを処理したいからです応答を待っています。もう1つの注意点-リモートWebサービスのルックアップを現在非同期で実行しているので、上記のサンプルをあまりにも混乱させたくありません。私が苦労しているのは、リモートサービスルックアップとデータベースルックアップの両方を同時に開始し、両方が完了した時点を把握することです。
ご提案ありがとうございます。
解決
スレッドごとに1組の AutoResetEvents
を使用できます。スレッド実行の最後に、 AutoResetEvents.Set()
を呼び出してイベントをトリガーします。
スレッドを生成した後、2つのAutoResetEventsで WaitAll()
を使用します。これにより、両方のイベントが設定されるまでスレッドがブロックされます。
このアプローチの注意点は、Set()が呼び出されることを保証する必要があることです。そうしないと、永久にブロックされます。さらに、スレッドを使用して適切な例外処理を実行するようにしてください。そうしないと、未処理の例外によってWebアプリケーションが再起動するときに、不注意でパフォーマンスの問題が発生します。
他のヒント
非同期XML Webサービスメソッド、方法:非同期Webサービスメソッドを作成および方法:Webサービスメソッドを使用して非同期呼び出しをチェーンする。
ただし、これらの記事の最初の段落に注意してください:
このトピックは、レガシーテクノロジーに固有のものです。 XML WebサービスとXML Webサービスクライアントは、 Windows Communication Foundation(WCF)を使用して作成する必要があります。 a>。
ところで、長時間実行されるタスクの実行中にASP.NETワーカースレッドが解放されるため、これらの記事のとおりに物事を行うことが重要です。そうしないと、ワーカースレッドをブロックして、それ以上のリクエストを処理できなくなり、スケーラビリティに影響を与える可能性があります。
Webリクエストとデータベースルックアップの両方にコールバック関数を使用できると仮定すると、これらの行に沿って何かが機能する可能性があります
bool webLookupDone = false;
bool databaseLookupDone = false;
private void FinishedDBLookupCallBack()
{
databaseLookupDone = true;
if(webLookupDone)
{
FinishMethod();
}
}
private void FinishedWebLookupCallBack()
{
webLookupDone = true;
if(databaseLookupDone)
{
FinishMethod();
}
}
投票したりコメントしたりするのに十分な担当者がいないと思います。したがって、これはジョン・サンダースの回答に対するコメントと、それに対するアランのコメントです。
スケーラビリティとリソース消費が心配な場合は、Johnの回答を必ず使用してください。
ここでは2つの考慮事項があります。個々のリクエストを高速化することと、システムが多くの同時リクエストを効率的に処理することです。前者のアランとジョンの両方の答えは、外部呼び出しを並行して実行することによって達成されます。
後者は、それがあなたの主な関心事であるように聞こえますが、スレッドをどこにもブロックしないことによって達成されます、つまりジョンの答え。
独自のスレッドを生成しないでください。スレッドは高価であり、.NETフレームワークが提供する非同期メソッドを使用する場合、外部呼び出しを処理するスレッドがIOスレッドプールに既にたくさんあります。
サービスのwebmethodも非同期である必要があります。そうしないと、外部呼び出しが完了するまでワーカースレッドがブロックされます(それらが並行して実行されても、まだ1〜2秒です)。また、着信リクエストを処理するCPUあたり12スレッドしかありません(machine.configが推奨)。つまり最大で12の同時要求(CPUの数の倍数)を処理できます。一方、Webメソッドが非同期の場合、Beginはほとんど瞬時に戻り、スレッドは別の着信要求を処理する準備ができているワーカースレッドプールに戻りますが、外部呼び出しはIO完了ポートによって待機されます。 IOスレッドプールから返されるスレッドによって処理されます。