[C#] .NET의 gRPC 인터셉터 - 클라이언트 측 인터셉터

참고


Interceptor 란?

  • 인터셉터는 앱이 들어오는 또는 나가는 gRPC 호출과 상호 작용할 수 있도록 하는 gRPC 개념으로, 요청 처리 파이프라인을 보강하는 방법을 제공합니다.
  • 인터셉터는 채널 또는 서비스에 대해 구성되고 각 gRPC 호출에서 자동으로 실행됩니다.
  • 인터셉터는 사용자의 애플리케이션 논리에 투명하므로 로깅, 모니터링, 인증, 유효성 검사와 같은 공통 사례에 적합한 솔루션입니다.

Interceptor 형식

  • Interceptor 형식에서 상속되는 클래스를 만들어 gRPC 서버 및 클라이언트 모두에 대해 인터셉터를 구현할 수 있습니다.
public class ExampleInterceptor : Interceptor
{
}
  • 기본적으로, Interceptor 기본 클래스는 아무 동작도 하지 않습니다.
  • 인터셉터 구현에서 적절한 기본 클래스 메서드를 재정의 하여 인터셉터에 동작을 추가합니다.

gRPC 인터셉터 프로젝트 만들기

  • 실제로 예제 코드를 작성하여 인터셉터에 대한 이해를 높여 보도록 하겠습니다.
  • gRPC 예제 코드는 C# 프로젝트로 작성할 것이고, Server/Client 작성해 보겠습니다.

클라이언트 측 인터셉터 작성

  • gRPC 클라이언트 인터셉터는 나가는 RPC 호출을 가로챌 수 있습니다.
  • 전송된 요청, 등어오는 응답 및 클라이언트 쪽 호출의 컨텍스트에 대한 액세스를 제공합니다.
  • 클라이언트에 대해 재정의할 Interceptor 메서드 :
    • BlockingUnaryCall : 단항 RPC의 차단 호출을 가로챕니다.
    • AsyncUnaryCall : 단항 RPC의 비동기 호출을 가로챕니다.
    • AsyncClientStreamCall : 클라이언트 스트리밍 RPC의 비동기 호출을 가로챕니다.
    • AsyncServerStreamCall : 서버 스트리밍 RPC 의 비동기 호출을 가로챕니다.
    • AsyncDuplexStreamingCall : 양방향 스트리밍 RPC의 비동기 호출을 가로챕니다.

BlockingUnaryCallAsyncUnaryCall 모두 단항 RPC 를 참조하지만 서로 교환하여 사용할 수는 없습니다. 차단 호출은 AsyncUnaryCall 에서 가로채지 않고, 비동기 호출은 BlockingUnaryCall 에서 가로채지 않습니다.

  • 그럼 ASP.NET Core gRPC 서비스 프로젝트 하나를 생성 후, 인터셉터 내용을 작성해 보겠습니다.

gRPC 서비스 프로젝트 생성

  • Visual Studio 2022 를 실행 후, ASP.NET Core gRPC 서비스 프로젝트를 생성 합니다.
  • 해당 프로젝트가 생성 되면, gRPC 서버가 생성된 것입니다.


클라이언트 gRPC 인터셉터 만들기

  • gRPC 서버 생성이 완료 되었다면, 콘솔 프로젝트 하나를 생성한 후 gRPC 클라이언트 인터셉터 클래스를 생성합니다.
  • 인터셉터 이름은 ClientLoggingInterceptor 이라고 정의하였고, 코드 내용은 아래와 같습니다.
using Grpc.Core;
using Grpc.Core.Interceptors;
using Microsoft.Extensions.Logging;

namespace Client.Interceptors
{
    public class ClientLoggingInterceptor : Interceptor
    {
        private readonly ILogger _logger;

        public ClientLoggingInterceptor(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<ClientLoggingInterceptor>();
        }

        public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
            TRequest request,
            ClientInterceptorContext<TRequest, TResponse> context,
            AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
        {
            _logger.LogInformation($"Starting call. Type: {context.Method.Type}. " +
                $"Method: {context.Method.Name}.");
            return continuation(request, context);
        }
    }
}
  • AsyncUnaryCall 재정의 :
    • 비동기 단항 호출을 가로챕니다.
    • 호출 세부 정보를 로깅합니다.
    • 메서드에 전달된 continuation 매개 변수를 호출합니다. 마지막 인터셉터인 경우 체인의 다음 인터셉터 또는 기본 호출 호출자를 호출합니다.
  • 각 서비스 메서드 종류의 Interceptor 에 대한 메서드에는 서로 다른 서명이 있습니다.
  • 그러나 continuationcontext 의 개념과 매개 변수는 동일하게 유지됩니다.
    • continuation 는 체인의 다음 인터셉터 또는 기본 호출 호출자를 호출하는 대리자입니다.
    • 한번도 호출하지 않거나 여러 번 호출하는 것은 오류가 아닙니다. 인터셉터는 continuation 대리자에서 반환된 호출 표현을 반환할 필요가 없습니다.
    • 대리자 호출을 생략하고 고유한 호출 표현 인스턴스를 반환하면 인터셉터 체인이 끊어지고 응답이 즉시 반환됩니다.
    • context 는 클라이언트 쪽 호출과 연결된 범위가 지정된 값을 전달합니다. context 는 보안 주체, 자격 증명 또는 추적 데이터와 같은 메타데이터를 전달하는 데 사용합니다.
    • 또한 context 는 최종 기한 및 취소에 대한 정보를 전달합니다.

클라이언트 인터셉터 구성하기

  • 앞서 ClientLoggingInterceptor 인터셉터 하나를 생성하였습니다.
  • 다음으로 gRPC 클라이언트 인터셉터를 채널에서 구성해야 합니다.
  • 구성 방법은 다음과 같습니다.
    • GrpcChannel.ForAddress 를 사용하여 태널을 만듭니다.
    • Intercept 확장 메서드를 사용하여 인터셉터를 사용하도록 채널을 구성합니다.
    • 이 메서드는 CallInvoker 를 반환합니다. 강력한 형식의 gRPC 클라이언트는 채널과 마찬가지로 호출자에게 만들 수 있습니다.
    • 호출자에서 클라이언트를 만듭니다. 클라이언트에서 수행한 gRPC 호출은 인터셉터를 자동으로 실행합니다.
using Client.Interceptors;
using Grpc.Core.Interceptors;
using Grpc.Net.Client;
using gRPCInterceptorSample;
using Microsoft.Extensions.Logging;

var loggerFactory = LoggerFactory.Create(logging =>
{
    logging.SetMinimumLevel(LogLevel.Debug);
});

using var channel = GrpcChannel.ForAddress("https://localhost:7081", new GrpcChannelOptions { LoggerFactory = loggerFactory });
var invoker = channel.Intercept(new ClientLoggingInterceptor(loggerFactory));

var client = new Greeter.GreeterClient(invoker);
var reply = await client.SayHelloAsync(
    new HelloRequest { Name = "Beom" }
    );

Console.WriteLine($"Greeting {reply.Message}");

실행 결과

  • 실행 결과, Client 가 Server 로 RPC 메시지를 보낼 때 ClientLoggingInterceptor 인터셉터가 먼저 요청 메시지를 가로채고, 로그가 찍히는 것을 확인할 수 있습니다.
Starting call. Type: Unary. Method: SayHello.
728x90

이 글을 공유하기

댓글(0)

Designed by JB FACTORY