gRPC Interceptor
May 12, 2024
Overview
gRPC Interceptors are middleware that wrap clients and servers. This middleware becomes part of the clients and servers themselves. It does not become a whole separate service that sits between the client and server (as is possible with the definition of middleware). The order in which interceptors are called depends on the implementation (see below).
Related blog posts:
Table of Contents
Use Cases
Below are some use cases for gRPC interceptors.
Use Case | Description |
---|---|
Authentication and Authorization | - Adding required credentials to client-side (outgoing) requests. - Ensuring that server-side (incoming) requests contain the required credentials. |
Logging | Logging request-level details (e.g. headers) at either client or server-side. Note: It's a security best practice to omit logging of authorization/authentication tokens in production. |
Monitoring and Metrics | Asynchronously sending a request (i.e. emitting a metric) for the purpose of observability/monitoring. Warning: synchronous I/O tasks should be avoided since they can noticeably increase latency. |
Error Handling | Centralized (in code in a single library that is consumed implicitly by all microservices) error handling can be located in interceptors. Retry mechanisms can be implemented here. |
Tracing | Interceptors are one of the best locations to implement tracing. Tracing is a useful nice-to-have in any distributed system that comes with its limitations (e.g. workloads that use batching). |
Caching | Checking if a request for a resource has already been made recently and returning a cached result, rather than allowing the request to complete its journey across a distributed system can noticeably reduce overall system load. |
Compression and Decompression | Interceptors are one way to add compression to your client/servers in a distributed system. But this is something that can be added when instantiating the client or server as well, which means it's also possible to alternatively create project-level client and server wrappers that enable compression/decompression. |
Client and Server Implementation Details
Official Java examples can be found at the grpc-java GitHub.
Something that is not shown in the official Java example above is how you define multiple interceptors. You add additional
interceptors as additional arguments to ServerInterceptors.intercept()
or ClientInterceptors.intercept()
. They're called
in reverse order (last is called first). Additional control exists within each interceptor where you can decide to implement
your logic before or after the next interceptor (indirectly through super.start()
for clients and next.startCall()
for
servers), assuming multiple interceptors (unless it's the final interceptor, in which case they'd be calling the client/server code).
Learn more about the ServerInterceptors and ClientInterceptors classes.
Conclusion
gRPC Interceptors can be thought of as wrappers around clients and servers. However, the instantiation of gRPC clients and servers can alternatively be wrapped with logic that is intended to be used globally in a project (e.g. using a Builder Pattern). Whether you choose interceptors or your own client/server-side wrapper should depend on the use case, performance, and maintainability trade-off. When the client or server offers built-in functionality (as is the case with compression in gRPC), it's likely that the efficiency is higher than implementation at the interceptor-level.
To be updated with benchmarks comparing client/server-level compression vs interceptor-level compression.
To be updated with diagram depicting multi-layered wrapping of multiple interceptors.