0Pricing
C# Academy · レッスン

デッドライン、キャンセルとインターセプター

デッドラインでタイムアウトを適用し、CancellationTokenを伝播させ、gRPCインターセプターで横断的関心事を追加します。

「デッドライン、キャンセルとインターセプター」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。

gRPC の信頼性: デッドラインとキャンセル

本番環境の gRPC サービスでは、2 つの重要な信頼性上の問題に対処する必要があります。デッドライン(クライアントが待機する時間)とキャンセル(処理を早期に停止すること)です。これらを組み合わせることで、リソースの枯渇や障害の連鎖を防げます。

呼び出しにデッドラインを設定する

デッドラインとは、呼び出しを完了しなければならない絶対時刻です。CallOptions を使用して設定します。期限を超えると、サーバーはキャンセルを受け取り、クライアントは StatusCode.DeadlineExceeded を受け取ります。

var deadline = DateTime.UtcNow.AddSeconds(5);

var reply = await client.GetOrderAsync(
    new GetOrderRequest { Id = 42 },
    deadline: deadline);

// Or via CallOptions:
var options = new CallOptions(deadline: deadline);
var reply2 = await client.GetOrderAsync(
    new GetOrderRequest { Id = 42 }, options);

クライアントで DeadlineExceeded を処理する

RpcException をキャッチし、ステータスコードを確認して、デッドラインの超過とその他のエラーを区別します。

try
{
    var reply = await client.GetOrderAsync(
        new GetOrderRequest { Id = 42 },
        deadline: DateTime.UtcNow.AddSeconds(3));
}
catch (RpcException ex) when (ex.StatusCode == StatusCode.DeadlineExceeded)
{
    Console.WriteLine("Call timed out");
}
catch (RpcException ex)
{
    Console.WriteLine($"gRPC error: {ex.Status.Detail}");
}

サーバーでデッドラインを尊重する

サーバーでは、時間のかかる処理中に context.CancellationToken を確認する必要があります。デッドラインを過ぎると、このトークンは自動的にキャンセルされます。

public override async Task<ReportResponse> GenerateReport(
    ReportRequest request,
    ServerCallContext context)
{
    var ct = context.CancellationToken;

    // Pass ct to every async call — work stops when deadline hits
    var data = await _db.FetchLargeDatasetAsync(request.Filters, ct);
    var report = await _reportEngine.BuildAsync(data, ct);

    ct.ThrowIfCancellationRequested(); // explicit check before CPU work
    return MapToResponse(report);
}

CancellationToken の伝播

サーバーのキャンセル トークンは、すべての非同期呼び出しチェーンを通じて必ず伝播してください。無視すると、キャンセルされた呼び出しが不必要にリソースを消費し続けます。

// GOOD: token flows through the call chain
await _db.Products.AsNoTracking()
    .Where(p => p.IsActive)
    .ToListAsync(context.CancellationToken); // cancels if client disconnects

// BAD: ignores cancellation
await _db.Products.ToListAsync(); // keeps running after deadline

gRPC インターセプター: 概念

インターセプターは ASP.NET Core のミドルウェアに相当する gRPC の仕組みです。RPC 呼び出しの前後で実行され、リクエスト、レスポンス、例外を検査または変更できます。ログ記録、認証、再試行ロジックに適しています。

サーバー側インターセプターを作成する

Interceptor を継承し、インターセプトするメソッドの種類をオーバーライドします。実際のハンドラーに処理を進めるには continuation を呼び出します。

using Grpc.Core.Interceptors;

public class LoggingInterceptor : Interceptor
{
    private readonly ILogger<LoggingInterceptor> _logger;
    public LoggingInterceptor(ILogger<LoggingInterceptor> l) => _logger = l;

    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        _logger.LogInformation("gRPC call: {Method}", context.Method);
        var sw = System.Diagnostics.Stopwatch.StartNew();
        var response = await continuation(request, context);
        _logger.LogInformation("Completed in {Ms}ms", sw.ElapsedMilliseconds);
        return response;
    }
}

サーバーインターセプターを登録する

Program.cs でインターセプターをグローバル(すべてのサービス対象)またはサービス単位で登録します。DI コンテナーがコンストラクターの依存関係を解決します。

// Globally for all gRPC services:
builder.Services.AddGrpc(opt =>
    opt.Interceptors.Add<LoggingInterceptor>());

// Or per service:
builder.Services.AddGrpc();
builder.Services.AddSingleton<LoggingInterceptor>();

app.MapGrpcService<OrderService>()
   .AddInterceptor<LoggingInterceptor>();

クライアント側インターセプター

クライアントインターセプターは送信する呼び出しをラップします。各呼び出し元を変更することなく、認証ヘッダー、再試行ロジック、相関 ID を追加できるため便利です。

public class AuthInterceptor : Interceptor
{
    private readonly ITokenProvider _tokens;
    public AuthInterceptor(ITokenProvider t) => _tokens = t;

    public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
        TRequest request,
        ClientInterceptorContext<TRequest, TResponse> context,
        AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
    {
        var headers = context.Options.Headers ?? new Metadata();
        headers.Add("authorization", $"Bearer {_tokens.GetToken()}");
        var newContext = new ClientInterceptorContext<TRequest, TResponse>(
            context.Method, context.Host,
            context.Options.WithHeaders(headers));
        return continuation(request, newContext);
    }
}

// Attach to channel:
var invoker = channel.Intercept(new AuthInterceptor(tokenProvider));
var client = new OrderService.OrderServiceClient(invoker);

例外インターセプター

サーバー側の例外インターセプターは、処理されていない例外を RpcExceptions に変換し、内部の詳細がクライアントに漏洩するのを防ぎます。

public class ExceptionInterceptor : Interceptor
{
    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        try
        {
            return await continuation(request, context);
        }
        catch (NotFoundException ex)
        {
            throw new RpcException(new Status(StatusCode.NotFound, ex.Message));
        }
        catch (Exception ex)
        {
            throw new RpcException(new Status(StatusCode.Internal, "Internal error"));
        }
    }
}

確認問題

gRPC 呼び出しがデッドラインを超えると何が起こりますか?

まとめ: デッドライン、キャンセル、インターセプター

重要なポイント:

  • 待機時間に上限がなくなるのを防ぐため、クライアントの呼び出しには必ずデッドラインを設定する
  • サーバーで context.CancellationToken を確認し、すべての非同期呼び出しに渡す
  • インターセプターを使用すると、サービスコードを複雑にせずに横断的関心事(ログ記録、認証、エラーマッピング)を処理できる
  • サーバーインターセプター: Unary/Stream ハンドラーメソッドをオーバーライドし、continuation を呼び出す
  • クライアントインターセプター: チャネルの呼び出し元チェーンにアタッチする

よくある質問

「デッドライン、キャンセルとインターセプター」レッスンは無料ですか?

はい。「デッドライン、キャンセルとインターセプター」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。

「デッドライン、キャンセルとインターセプター」で何を学びますか?

デッドラインでタイムアウトを適用し、CancellationTokenを伝播させ、gRPCインターセプターで横断的関心事を追加します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C# Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「デッドライン、キャンセルとインターセプター」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC# Academyレッスンでコードを書いて実行できますか?

はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. gRPCとProtobufの基礎
  2. UnaryとサーバーストリーミングRPC
  3. クライアントと双方向ストリーミング
  4. デッドライン、キャンセルとインターセプター
← C# Academyに戻る