2016-07-15 9 views
0

HttpClientを使用してAPIにアクセスするクラスを作成しています。このクラスの特定の関数に対して実行可能な同時呼び出しの数を調整したいと考えています。トリックは、テナントごとに制限があり、複数のテナントがクラスのインスタンスを一度に使用している可能性があります。何らかの変数でHttpClientリクエストを調整する

私のテナントクラスは、読み取り専用コンテキスト情報の単なるコンテナです。ここで

public class Tenant 
{ 
    public string Name { get; } 
    public string ApiKey { get; } 
} 

はApiClientです:

public class ApiClient 
{ 
    private readonly Tenant tenant; 

    public ApiClient(Tenant tenant) 
    { 
     this.tenant = tenant; 
    } 

    public async Task<string> DoSomething() 
    { 
     var response = await this.SendCoreAsync(); 
     return response.ToString(); 
    } 

    private Task<XElement> SendCore() 
    { 
     using (var httpClient = new HttpClient()) 
     { 
      var httpRequest = this.BuildHttpRequest(); 
      var httpResponse = await httpClient.SendAsync(httpRequest); 
      return XElement.Parse(await httpResponse.Content.ReadAsStringAsync()); 
     } 
    } 
} 

私が何をしたいのかSendCore方法を絞るとテナントごとに2つの同時要求に制限されます。私はTPLまたはSemaphoreSlimを使用して基本的な調整(ここでは:Throttling asynchronous tasksなど)を行う提案を読んだが、テナントのさらなる複雑さをどのように追加するかについては明確ではない。

ありがとうございます。

UPDATE

私はConcurrentDictionaryに含まれるSemaphoreSlimオブジェクト(テナントごとに1)のセットを使用しようとしました。これはうまくいくようですが、これが理想的かどうかはわかりません。新しいコードは次のとおりです。

public class ApiClient 
{ 
    private static readonly ConcurrentDictionary<string, SemaphoreSlim> Semaphores = new ConcurrentDictionary<string, SemaphoreSlim>(); 
    private readonly Tenant tenant; 
    private readonly SemaphoreSlim semaphore; 

    public ApiClient(Tenant tenant) 
    { 
     this.tenant = tenant; 
     this.semaphore = Semaphores.GetOrAdd(this.tenant.Name, k => new SemaphoreSlim(2)); 
    } 

    public async Task<string> DoSomething() 
    { 
     var response = await this.SendCoreAsync); 
     return response.ToString(); 
    } 

    private Task<XElement> SendCore() 
    { 
     await this.semaphore.WaitAsync(); 
     try 
     { 
      using (var httpClient = new HttpClient()) 
      { 
       var httpRequest = this.BuildHttpRequest(); 
       var httpResponse = await httpClient.SendAsync(httpRequest); 
       return XElement.Parse(await httpResponse.Content.ReadAsStringAsync()); 
      } 
     } 
     finally 
     { 
      this.semaphore.Release(); 
     } 
    } 
} 
+0

「テナント」とは何ですか?それはいくつかのライブラリ(どちらか?)かあなたのカスタムクラスのクラスですか?この目的のためだけに変更するのは理にかなっていますか?また、「テナント」は複数の「ApiClient」を持つことができますか? – svick

+0

テナントはテナントの名前と、username/passwordのようなAPIクライアントによって使用されるいくつかの接続情報を含む小さなコンテキストクラスです。私はASP.NET WebJobs SDKを使用しており、テナントは単にサービスバスメッセージを処理するときに挿入されます。テナントオブジェクトは読み取り専用と考えることができます。 –

+0

私は思いついたセマフォのアプローチで質問を更新しました。 –

答えて

0

SemaphoreSlimのアプローチは、私にはほとんど妥当と思われます。

潜在的な問題の1つは、Tenantがアプリケーションのライフタイムを往復することができる場合、もはや存在しないTenantがセマフォを保持していることです。

ConcurrentDictionaryの代わりにConditionalWeakTable<Tenant, SemaphoreSlim>を使用すると、そのキーがガベージコレクトされることが確認され、そのときに値が解放されます。

+0

ありがとうございます。テナントはシステムからほとんど削除されません。サービスが再起動される頻度がずっと少なくなるので、私は安全だと思います。 –

関連する問題