Add health checks to your gRPC service ConsoleOut

2 minute read

Health check support has been introduced via nuget package: Grpc.HealthCheck 1.17.1 and you can read more about it in official gRPC docs.

How it all looks like?

HealthCheck service exposes one method: “Check”, which will return an enum value.

enum ServingStatus {
    UNKNOWN = 0;
    SERVING = 1;
    NOT_SERVING = 2;
    SERVICE_UNKNOWN = 3;  // Used only by the Watch method.
}

The service implementation is pretty simple as you would expect.

public override Task<HealthCheckResponse> Check(HealthCheckRequest request, ServerCallContext context)
{
    lock (myLock)
    {
        var service = request.Service;

        HealthCheckResponse.Types.ServingStatus status;
        if (!statusMap.TryGetValue(service, out status))
        {
            // TODO(jtattermusch): returning specific status from server handler is not supported yet.
            throw new RpcException(new Status(StatusCode.NotFound, ""));
        }
        return Task.FromResult(new HealthCheckResponse { Status = status });
    }
}

How does it work?

The field “statusMap” is a dictionary where the key value is a service name of your gRPC service with the value being a ServingStatus. If you are wandering who is adding the values to the dictionary, well that’s your job :smile: Just as you need to register the different types when you are configuring a DI framework, for gRPC HealthCheck service you need to register the service name and accompanying ServingStatus.

To be honest I was expecting that I will be able to “reflect” my way out without manual labor, which I still think it’s doable and I plan to invest some time in it, but for the sake and brevity of this post let’s stick with the simpler way.

If the documentation is confusing to you, which might happen because it is not C# specific which is normal you can check the tests for HealthCheck service where you will see how to register your service with the HealthCheck service.

Summing it up

So, to have health checks for your gRPC service you need to:

  1. Export HealthCheck service to you gRPCS server
  2. Register your service with appropriate status

Here you can find a code example that contains simple gRPC service: “BookService” that support HealthChecks.

Register for health checks

Let’s say that you have your gRPC server implemented exposing service called: “BookingService”, to include HealthCheck service into you server logic you’d need to do this:

private static void RegisterHealthCheck(Server server, string serviceName)
{
    var healthImplementation = new HealthServiceImpl();
    healthImplementation.SetStatus("BookingService", HealthCheckResponse.Types.ServingStatus.Serving);
    server.Services.Add(Health.BindService(healthImplementation));
}

Depending on your needs, this might not be enough, and documentation specifies that we should also register an empty service name. One also might argue that if my gRPC server has only one service having it registered with an empty string should be fine.

Am I a healthy service?

We have our server running on localhost:5000 and know we won’t to check if the service is healthy, doing that is not different from consuming any other gRPC service.

var channel = new Channel("localhost:5000", ChannelCredentials.Insecure);
var healthCheckClient = new Health.HealthClient(channel);
var healthCheckResponse = healthCheckClient.Check(new HealthCheckRequest { Service = "BookService" });

The returned status will be: “SERVING”, keep in mind that if we would try to check a non existing service name, for example just send an empty request since we didn’t register the case for empty service name, an exception (RPCException) would be thrown and in it you would find the status of: “UNKNOWN”.

RpcException.Status.StatusCode

For production usage your client should offer some resiliency which you may solve with Polly, or through other means, also the connection timeout (deadline) should be set.

Conclusion

Simple implemantaion for a very welcomed new functionality for gRPCs. You may not like the manual registration for service name and status, but the step is reusable and probably doable through some smart reflection coding.

Leave a comment