Exploring the async callback interface for gRPC in C++
If you have read the previous articles in this series, you probably agree with me that there it way too much boilerplate code required to implement even the simplest gRPC interface using the async API. And that is even without going in depth with threading models and optimizations.
Fortunately, there is a newer interface we can use. With that, we don't have to implement the event-loop and deal with absolutely all the details. rpcgen implements a callback interface as well as the legacy async interface. I have not seen it mentioned on the gRPC C++ landing page - so I hope I'm not revealing a big secret ;)
The callback interface is also async. If we use it for the server, we just have to override one method for each RPC in our interface. Well - kind of. The methods with one stream (in any direction) could have been implemented as coroutines. gRPC don't support that, and we have to fall back to implementing a second interface for the streaming methods to deal with the stream.
The only documentation i have found for the callback interface is a "C++ callback-based asynchronous API" proposal document that seems to have been accepted.
There are also two examples in the gRPC code base that shows how it can be used.
The callback interfaces provides a radical simplification, compared with the async API. It still require some code, but for most projects - it's probably better to not deal with the event loop and the low-level threading model. The gRPC team accumulate experience from many projects, and by leaving the low level details to them, we can hopefully expect better general performance and resource utilization than an average C++ development team without deep gRPC knowledge (or time to implement various approaches and experiment to find the better one) can hope for. In my experience, most teams just want to implement some RPC interface, and their focus is on their own functionality. They don't want to get too deeply into gRPC.
I think it's a huge mistake by the gRPC people to point C++ developers to the legacy async interface and not mentioning the callback interface at all on their C++ landing pages. I think the async interface can be useful for some large scale implementations or use-cases with special needs. But those use-cases require senior C++ developers with intimate knowledge about gRPC. For most projects, the callback interface should be the starting-point. At least, that's how I think about it.
Using the callbacks interface makes it easier to choose gRPC for inter-service or inter-system communication in our C++ projects. It still require careful coding to avoid performance bottlenecks, resource leaks and race conditions. That should not be the case in 2023!
I can't help it - I still hope the gRPC team, or someone else, release an open source tool to generate nice, co-routine friendly versions of the RPC's in our proto-files. Something that handles task dispatching, reference counting and removes all the current constraints from the front facing gRPC API's we use in our C++ code.
In the following two articles we will explore the gRPC callback interfaces together, first for the server, then for the client.