다음을 통해 공유


gRPC 재시도를 통해 일시적인 오류 처리

작성자: James Newton-King

gRPC 재시도는 gRPC 클라이언트가 실패한 호출을 자동으로 다시 시도하는 기능입니다. 이 문서에서는 .NET에서 복원력과 내결함성이 있는 gRPC 앱을 만들기 위해 재시도 정책을 구성하는 방법에 관해 설명합니다.

gRPC 다시 시도에는 Grpc.Net.Client 버전 2.36.0 이상이 필요합니다.

일시적인 오류 처리

gRPC 호출은 일시적인 오류로 인해 중단될 수 있습니다. 일시적인 오류에는 다음이 포함됩니다.

  • 네트워크 연결이 일시적으로 끊김
  • 서비스를 일시적으로 사용할 수 없음
  • 서버 로드로 인한 시간 초과

gRPC 호출이 중단되면 클라이언트는 오류에 대한 세부 정보와 함께 RpcException을 throw합니다. 클라이언트 앱은 예외를 catch하고 오류를 처리하는 방법을 선택해야 합니다.

var client = new Greeter.GreeterClient(channel);
try
{
    var response = await client.SayHelloAsync(
        new HelloRequest { Name = ".NET" });

    Console.WriteLine("From server: " + response.Message);
}
catch (RpcException ex)
{
    // Write logic to inspect the error and retry
    // if the error is from a transient fault.
}

앱 전체에서 다시 시도 논리를 중복하는 것은 장황한 일이며 오류를 일으킬 수 있습니다. 다행히 .NET gRPC 클라이언트는 자동 다시 시도를 기본적으로 지원합니다.

gRPC 재시도 정책 구성

gRPC 채널을 만들 때 재시도 정책이 한 번 구성됩니다.

var defaultMethodConfig = new MethodConfig
{
    Names = { MethodName.Default },
    RetryPolicy = new RetryPolicy
    {
        MaxAttempts = 5,
        InitialBackoff = TimeSpan.FromSeconds(1),
        MaxBackoff = TimeSpan.FromSeconds(5),
        BackoffMultiplier = 1.5,
        RetryableStatusCodes = { StatusCode.Unavailable }
    }
};

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
    ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
});

앞의 코드가 하는 역할은 다음과 같습니다.

  • MethodConfig을 만듭니다. 다시 시도 정책은 메서드당 구성할 수 있으며 Names 속성을 사용하여 메서드를 일치시킵니다. 이 메서드는 MethodName.Default로 구성되므로 이 채널에서 호출하는 모든 gRPC 메서드에 적용됩니다.
  • 재시도 정책을 구성합니다. 이 정책은 상태 코드 Unavailable로 실패한 gRPC 호출을 자동으로 다시 시도하도록 클라이언트에 지시합니다.
  • GrpcChannelOptions.ServiceConfig를 설정하여 만든 채널이 재시도 정책을 사용하도록 구성합니다.

채널로 만든 gRPC 클라이언트는 실패한 호출을 자동으로 다시 시도합니다.

var client = new Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(
    new HelloRequest { Name = ".NET" });

Console.WriteLine("From server: " + response.Message);

다시 시도가 유효한 경우

호출은 다음의 경우에 다시 시도됩니다.

  • 실패 상태 코드가 RetryableStatusCodes의 값과 일치하는 경우
  • 이전 시도 횟수가 MaxAttempts보다 적은 경우
  • 호출이 커밋되지 않은 경우
  • 최종 기한을 초과하지 않았습니다.

gRPC 호출은 다음과 같은 두 가지 시나리오에서 커밋됩니다.

  • 클라이언트가 응답 헤더를 받습니다. 응답 헤더는 ServerCallContext.WriteResponseHeadersAsync가 호출될 때 또는 첫 번째 메시지가 서버 응답 스트림에 기록될 때 서버에서 전송됩니다.
  • 클라이언트의 보내는 메시지(또는 스트리밍인 경우의 메시지)가 클라이언트의 최대 버퍼 크기를 초과했습니다. MaxRetryBufferSizeMaxRetryBufferPerCallSize채널에 구성되어 있습니다.

커밋된 호출은 상태 코드나 이전 시도 횟수와 관계없이 다시 시도되지 않습니다.

스트리밍 호출

스트리밍 호출은 gRPC 다시 시도와 함께 사용할 수 있지만 함께 사용할 경우 중요한 고려 사항이 있습니다.

  • 서버 스트리밍, 양방향 스트리밍: 서버에서 여러 메시지를 반환하는 스트리밍 RPC는 첫 번째 메시지를 받은 후 다시 시도되지 않습니다. 앱이 서버 및 양방향 스트리밍 호출을 다시 설정하기 위해서는 추가 논리를 추가해야 합니다.
  • 클라이언트 스트리밍, 양방향 스트리밍: 서버에 여러 메시지를 보내는 스트리밍 RPC는 보내는 메시지가 클라이언트의 최대 버퍼 크기를 초과한 경우 다시 시도되지 않습니다. 최대 버퍼 크기는 구성을 통해 늘릴 수 있습니다.

자세한 내용은 다시 시도가 유효한 경우를 참조하세요.

재시도 백오프 지연

재시도 사이의 백오프 지연은 InitialBackoff, MaxBackoffBackoffMultiplier로 구성됩니다. 각 옵션에 대한 자세한 내용은 gRPC 재시도 옵션 섹션에서 확인할 수 있습니다.

재시도 사이의 실제 지연은 임의화됩니다. 0과 현재 백오프 사이의 임의 지연은 언제 다음 다시 시도가 수행될지를 결정합니다. 지수 백오프가 구성된 경우에도 시도 사이의 현재 백오프를 늘린다고 해서 시도 간의 실제 지연 시간이 항상 더 큰 것은 아닙니다. 지연은 임의화되어 클러스터링에서 여러 호출이 재시도되는 것을 방지하고 잠재적인 서버 오버로드를 방지합니다.

메타데이터를 사용하여 다시 시도 검색

gRPC 재시도는 grpc-previous-rpc-attempts 메타데이터가 있으면 검색할 수 있습니다. grpc-previous-rpc-attempts 메타데이터:

  • 재시도된 호출에 자동으로 추가되고 서버로 전송됩니다.
  • 값은 이전 재시도 횟수를 나타냅니다.
  • 값은 항상 정수입니다.

다음 재시도 시나리오를 고려합니다.

  1. 클라이언트가 서버에 gRPC를 호출합니다.
  2. 서버가 실패하고 다시 시도 가능한 상태 코드 응답을 반환합니다.
  3. 클라이언트는 gRPC 호출을 다시 시도합니다. 이전에 한 번의 시도가 있었기 때문에 grpc-previous-rpc-attempts 메타데이터의 값은 1입니다. 메타데이터는 재시도를 사용하여 서버로 전송됩니다.
  4. 서버가 성공하고 확인을 반환합니다.
  5. 클라이언트는 성공을 보고합니다. grpc-previous-rpc-attempts는 응답 메타데이터에 있고 값은 1입니다.

grpc-previous-rpc-attempts 메타데이터는 초기 gRPC 호출에 없으며, 첫 번째 재시도에 대한 1, 두 번째 재시도에 대한 2 등입니다.

gRPC 다시 시도 옵션

다음 표에서는 gRPC 다시 시도 정책을 구성하기 위한 옵션을 설명합니다.

옵션 설명
MaxAttempts 원래 시도를 포함한 최대 호출 시도 횟수입니다. 이 값은 GrpcChannelOptions.MaxRetryAttempts에 의해 제한되며 기본값은 5입니다. 값은 필수이며 1보다 커야 합니다.
InitialBackoff 다시 시도 사이의 초기 백오프 지연입니다. 0과 현재 백오프 사이의 임의 지연은 언제 다음 다시 시도가 수행될지를 결정합니다. 각 시도 후에는 현재 백오프에 BackoffMultiplier를 곱합니다. 값은 필수이며 0보다 커야 합니다.
MaxBackoff 최대 백오프는 지수 백오프 증가에 대한 상한을 설정합니다. 값은 필수이며 0보다 커야 합니다.
BackoffMultiplier 다시 시도할 때마다 백오프에 이 값을 곱하게 되며 승수가 1보다 크면 백오프가 기하급수적으로 증가합니다. 값은 필수이며 0보다 커야 합니다.
RetryableStatusCodes 상태 코드의 컬렉션입니다. 일치 상태와 함께 실패하는 gRPC 호출을 자동으로 다시 시도합니다. 상태 코드에 대한 자세한 내용은 상태 코드 및 gRPC에서의 사용을 참조하세요. 다시 시도 가능한 상태 코드가 하나 이상 필요합니다.

Hedging

Hedging은 대체 다시 시도 전략입니다. Hedging은 응답을 기다리지 않고 단일 gRPC 호출의 여러 복사본을 적극적으로 전송할 수 있습니다. Hedged gRPC 호출은 서버에서 여러 번 실행될 수 있으며 처음 성공한 결과가 사용됩니다. Hedging은 부정적인 영향 없이 여러 번 실행해도 안전한 메서드에만 사용하도록 설정하는 것이 중요합니다.

Hedging은 다시 시도와 비교했을 때 장단점이 있습니다.

  • Hedging의 장점은 성공적인 결과를 더 빠르게 반환할 수 있다는 것입니다. 여러 동시 gRPC 호출을 허용하고 첫 번째 성공적인 결과를 사용할 수 있을 때 완료됩니다.
  • Hedging의 단점은 낭비가 될 수 있다는 것입니다. 여러 번 호출할 수 있으며 모두 성공할 수 있습니다. 첫 번째 결과만 사용되고 나머지는 삭제됩니다.

gRPC hedging 정책 구성

Hedging 정책은 재시도 정책처럼 구성됩니다. Hedging 정책은 재시도 정책과 함께 사용할 수 없습니다.

var defaultMethodConfig = new MethodConfig
{
    Names = { MethodName.Default },
    HedgingPolicy = new HedgingPolicy
    {
        MaxAttempts = 5,
        NonFatalStatusCodes = { StatusCode.Unavailable }
    }
};

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
    ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
});

gRPC hedging 옵션

다음 표에서는 gRPC hedging 정책을 구성하기 위한 옵션을 설명합니다.

옵션 설명
MaxAttempts Hedging 정책은 이 호출 수만큼 보냅니다. MaxAttempts는 원래 시도를 포함한 모든 시도의 총횟수를 나타냅니다. 이 값은 GrpcChannelOptions.MaxRetryAttempts에 의해 제한되며 기본값은 5입니다. 값은 필수이며 2 이상이어야 합니다.
HedgingDelay 첫 번째 호출은 즉시 전송되며, 후속 hedging 호출은 이 값으로 지연됩니다. 지연을 0 또는 null로 설정하면 모든 hedged 호출이 즉시 전송됩니다. HedgingDelay는 선택 사항이며 기본적으로 0으로 설정됩니다. 값은 0 이상이어야 합니다.
NonFatalStatusCodes 다른 hedge 호출이 성공할 수 있음을 나타내는 상태 코드의 컬렉션입니다. 서버에서 심각하지 않은 상태 코드를 반환하는 경우에는 hedged 호출이 계속됩니다. 아니면 처리되지 않은 요청이 취소되고 오류가 앱으로 반환됩니다. 상태 코드에 대한 자세한 내용은 상태 코드 및 gRPC에서의 사용을 참조하세요.

추가 리소스