gRPC - 인터셉터

2022. 5. 25. 00:00ASPNET/Grpc

반응형

인터셉터는 앱이 들어오는 또는 나가는 gRPC 호출과 상호 작용할 수 있도록 하는 gRPC 개념으로,

요청 처리 파이프라인을 보강하는 방법을 제공합니다.

인터셉터는 채널 또는 서비스에 대해 구성되고 각 gRPC 호출에서 자동으로 실행됩니다.

인터셉터는 사용자의 애플리케이션 논리에 투명하므로

로깅, 모니터링, 인증, 유효성 검사와 같은 공통 사례에 적합한 솔루션입니다.

 

기본 코드 

public class ExampleInterceptor : Interceptor
{
...
}

 

 

클라이언트 인터셉터

grpc server 로 나가는 rpc 호출을 가로챌 수 있다. 

  • BlockingUnaryCall: 단항 RPC의 차단 호출을 가로챕니다.
  • AsyncUnaryCall: 단항 RPC의 비동기 호출을 가로챕니다.
  • AsyncClientStreamingCall: 클라이언트 스트리밍 RPC의 비동기 호출을 가로챕니다.
  • AsyncServerStreamingCall: 서버 스트리밍 RPC의 비동기 호출을 가로챕니다.
  • AsyncDuplexStreamingCall: 양방향 스트리밍 RPC의 비동기 호출을 가로챕니다.

 

** 차단 호출과 비동기 호출은 서로 교환 하여 사용할 수 없다. 

둘은 각각에 해당하는 call 만 가로챈다. 다른 call 에 대해 가로채지 않는다.

즉 UnaryCall 은 둘(blockingXXX or asyncXXX) 모두에서 intercept 되지 않는다. **

 

 

ClientInterceptor/ClientLoggingInterceptor.cs

using Grpc.Core;
using Grpc.Core.Interceptors;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;

namespace GrpcClient.ClientInterceptor;
public class ClientLoggingInterceptor : Interceptor
{

    public override AsyncUnaryCall<TResponse> AsyncUnaryCall<TRequest, TResponse>(
        TRequest request,
        ClientInterceptorContext<TRequest, TResponse> context,
        AsyncUnaryCallContinuation<TRequest, TResponse> continuation)
    {
        var options = new JsonSerializerOptions()
        {
            IncludeFields = true,
            WriteIndented = true,
            Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
        };
        var jsonData = JsonSerializer.Serialize<object>(request, options);
        var message = $"Starting async unary call. Type: {context.Method.Type}. "
                    + $"Method: {context.Method.Name}."
                    + $"\nRequest => {jsonData}";
        Console.WriteLine(message);
        var responseResult = continuation(request, context);
        var response = responseResult.ResponseAsync?.Result;
        if (response != null)
            jsonData = JsonSerializer.Serialize<object>(response, options);
        else
            jsonData = String.Empty;
        message = $"\nResponse => {jsonData}";
        Console.WriteLine(message);
        return responseResult;
    }

    public override TResponse BlockingUnaryCall<TRequest, TResponse>(
        TRequest request,
        ClientInterceptorContext<TRequest, TResponse> context,
        BlockingUnaryCallContinuation<TRequest, TResponse> continuation)
    {
        var options = new JsonSerializerOptions()
        {
            IncludeFields = true,
            WriteIndented = true,
            Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
        };
        var jsonData = JsonSerializer.Serialize<object>(request, options);
        var message = $"Starting blocking unary call. Type: {context.Method.Type}. "
                    + $"Method: {context.Method.Name}."
                    + $"\nRequest => {jsonData}";
        Console.WriteLine(message);
        var response = continuation(request, context);
        jsonData = JsonSerializer.Serialize<object>(response, options);

        message = $"\nResponse => {jsonData}";
        Console.WriteLine(message);

        return response;
    }
}

 

Program.cs

...
var invoker = channel.Intercept(new ClientLoggingInterceptor());
var client = new Greeter.GreeterClient(invoker);

var replyAsync = await client.SayHelloAsync(
                  new HelloRequest { Name = "나는 천재" });
Console.WriteLine("Async Greeting: " + replyAsync.Message);

var reply = client.SayHello(
                  new HelloRequest { Name = "나는 천재" });
Console.WriteLine("Block Greeting: " + reply.Message);
...

실행

 

서버 인터셉터

 

ServerInterceptor/ServerLoggingInterceptor.cs

using Grpc.Core;
using Grpc.Core.Interceptors;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;

namespace GrpcServerWithASPNet.ServerInterceptor;

public class ServerLoggingInterceptor : Interceptor
{
    private readonly ILogger _logger;

    public ServerLoggingInterceptor(ILogger<ServerLoggingInterceptor> logger)
    {
        _logger = logger;
    }

    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        try
        {
            var options = new JsonSerializerOptions()
            {
                IncludeFields = true,
                WriteIndented = true,
                Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
            };
            var jsonData = JsonSerializer.Serialize<object>(request, options);
            var message = $"Starting ServerLoggingInterceptor. Type: {MethodType.Unary}. "
                        + $"Method: {context.Method}."
                        + $"\nRequest => {jsonData}";
            _logger.LogInformation(message);
            var response = await continuation(request, context);
            jsonData = JsonSerializer.Serialize<object>(response, options);

            message = $"\nResponse => {jsonData}";
            _logger.LogInformation(message);
            return response;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"Error thrown by {context.Method}.");
            throw;
        }
    }
}

Program.cs

...
//builder.Services.AddGrpc();
builder.Services.AddGrpc(options =>
{
    options.Interceptors.Add<ServerLoggingInterceptor>();
});
...

 

실행

 

관련영상

https://youtu.be/nyIALkvTLog

 

반응형

'ASPNET > Grpc' 카테고리의 다른 글

gRPC - 프로세스 간 통신 (IPC)  (0) 2022.05.27
gRPC - 로깅 및 진단  (0) 2022.05.26
gRPC - JWT Token 을 통한 인증 처리  (16) 2022.05.24
gRPC - Configuration  (0) 2022.05.23
grpc test - grpcui  (0) 2022.05.20