.NET での gRPC クライアント ファクトリの統合

作成者: James Newton-King

gRPC と HttpClientFactory の統合により、gRPC クライアントを一元的に作成する方法が提供されています。 これは、スタンドアロンの gRPC クライアント インスタンスを構成するための代替手段として使用できます。 ファクトリの統合は、Grpc.Net.ClientFactory NuGet パッケージで提供されています。

ファクトリには以下のような利点があります。

  • 論理 gRPC クライアント インスタンスの構成を一元管理する場所となります。
  • 基になる HttpClientMessageHandler の存続期間を管理します。
  • ASP.NET Core gRPC サービスで期限とキャンセルを自動伝達します。

gRPC クライアントを登録する

gRPC クライアントを登録するには、ジェネリック AddGrpcClient 拡張メソッドを Program.cs のアプリのエントリ ポイントにある WebApplicationBuilder のインスタンス内で使用して、gRPC の型指定されたクライアント クラスとサービス アドレスを指定することができます。

builder.Services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
    o.Address = new Uri("https://localhost:5001");
});

GRPC クライアントの種類は、依存関係の挿入 (DI) がある一時的なものとして登録されます。 これで、DI によって作成される種類でクライアントを直接挿入し、使用できるようになります。 ASP.NET Core MVC コントローラー、SignalR ハブ、および gRPC サービスは、gRPC クライアントを自動的に挿入できる場所です。

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(Greeter.GreeterClient client)
    {
        _client = client;
    }

    public override async Task SayHellos(HelloRequest request,
        IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
    {
        // Forward the call on to the greeter service
        using (var call = _client.SayHellos(request))
        {
            await foreach (var response in call.ResponseStream.ReadAllAsync())
            {
                await responseStream.WriteAsync(response);
            }
        }
    }
}

HttpHandler を構成する

HttpClientFactory では、gRPC クライアントによって使用される HttpMessageHandler が作成されます。 標準の HttpClientFactory メソッドを使用して、送信要求ミドルウェアを追加したり、HttpClient の基になる HttpClientHandler を構成したりすることができます。

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(LoadCertificate());
        return handler;
    });

詳細については、IHttpClientFactory を使用した HTTP 要求の作成に関するページを参照してください。

インターセプターを構成する

gRPC インターセプターは AddInterceptor メソッドを使ってクライアントに追加できます。

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>();

上記のコードでは次の操作が行われます。

  • GreeterClient 型を登録します。
  • このクライアント用に LoggingInterceptor を構成します。 LoggingInterceptor は 1 回作成され、GreeterClient インスタンス間で共有されます。

既定では、インターセプターは 1 回作成され、クライアント間で共有されます。 この動作は、インターセプターの登録時にスコープを指定することでオーバーライドできます。 InterceptorScope.Client を指定すると、クライアントごとに新しいインターセプターを作成するようにクライアント ファクトリを構成できます。

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>(InterceptorScope.Client);

クライアント スコープのインターセプターを作成することは、DI のスコープ付きサービスまたは一時的なスコープ付きサービスがインターセプターに必要な場合に便利です。

gRPC インターセプターまたはチャネル資格情報は、各要求で Authorization メタデータを送信するために使用できます。 認証の構成の詳細については、gRPC クライアント ファクトリでのベアラー トークンの送信に関する記事を参照してください。

チャネルを構成する

ConfigureChannel メソッドを使うと、チャネルに追加の構成を適用することができます。

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

ConfigureChannelGrpcChannelOptions インスタンスを渡されます。 詳細については、クライアント オプションの構成に関するページを参照してください。

Note

GrpcChannelOptions 上の一部のプロパティは、ConfigureChannel コールバックが実行される前に設定されます。

これらの値は ConfigureChannel でオーバーライドできます。

資格情報を呼び出す

認証ヘッダーは、AddCallCredentials メソッドを使用して、gRPC 呼び出しに追加できます。

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
        if (!string.IsNullOrEmpty(_token))
        {
            metadata.Add("Authorization", $"Bearer {_token}");
        }
        return Task.CompletedTask;
    });

呼び出しの資格情報の構成に関する詳細については、gRPC クライアント ファクトリでのベアラー トークンの送信に関する記事を参照してください。

期限とキャンセルの伝達

gRPC サービスでファクトリによって作成された gRPC クライアントは、EnableCallContextPropagation() を使用して、期限とキャンセル トークンが子の呼び出しに自動伝達されるように構成できます。 EnableCallContextPropagation() 拡張メソッドは Grpc.AspNetCore.Server.ClientFactory NuGet パッケージで提供されています。

呼び出しコンテキストの伝達は、現在の gRPC 要求コンテキストから期限とキャンセル トークンを読み取り、それらを、gRPC クライアントによって行われた送信呼び出しに自動的に伝達することによって機能します。 呼び出しコンテキストの伝達は、複雑で入れ子になった gRPC のシナリオで、常に期限とキャンセルが確実に伝達されるようにする優れた方法です。

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation();

既定では、クライアントが gRPC 呼び出しのコンテキスト外で使用されると、EnableCallContextPropagation によってエラーが発生します。 このエラーは、伝達する呼び出しコンテキストが存在しないことを警告するために設計されています。 クライアントを呼び出しコンテキストの外部で使用する場合は、クライアントが SuppressContextNotFoundErrors で構成されていると、エラーが抑制されます。

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation(o => o.SuppressContextNotFoundErrors = true);

期限と RPC のキャンセルの詳細については、「期限とキャンセルを使用した信頼性の高い gRPC サービス」を参照してください。

名前付きクライアント

通常、gRPC クライアントの種類は一度登録され、DI によって型のコンストラクターに直接挿入されます。 ただし、1 つのクライアントに複数の構成を設定すると便利な場合があります。 たとえば、認証を使用して、または認証を使用せずに gRPC 呼び出しを行うクライアントなどです。

各クライアントに名前を付けることで、同じ種類の複数のクライアントを登録できます。 名前付きの各クライアントには、独自の構成を含めることができます。 ジェネリック AddGrpcClient 拡張メソッドには、name パラメーターを含むオーバーロードがあります。

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>("Greeter", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    });

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>("GreeterAuthenticated", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

上記のコードでは次の操作が行われます。

  • GreeterClient 型を 2 回登録し、それぞれに一意の名前を指定します。
  • 名前付きクライアントごとに異なる設定を構成します。 GreeterAuthenticated 登録によって資格情報がチャネルに追加され、それを使用して実行された gRPC 呼び出しが認証されるようになります。

名前付き gRPC クライアントは、GrpcClientFactory を使用してアプリ コードで作成されます。 目的のクライアントの型と名前は、ジェネリック GrpcClientFactory.CreateClient メソッドを使用して指定されます。

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(GrpcClientFactory grpcClientFactory)
    {
        _client = grpcClientFactory.CreateClient<Greeter.GreeterClient>("GreeterAuthenticated");
    }
}

その他のリソース

gRPC と HttpClientFactory の統合により、gRPC クライアントを一元的に作成する方法が提供されています。 これは、スタンドアロンの gRPC クライアント インスタンスを構成するための代替手段として使用できます。 ファクトリの統合は、Grpc.Net.ClientFactory NuGet パッケージで提供されています。

ファクトリには以下のような利点があります。

  • 論理 gRPC クライアント インスタンスの構成を一元管理する場所となります
  • 基になる HttpClientMessageHandler の存続期間を管理します
  • ASP.NET Core gRPC サービスで期限とキャンセルを自動伝達

gRPC クライアントを登録する

gRPC クライアントを登録するには、ジェネリック AddGrpcClient 拡張メソッドを Startup.ConfigureServices 内で使用して、gRPC の型指定されたクライアント クラスとサービス アドレスを指定します。

services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
    o.Address = new Uri("https://localhost:5001");
});

GRPC クライアントの種類は、依存関係の挿入 (DI) がある一時的なものとして登録されます。 これで、DI によって作成される種類でクライアントを直接挿入し、使用できるようになります。 ASP.NET Core MVC コントローラー、SignalR ハブ、および gRPC サービスは、gRPC クライアントを自動的に挿入できる場所です。

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(Greeter.GreeterClient client)
    {
        _client = client;
    }

    public override async Task SayHellos(HelloRequest request,
        IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
    {
        // Forward the call on to the greeter service
        using (var call = _client.SayHellos(request))
        {
            await foreach (var response in call.ResponseStream.ReadAllAsync())
            {
                await responseStream.WriteAsync(response);
            }
        }
    }
}

HttpHandler を構成する

HttpClientFactory では、gRPC クライアントによって使用される HttpMessageHandler が作成されます。 標準の HttpClientFactory メソッドを使用して、送信要求ミドルウェアを追加したり、HttpClient の基になる HttpClientHandler を構成したりすることができます。

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(LoadCertificate());
        return handler;
    });

詳細については、IHttpClientFactory を使用した HTTP 要求の作成に関するページを参照してください。

インターセプターを構成する

gRPC インターセプターは AddInterceptor メソッドを使ってクライアントに追加できます。

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>();

上記のコードでは次の操作が行われます。

  • GreeterClient 型を登録します。
  • このクライアント用に LoggingInterceptor を構成します。 LoggingInterceptor は 1 回作成され、GreeterClient インスタンス間で共有されます。

既定では、インターセプターは 1 回作成され、クライアント間で共有されます。 この動作は、インターセプターの登録時にスコープを指定することでオーバーライドできます。 InterceptorScope.Client を指定すると、クライアントごとに新しいインターセプターを作成するようにクライアント ファクトリを構成できます。

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor<LoggingInterceptor>(InterceptorScope.Client);

クライアント スコープのインターセプターを作成することは、DI のスコープ付きサービスまたは一時的なスコープ付きサービスがインターセプターに必要な場合に便利です。

gRPC インターセプターまたはチャネル資格情報は、各要求で Authorization メタデータを送信するために使用できます。 認証の構成の詳細については、gRPC クライアント ファクトリでのベアラー トークンの送信に関する記事を参照してください。

チャネルを構成する

ConfigureChannel メソッドを使うと、チャネルに追加の構成を適用することができます。

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

ConfigureChannelGrpcChannelOptions インスタンスを渡されます。 詳細については、クライアント オプションの構成に関するページを参照してください。

Note

GrpcChannelOptions 上の一部のプロパティは、ConfigureChannel コールバックが実行される前に設定されます。

これらの値は ConfigureChannel でオーバーライドできます。

期限とキャンセルの伝達

gRPC サービスでファクトリによって作成された gRPC クライアントは、EnableCallContextPropagation() を使用して、期限とキャンセル トークンが子の呼び出しに自動伝達されるように構成できます。 EnableCallContextPropagation() 拡張メソッドは Grpc.AspNetCore.Server.ClientFactory NuGet パッケージで提供されています。

呼び出しコンテキストの伝達は、現在の gRPC 要求コンテキストから期限とキャンセル トークンを読み取り、それらを、gRPC クライアントによって行われた送信呼び出しに自動的に伝達することによって機能します。 呼び出しコンテキストの伝達は、複雑で入れ子になった gRPC のシナリオで、常に期限とキャンセルが確実に伝達されるようにする優れた方法です。

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation();

既定では、クライアントが gRPC 呼び出しのコンテキスト外で使用されると、EnableCallContextPropagation によってエラーが発生します。 このエラーは、伝達する呼び出しコンテキストが存在しないことを警告するために設計されています。 クライアントを呼び出しコンテキストの外部で使用する場合は、クライアントが SuppressContextNotFoundErrors で構成されていると、エラーが抑制されます。

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation(o => o.SuppressContextNotFoundErrors = true);

期限と RPC のキャンセルの詳細については、「期限とキャンセルを使用した信頼性の高い gRPC サービス」を参照してください。

名前付きクライアント

通常、gRPC クライアントの種類は一度登録され、DI によって型のコンストラクターに直接挿入されます。 ただし、1 つのクライアントに複数の構成を設定すると便利な場合があります。 たとえば、認証を使用して、または認証を使用せずに gRPC 呼び出しを行うクライアントなどです。

各クライアントに名前を付けることで、同じ種類の複数のクライアントを登録できます。 名前付きの各クライアントには、独自の構成を含めることができます。 ジェネリック AddGrpcClient 拡張メソッドには、name パラメーターを含むオーバーロードがあります。

services
    .AddGrpcClient<Greeter.GreeterClient>("Greeter", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    });

services
    .AddGrpcClient<Greeter.GreeterClient>("GreeterAuthenticated", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

上記のコードでは次の操作が行われます。

  • GreeterClient 型を 2 回登録し、それぞれに一意の名前を指定します。
  • 名前付きクライアントごとに異なる設定を構成します。 GreeterAuthenticated 登録によって資格情報がチャネルに追加され、それを使用して実行された gRPC 呼び出しが認証されるようになります。

名前付き gRPC クライアントは、GrpcClientFactory を使用してアプリ コードで作成されます。 目的のクライアントの型と名前は、ジェネリック GrpcClientFactory.CreateClient メソッドを使用して指定されます。

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(GrpcClientFactory grpcClientFactory)
    {
        _client = grpcClientFactory.CreateClient<Greeter.GreeterClient>("GreeterAuthenticated");
    }
}

その他のリソース