DotNet GRPC clients - How to recover from failure?
•
jonashendrickx
Introduction
In distributed computing, failures are inevitable. The ability to handle failures gracefully is critical to building robust systems. In this article, we will explore how to recover from failures when using .NET gRPC clients.
.NET gRPC is a modern, high-performance framework for building distributed systems. It uses Protocol Buffers as the data serialization format and supports bi-directional streaming. gRPC clients are lightweight and can be used in any .NET application.
Before we can discuss recovery strategies, we need to understand the types of failures that can occur in gRPC communication. These include network failures, service failures, and protocol errors.
There are several strategies that we can use to recover from failures in gRPC clients. One approach is to use a retry mechanism that attempts to reconnect to the server after a configurable delay. Another strategy is to implement circuit-breaking, which temporarily disables communication with the service in case of repeated failures. Finally, we can use a fallback mechanism to switch to an alternative service or data source when the primary service is unavailable.
The code
We could either choose to write our caching logic in our client class implementing the GRPC client. Or we can make our code more flexible and use ‘Interceptor’. With ‘Interceptor’ we can quickly try swap different solutions of trying to solve our problem without sacrificing platform stability.
We will be serializing the method name and then the parameter request object to a string and concatenate them together for our interceptor.
We will need our configuration class which we will inject using the options pattern.
Our interface for our GRPC client.
And its implementation. Note how light this code is.
Now that we have our implementation. Let’s go bootstrap this into our dependency injection container. Generally, I like to split this in a separate class or extension method. So it’s easier to identify what belongs together and swap in or out with something else. Otherwise your ‘Program.cs’ or ‘Startup.cs’ could become a nightmare to manage.
We are adding all our services as a singleton, because they don’t really need to maintain relevant a state. If you want to know more about the IHttpClientFactory pattern, check the Microsoft documentation. We are similarly wiring up our GrpcClient using the ‘named’ strategy.
You always want to set the base url here too, to avoid having to copy paste it all over your client implementation.
In the ‘AddCallCredentials’ extension method, you can see an example how we’re injecting an API key from our options pattern.
And last but not least, we’re adding our ‘ResiliencyInterceptor’, note that you’ll also need to set a scope in the ‘IServiceCollection’.
If you take a peek back at our GRPC client we’ve written, you’ll notice quickly how ‘GetAllAsync’ is actually async, while ‘GetById’ is synchronous. ‘GetAllAsync’ will hit the ‘AsyncUnaryCall’ method in our interceptor. While ‘GetById’ will hit the ‘BlockingUnaryCall’ method in our interceptor.