Share via


gRPC를 사용하여 오류 처리

작성자: James Newton-King

이 문서에서는 오류 처리 및 gRPC에 대해 설명합니다.

기본 제공 오류 처리

gRPC 호출은 상태 코드를 사용하여 성공 또는 실패를 전달합니다. gRPC 호출이 성공적으로 완료되면 서버는 클라이언트에 OK 상태 반환합니다. 오류가 발생하면 gRPC는 다음을 반환합니다.

  • 오류 상태 코드(예: CANCELLED 또는 UNAVAILABLE.
  • 선택적 문자열 오류 메시지입니다.

오류 처리에 일반적으로 사용되는 형식은 다음과 같습니다.

  • StatusCode: gRPC 상태 코드의 열거형입니다. OK 신호 성공; 다른 값은 실패입니다.
  • Statusstruct: 문자열 오류 메시지와 선택적 문자열을 결합 StatusCode 하는 값입니다. 오류 메시지는 발생한 작업에 대한 자세한 정보를 제공합니다.
  • RpcException: 값이 있는 예외 형식입니다 Status . 이 예외는 gRPC 서버 메서드에서 throw되고 gRPC 클라이언트에 의해 catch됩니다.

기본 제공 오류 처리는 상태 코드 및 문자열 설명만 지원합니다. 서버에서 클라이언트 로 복잡한 오류 정보를 보내려면 풍부한 오류 처리를 사용합니다.

서버 오류 throw

gRPC 서버 호출은 항상 상태 반환합니다. 메서드가 성공적으로 완료되면 서버가 자동으로 반환 OK 됩니다.

public class GreeterService : GreeterBase
{
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        return Task.FromResult(new HelloReply { Message = $"Hello {request.Name}" });
    }

    public override async Task SayHelloStreaming(HelloRequest request,
        IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
    {
        for (var i = 0; i < 5; i++)
        {
            await responseStream.WriteAsync(new HelloReply { Message = $"Hello {request.Name} {i}" });
            await Task.Delay(TimeSpan.FromSeconds(1));
        }
    }
}

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

  • 응답 메시지를 반환할 때 성공적으로 완료되는 단항 SayHello 메서드를 구현합니다.
  • 메서드가 완료되면 성공적으로 완료되는 서버 스트리밍 SayHelloStreaming 메서드를 구현합니다.

서버 오류 상태

gRPC 메서드는 예외를 throw하여 오류 상태 코드를 반환합니다. RpcException 서버에서 throw되면 해당 상태 코드 및 설명이 클라이언트에 반환됩니다.

public class GreeterService : GreeterBase
{
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        if (string.IsNullOrEmpty(request.Name))
        {
            throw new RpcException(new Status(StatusCode.InvalidArgument, "Name is required."));
        }
        return Task.FromResult(new HelloReply { Message = $"Hello {request.Name}" });
    }
}

호출이 실패하지 RpcException 는 않지만 UNKNOWN 상태 코드와 일반 메시지를 Exception was thrown by handler포함하는 throw된 예외 형식입니다.

Exception was thrown by handler 는 잠재적으로 중요한 정보가 노출되지 않도록 예외 메시지 대신 클라이언트로 전송됩니다. 개발 환경에서 보다 설명적인 오류 메시지를 보려면 구성 EnableDetailedErrors합니다.

클라이언트 오류 처리

gRPC 클라이언트가 호출을 하면 응답에 액세스할 때 상태 코드의 유효성이 자동으로 검사됩니다. 예를 들어 단항 gRPC 호출을 기다리면 호출이 성공하면 서버에서 보낸 메시지가 반환되고 오류가 발생하면 throw됩니다 RpcException . Catch RpcException 를 통해 클라이언트에서 오류를 처리합니다.

var client = new Greet.GreeterClient(channel);

try
{
    var response = await client.SayHelloAsync(new HelloRequest { Name = "World" });
    Console.WriteLine("Greeting: " + response.Message);
}
catch (RpcException ex)
{
    Console.WriteLine("Status code: " + ex.Status.StatusCode);
    Console.WriteLine("Message: " + ex.Status.Detail);
}

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

  • 메서드에 대한 단항 gRPC 호출을 만듭니 SayHello 다.
  • 성공하면 응답 메시지를 콘솔에 씁니다.
  • 오류에 대한 오류 세부 정보를 catch RpcException 하고 씁니다.

오류 시나리오

오류는 오류 상태 코드 및 선택적 세부 정보 메시지로 표시됩니다RpcException. RpcException 는 다음과 같은 여러 시나리오에서 throw됩니다.

  • 서버에서 호출이 실패했고 서버가 코드에 상태 오류를 보냈습니다. 예를 들어 gRPC 클라이언트는 요청 메시지에서 필요한 데이터가 누락된 호출을 시작했고 서버는 상태 코드를 반환 INVALID_ARGUMENT 합니다.
  • gRPC를 호출할 때 클라이언트 내부에서 오류가 발생했습니다. 예를 들어 클라이언트가 gRPC를 호출하고 서버에 연결할 수 없으며 상태 UNAVAILABLE오류가 발생합니다.
  • CancellationToken gRPC 호출에 전달된 호출이 취소됩니다. gRPC 호출이 중지되고 클라이언트가 상태 CANCELLED오류를 throw합니다.
  • gRPC 호출이 구성된 기한을 초과합니다. gRPC 호출이 중지되고 클라이언트가 상태 DEADLINE_EXCEEDED오류를 throw합니다.

풍부한 오류 처리

풍부한 오류 처리를 사용하면 복잡한 구조적 정보를 오류 메시지와 함께 보낼 수 있습니다. 예를 들어 잘못된 필드 이름 및 설명 목록을 반환하는 들어오는 메시지 필드의 유효성을 검사합니다. google.rpc.Status 오류 모델은 gRPC 앱 간에 복잡한 오류 정보를 보내는 데 자주 사용됩니다.

.NET의 gRPC는 패키지를 사용하여 Grpc.StatusProto 풍부한 오류 모델을 지원합니다. 이 패키지에는 서버에서 다양한 오류 모델을 만들고 클라이언트에서 읽는 메서드가 있습니다. 풍부한 오류 모델은 gRPC의 기본 제공 처리 기능을 기반으로 하며 병렬로 사용할 수 있습니다.

Important

오류는 헤더에 포함되며 응답의 총 헤더는 종종 8KB(8,192바이트)로 제한됩니다. 오류가 포함된 헤더가 8KB를 초과하지 않는지 확인합니다.

서버에서 다양한 오류 만들기

에서 다양한 오류가 생성 Google.Rpc.Status됩니다. 이 형식은 Grpc.Core.Status.

Google.Rpc.Status에는 상태, 메시지 및 세부 정보 필드가 있습니다. 가장 중요한 필드는 값의 Any 반복 필드인 세부 정보입니다. 세부 정보는 복잡한 페이로드가 추가되는 위치입니다.

모든 메시지 유형을 페이로드로 사용할 수 있지만 표준 오류 페이로드 중 하나를 사용하는 것이 좋습니다.

  • BadRequest
  • PreconditionFailure
  • ErrorInfo
  • ResourceInfo
  • QuotaFailure

Grpc.StatusProto 에는 ToRpcException 오류로 변환 Google.Rpc.Status 할 도우미 메서드가 포함되어 있습니다. gRPC 서버 메서드에서 오류를 throw합니다.

public class GreeterService : Greeter.GreeterBase
{
    public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
    {
        ArgumentNotNullOrEmpty(request.Name);

        return Task.FromResult(new HelloReply { Message = "Hello " + request.Name });
    }
    
    public static void ArgumentNotNullOrEmpty(string value, [CallerArgumentExpression(nameof(value))] string? paramName = null)
    {
        if (string.IsNullOrEmpty(value))
        {
            var status = new Google.Rpc.Status
            {
                Code = (int)Code.InvalidArgument,
                Message = "Bad request",
                Details =
                {
                    Any.Pack(new BadRequest
                    {
                        FieldViolations =
                        {
                            new BadRequest.Types.FieldViolation { Field = paramName, Description = "Value is null or empty" }
                        }
                    })
                }
            };
            throw status.ToRpcException();
        }
    }
}

클라이언트에서 다양한 오류 읽기

클라이언트에서 catch된 RpcException 오류에서 다양한 오류를 읽습니다. 예외를 catch하고 제공된 도우미 메서드를 Grpc.StatusCode 사용하여 해당 Google.Rpc.Status 인스턴스를 가져옵니다.

var client = new Greet.GreeterClient(channel);

try
{
    var reply = await client.SayHelloAsync(new HelloRequest { Name = name });
    Console.WriteLine("Greeting: " + reply.Message);
}
catch (RpcException ex)
{
    Console.WriteLine($"Server error: {ex.Status.Detail}");
    var badRequest = ex.GetRpcStatus()?.GetDetail<BadRequest>();
    if (badRequest != null)
    {
        foreach (var fieldViolation in badRequest.FieldViolations)
        {
            Console.WriteLine($"Field: {fieldViolation.Field}");
            Console.WriteLine($"Description: {fieldViolation.Description}");
        }
    }
}

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

  • catch하는 try/catch 내에서 gRPC 호출을 만듭니다 RpcException.
  • 예외에서 풍부한 오류 모델을 가져오기 위한 호출 GetRpcStatus() 입니다.
  • 풍부한 오류에서 페이로드를 BadRequest 가져오기 위해 호출 GetDetail<BadRequest>() 합니다.

추가 리소스