web-dev-qa-db-ja.com

.NET Core Consoleアプリケーションの複数のHttpClient

REST APIの.NET Coreクラスライブラリラッパーを構築しています。これは、理想的には、コンソールアプリケーションとASP.NET Core Webアプリケーションの両方で使用できます。これまでのところ、REST APIメソッドのグループごとに typed client を作成することにより、後者の依存性注入をサポートする開発に基づいています。つまり、(Index、Create 、破棄など)。この場合、各グループのベースアドレスは同じであり、ほとんどの場合、同じ認証ヘッダーが使用されることに注意することが重要です。

型指定されたクライアントは、一部の構成を処理する基本クライアントから継承します。

public abstract class BaseClient
{
    protected readonly HttpClient _client;

    public BaseClient(IConfigService config, HttpClient client)
    {
        _client = client;
        _client.BaseAddress = new Uri(config.BaseAddress);

       // More configuration
    }
}

だから私はこのようなものになってしまいます:

public class ClientA : BaseClient, IClientA
{
    public ClientA(IConfigService config, HttpClient client) : base(config, client) { }

    // Some methods
}

public class ClientB : BaseClient, IClientB
{
    public ClientB(IConfigService config, HttpClient client) : base(config, client) { }

    // More methods
}

等々。これで、これらすべてのサービスを登録するIServiceCollection拡張を作成しました:

public static class WrapperServiceCollectionExtensions
{
    public static IServiceCollection AddWrapper(this IServiceCollection services, string username, string key)
    {
        services.AddSingleton<IConfigService>(new ConfigService(username, key));
        services.AddHttpClient<IClientA, ClientA>();
        services.AddHttpClient<IClientB, ClientB>();

        // More clients

        return services;
    }
}

私が理解しているように、このDIパターンを使用すると、新しいHttpClientが型付きクライアントごとにインスタンス化されるため、型付きクライアントが独自の構成を個別に担当していることには問題がありません。しかし、これらのクライアントをコンソールアプリケーションで作成したいとします。最後に、私は私の質問に行きます:クライアントはすべて同じベースアドレスを持ち、すべてが承認に同じ資格情報を使用する可能性が高いことを知っている場合、コンソールアプリケーションでそれらをインスタンス化する推奨される方法は何でしょうか?入力したクライアントごとに新しいHttpClientを作成する傾向があります。

class Program
{
    private static HttpClient _clientA = new HttpClient();
    private static HttpClient _clientB = new HttpClient();

    static void Main(string[] args)
    {
        var config = new ConfigService("user", "key");
        var a = new ClientA(config, _clientA);
        var b = new ClientB(config, _clientB);
    }
}

しかし、両方の構成が同じである場合、これは本当に必要ですか?複数の構成を扱っていない限り、1つのHttpClientだけを心配すればよいですか構成がそれぞれ同じである場合、複数の型付きクライアントを使用する必要がありますか?私には、型指定された各クライアントがコンストラクターで単一のHttpClientの構成を連続して上書きできるようにするのは少しハックに感じますが、動作に関しては、別の資格情報が使用されない限り、それは重要ではないと思います。ここで本気で立ち往生。

2

まず、これは良い質問です。 :)

あなたが述べた2つのアプローチを比較することで、あなたを助けようと思います。

APIのグループごとに1つの型付きクライアント

長所

  • これはより柔軟なアプローチです
    • 必要に応じて、後で別の資格情報を適用できます
    • 異なるドメインに対して異なるタイムアウトを定義できます
    • さまざまな回復力のある戦略を使用できます(たとえば、べき等演算の場合、再試行ロジックを導入できます)
  • 単一のインスタンスに障害が発生しても、他のインスタンスには影響しません
    • 個別の監視を導入することができます(および回路ブレーカーを使用してダウンストリームシステムのフラッディングを回避します)
  • 消費者はどちらを使用するかを決定できます
    • コンシューマーは、ドメインベースで(たとえば、バルクヘッドを使用して)発信要求を抑制できます。

短所

  • ほぼ同じAPIグループが10個以上ある場合、手動で実行する必要がある場合、すべてのクライアントの管理が困難になる可能性があります。
  • ほぼ同じ設定ですべてのクライアントを構成することは、面倒でエラーが発生しやすい作業になる可能性があります

すべてのグループの単一の共有クライアント

長所

  • 簡素化された構成
  • よりシンプルな資格情報管理
  • コードは理解しやすいかもしれません(読みやすくすることができます)

短所

  • httphttpsの呼び出しを混在させると、広範囲にわたるハンドシェイクによりオーバーヘッドが発生する可能性があります
    • 間違ったTLSバージョンを使用すると、IOExceptionになる可能性があります
    • 接続プールを使い果たすと、SocketExceptionになる可能性があります(接続オープン要求が多すぎる)。
  • 保留中の要求が1つ以上ある場合、クライアントのBaseAddressを変更するとInvalidOperationExceptionが発生する可能性があります。
    • TimeoutMaxResponseContentBufferSizeなどの他のグローバル設定についても同様です。
  • CancelPendingRequestsメソッドの呼び出しには時間がかかる場合があります

私のポイントは、それぞれのアプローチには独自の長所とトレードオフがあるということです。機能要件と非機能要件に基づいて、どちらを使用するかを決定するのはあなた次第です。

1
Peter Csala