Search code examples
javagrpcinterceptorgrpc-javahealth-check

Bypass gRPC interceptor for specific endpoint


Lately I have been struggling with one problem : I can't ignore io.grpc.ServerInterceptor for some endpoints, in my case the health-check one, provided under the hood by io.grpc.protobuf.services.HealthServiceImpl

The only option that I could find is to define a new ServerInterceptor that retrieves the endpoint name through an io.grpc.Listener#onMessage and close the gRPC call before doing any of my other ServerInteceptor if the endpoint matches.

However, this solution does not seem very elegant. Is there a way, maybe from configuration, to ignore my existing interceptors ?

This issue is new, when I started using gRPC version 1.42.2. Everything was working well under gRPC 1.35.0.

Thank you for your help.

EDIT : some code

In my configuration class :


@Bean
HealthStatusManager healthStatusManager() {
        return new HealthStatusManager();
}

@Bean
Server server(
        Service1 service1,
        Service2 service2,
        HealthStatusManager healthStatusManager
) {
    return ServerBuilder.forPort(9090)
            .addService(service1)
            .addService(service2)
            .addService(healthStatusManager.getHealthService())
            // I do not want these to interceptors to be executed for healthService
            .intercept(new Interceptor1()) 
            .intercept(new Interceptor2())
            .build();
}

The Interceptor2 does some checks that I don't need for my health-check service (and even, that prevents it from working properly)


Solution

  • Protobuf/gRPC reflection could be useful in this case. I would implement such a solution by doing the following:

    Let's say that I have the following service:

    service HealthService {
        //...
    }
    

    that would generate a HealthServiceGrpc class and in this class there is a public static final String SERVICE_NAME (note that if you are using java lite version of gRPC this is maybe not existing).

    After that the ServerCall class that you have in your ServerInterceptor.interceptCall has a getMethodDescriptor() function. This returns a descriptor for the endpoint that you are calling. From that you can get the service name. With all that, a basic implementation would be:

    public class Interceptor1 implements ServerInterceptor {
        @Override
        public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
            if (Objects.equals(call.getMethodDescriptor().getServiceName(), HealthServiceGrpc.SERVICE_NAME))
                return next.startCall(call, headers);
    
            return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(next.startCall(call, headers)) {
                //...
            };
        }
    }
    

    To explain a little bit here, if the name of the service is HealthService, we just go ahead and make the call, otherwise we go through the interceptor implementation.

    Obviously, after that, if you want something less manual you might want to use a Singleton instance which register which interceptor to skip for which service.