POR ROBERTO ALANIZ, BACKEND ENGINEER
Cuando desarrollamos sistemas distribuidos, el manejo de errores no es una problemática trivial. Los diferentes enfoques para solventar dicha situación pueden traer consigo un código mantenible y escalable en el tiempo o, todo lo contrario. En este artículo veremos una de las formas más óptimas del manejo de errores en arquitecturas basadas en microservicios que implementan comunicación vía gRPC.

Interceptores: ¿para qué?

Los interceptores son una especie de middleware con el que podemos acceder al pipeline de consultas gRPC de nuestro microservicio. De esta manera podemos centralizar todo tipo de excepciones en un solo lugar y manejarlas de la forma que mejor nos convenga.

Tipos de interceptores

Dependiendo del tipo de comunicación que establezcan nuestros microservicios a través de gRPC deberemos utilizar un tipo de interceptor u otro, siendo éstos los más comunes:
● Unary requests
● Client streaming requests
● Server streaming requests
● Bi-directional streaming requests
Implementación
Para realizar la implementación de un interceptor en un microservicio será necesario seguir los siguientes pasos:

  1. Define una clase que implemente un interceptor en tu proyecto.
  2. Sobrescribe el método UnaryServerHandler para interceptar las llamadas unarias del servidor. En caso de no utilizar una comunicación de tipo “unary” sobrescribe el método correspondiente que se necesite.
  3. Implementa la lógica deseada dentro del método sobrescrito para este ejemplo UnaryServerHandler.
  4. Registra el interceptor en el servidor gRPC durante la configuración del servicio. 5. Prueba y verifica el funcionamiento del interceptor en tus servicios gRPC.
namespace Demo.gRPC.Interceptors;


public class ExceptionInterceptor : Interceptor
{
   public ExceptionInterceptor()
   {
   }


   public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
       TRequest request,
       ServerCallContext context,
       UnaryServerMethod<TRequest, TResponse> continuation)
   {
         return await continuation(request, context);
   }


   public override async Task<TResponse> ClientStreamingServerHandler<TRequest, TResponse>(
       IAsyncStreamReader<TRequest> requestStream,
       ServerCallContext context,
       ClientStreamingServerMethod<TRequest, TResponse> continuation)
   {
         return await continuation(requestStream, context);
   }




   public override async Task ServerStreamingServerHandler<TRequest, TResponse>(
       TRequest request,
       IServerStreamWriter<TResponse> responseStream,
       ServerCallContext context,
       ServerStreamingServerMethod<TRequest, TResponse> continuation)
   {
         await continuation(request, responseStream, context);
   }


   public override async Task DuplexStreamingServerHandler<TRequest, TResponse>(
       IAsyncStreamReader<TRequest> requestStream,
       IServerStreamWriter<TResponse> responseStream,
       ServerCallContext context,
       DuplexStreamingServerMethod<TRequest, TResponse> continuation)
   {
         await continuation(requestStream, responseStream, context);


   }
}

Como podemos ver, los interceptores proporcionan un mecanismo flexible para interceptar y procesar solicitudes gRPC, lo que permite aplicar una lógica personalizada para el manejo de errores, la autenticación, el registro y otras tareas transversales. Por esto es que la implementación de interceptores bien diseñados puede mejorar significativamente la calidad y la confiabilidad de las aplicaciones basadas en microservicios.