2017-02-20 8 views
1

私は毎秒500ユーザ(それらのすべてが同時にAPIを呼び出す)C#同時の複数のタイムアウト

の同時実行をサポートするパラレル 4 HttpClientsに呼び出す必要がありますAPIを持って呼び出します

すべてのHttpClients呼び出しが値を返さなかった場合でも、結果を返すように厳密なタイムアウトが必要です。
エンドポイントは外部のサードパーティ製のAPIであり、私はそれらのコントロールやコードの知識は持っていません。
私はこの問題について多大な研究をしましたが、多くのソリューションが動作しても、サーバーの予算が低いので、できるだけCPUを消費しないものが必要です。

は、これまでのところ、私はこの思い付いた:

var conn0 = new HttpClient 
{ 
    Timeout = TimeSpan.FromMilliseconds(1000), 
    BaseAddress = new Uri("http://endpoint") 
}; 
var conn1 = new HttpClient 
{ 
    Timeout = TimeSpan.FromMilliseconds(1000), 
    BaseAddress = new Uri("http://endpoint") 
}; 
var conn2 = new HttpClient 
{ 
    Timeout = TimeSpan.FromMilliseconds(1000), 
    BaseAddress = new Uri("http://endpoint") 
}; 
var conn3 = new HttpClient 
{ 
    Timeout = TimeSpan.FromMilliseconds(1000), 
    BaseAddress = new Uri("http://endpoint") 
}; 

var list = new List<HttpClient>() { conn0, conn1, conn2, conn3 }; 
var timeout = TimeSpan.FromMilliseconds(1000); 
var allTasks = new List<Task<Task>>(); 

//the async DoCall method just call the HttpClient endpoint and return a MyResponse object 
foreach (var call in list) 
{ 
    allTasks.Add(Task.WhenAny(DoCall(call), Task.Delay(timeout))); 
} 
var completedTasks = await Task.WhenAll(allTasks); 
var allResults = completedTasks.OfType<Task<MyResponse>>().Select(task => task.Result).ToList(); 
return allResults; 

私は呼び出しタスクが遅れている、もう一つはとにかく返しWhenAnyと二つのタスク、呼び出しのための1つ、timeout.Ifのための1つを使用します。

ここで、このコードは完全に機能し、すべてが非同期ですが、これを実現するより良い方法があるのだろうかと思います。
このAPIを一回呼び出すとスレッドが大量に生成され、500人の同時ユーザーの場合は、8(8)個のD3_V2 Azure 4コアマシンを使用する必要があります。その結果、CPU使用率が高くなります。

これほど多くのCPUリソースを使用せずに、これを行うより良い方法がありますか(おそらくParallel Linqはこれよりも良い選択です)。

HttpClientのタイムアウトだけでも、コールを停止し、エンドポイントが応答しない場合は、WhenAnyの2番目のタスクを使用しなくても十分ですか?

UPDATE:

  • のエンドポイントがあるサードパーティのAPIは、私がコードを知っているか、または任意のコントロールを持って、通話がJSONで行われ、JSONまたは文字列を返しません。
  • 10 +秒後に返答するものや、滞留して非常に遅いものがあるため、タイムアウトはスレッドを解放して、時間内に返された部分データからの部分データであっても返されます。
  • キャッシングは可能ですが、株式や外国為替リアルタイム通貨取引のように常にデータが変更されているため部分的にしかキャッシュできません。
  • だけタイムアウトのための2つのタスクを使用して
+0

あなたはより多くを提供することができますあなたが呼んでいるエンドポイントのタイプと性質に関する情報は、ユースケースで実行可能な結果の一部をキャッシュしています。なぜ、時間切れになっているのですか(それはコスト駆動型だと思われますが、他の理由もあります)エンドポイントのコードとインフラストラクチャへのアクセス、またはサードパーティのAPIを呼び出していますか? – univ

+0

質問にいくつかの情報を追加しました。私は、現在実装されている方法が間違っているかどうかを知りたいと思っています。それ以外の場合は、並列/並行処理ソリューションの多様性のために可能ですか。 –

答えて

2

あなたのアプローチは、仕事をするが、あなたはより良いことを行うことができます:use CancellationToken for the task、およびサーバーからの回答を得るための:その後

var cts = new CancellationTokenSource(); 
// set the timeout equal to the 1 second 
cts.CancelAfter(1000); 
// provide the token for your request 
var response = await client.GetAsync(url, cts.Token); 

を、あなたは、単にすることができますcompletedタスクをフィルタリング:

var allResults = completedTasks 
    .Where(t => t.IsCompleted) 
    .Select(task => task.Result).ToList(); 

このアプローチは、あなたが何も2倍未満を作成していないしているタスクの数を減少し、サーバー上のオーバーヘッドが減少します。また、処理の一部または全体を取り消す簡単な方法を提供します。あなたのタスクは互いに完全に独立している場合は、HTTPクライアントを呼び出すためParallel.Forを使用することができ、それでもtoken for cancelling the operationをusee:

ParallelLoopResult result = Parallel.For(list, call => DoCall(call, cts.Token)); 
// handle the result of the parallel tasks 

や、PLINQを使用して:

var results = list 
    .AsParallel() 
    .Select(call => DoCall(call, cts.Token)) 
    .ToList(); 
+0

私はcancellationtokenアプローチを試しましたが、常にHttpclientから尊重されています。私はいくつかの研究を行なったし、この問題にほとんどすべての時間を持つことになった: http://stackoverflow.com/questions/29319086/cancelling-an-httpclient-request-why-is-taskcanceledexception-cancellationtoke HTTPClientを作成します新しいcancellationTokenを使用して、10秒間かかると思われる処理を完了しようとすると、この時間内にすべてのAPIをブロックします。これが私が厳密な2つのタスクの解決策をとった理由ですが、それはあまりにも計算集中的です。 –

+0

PLINQのアプローチを試してみて、アップデートを投稿していただきますようお願い申し上げます。 –

関連する問題