Quality: Platinum Tier.
🐙 userver provides a gRPC driver as userver-grpc
library. It uses namespace ugrpc::client
and namespace ugrpc::server
.
The driver wraps grpcpp
in the userver asynchronous interface.
See also:
Generate and link a library from .proto
schemas and link to it in your CMakeLists.txt
:
userver_add_grpc_library
will link userver-grpc
transitively and will generate the usual .pb.h + .pb.cc
files. For service definitions, it will additionally generate asynchronous interfaces foo_client.usrv.pb.hpp
and foo_service.usrv.pb.hpp
.
To create gRPC clients in your microservice, register the provided ugrpc::client::ClientFactoryComponent and add the corresponding component section to the static config.
To create gRPC services in your microservice, register the provided ugrpc::server::ServerComponent and add the corresponding component section to the static config.
In a component constructor, find ugrpc::client::ClientFactoryComponent and store a reference to its ugrpc::client::ClientFactory. Using it, you can create gRPC clients of code-generated YourServiceClient
types.
Client creation in an expensive operation! Either create them once at the server boot time or cache them.
Typical steps include:
std::unique_ptr<grpc::ClientContext>
with request settingsset_deadline
for each RPCFinish
or Read
until it returns false
(otherwise the connection will close abruptly)Read the documentation on gRPC streams:
On errors, exceptions from userver/ugrpc/client/exceptions.hpp are thrown. It is recommended to catch them outside the entire stream interaction. You can catch exceptions for specific gRPC error codes or all at once.
May be enabled for gRPC client via:
Available values are:
insecure
(default)ssl
SSL has to be disabled in tests, because it requires the server to have a public domain name, which it does not in tests. In testsuite, SSL in gRPC clients is disabled automatically.
TLS for gRPC server may be enabled via:
Client behaviour can be modified with a middleware. Middleware code is executed before or after the client code. Middlewares to use are indicated in static config in the defining component. For example:
grpc-client-logging
with component ugrpc::client::middlewares::log::Component - logs requests and responses.grpc-client-deadline-propagation
with component ugrpc::client::middlewares::deadline_propagation::Component - activates Deadline propagation.grpc-client-baggage
with component ugrpc::client::middlewares::baggage::Component - passes request baggage to subrequests.A service implementation is a class derived from a code-generated YourServiceBase
interface class. Each service method from the schema corresponds to a method of the interface class. If you don't override some of the methods, UNIMPLEMENTED
error code will be reported for those.
To register your service:
Each method receives:
When using a server stream, always call Finish
or FinishWithError
. Otherwise the client will receive UNKNOWN
error, which signifies an internal server error.
Read the documentation on gRPC streams:
On connection errors, exceptions from userver/ugrpc/server/exceptions.hpp are thrown. It is recommended not to catch them, leading to RPC interruption. You can catch exceptions for specific gRPC error codes or all at once.
By default, gRPC server uses grpc::InsecureServerCredentials
. To pass a custom credentials:
grpc-server.port
in the static configGrpcServerConfigurator
context.FindComponent<ugrpc::server::ServerComponent>().GetServer()
grpc::ServerBuilder::AddListeningPort
, passing it your custom credentials<grpcpp/security/server_credentials.h>
for available credentialsgrpc::SslServerCredentials
The gRPC server can be extended by middlewares. Middleware is called on each incoming (for service) or outgoing (for client) RPC request. Different middlewares handle the call in the defined order. A middleware may decide to reject the call or call the next middleware in the stack. Middlewares may implement almost any enhancement to the gRPC server including authorization and authentication, ratelimiting, logging, tracing, audit, etc.
Middlewares to use are indicated in static config in section middlewares
of ugrpc::server::ServiceComponentBase
descendant component. Default middleware list for handlers can be specified in grpc-server.service-defaults.middlewares
config section.
Example configuration:
Use ugrpc::server::MiddlewareBase and ugrpc::client::MiddlewareBase to implement new middlewares.
grpc-server-logging
with component ugrpc::server::middlewares::log::Component - logs requests.grpc-server-deadline-propagation
with component ugrpc::server::middlewares::deadline_propagation::Component - activates Deadline propagation.grpc-server-congestion-control
with component ugrpc::server::middlewares::congestion_control::Component - limits requests. See Congestion Control section of Production configs and best practices.grpc-server-baggage
with component ugrpc::server::middlewares::baggage::Component - passes request baggage to subrequests.grpc-server-headers-propagator
with component ugrpc::server::middlewares::headers_propagator::Component - propagates headers.grpc-server-field-mask
with component ugrpc::server::middlewares::field_mask::Component - parses the field mask from the request metadata and trims the response accordingly.Each gRPC call generates a span ( tracing::Span
) containing tags which are inherited by all child logs.
Additionally, if logging is activated, a separate log is generated for every gRPC request-response in grpc-server-logging
and grpc-client-logging
middlewares.
gRPC logs are listed below.
Middleware component name | Key | Value |
---|---|---|
builtin | error | error flag, true |
builtin | grpc_code | error code grpc::StatusCode , list |
builtin | error_msg | error message |
grpc-client-logging | meta_type | call name |
grpc-client-logging | grpc_type | request or response |
grpc-client-logging | body | logged message body |
grpc-client-deadline-propagation | deadline_updated | error flag, true |
grpc-client-deadline-propagation | timeout_ms | amount of time before request deadline |
Middleware component name | Key | Value |
---|---|---|
builtin | error | error flag true |
builtin | error_msg | error message |
grpc-server-logging | meta_type | call name |
grpc-server-logging | body | logged message body |
grpc-server-logging | type | request or response |
grpc-server-logging | grpc_type | request or response |
grpc-server-deadline-propagation | received_deadline_ms | deadline |
grpc-server-deadline-propagation | cancelled_by_deadline | true or false |
grpc-server-congestion-control | limit | rate limit when request is throttled (tokens per second) |
grpc-server-congestion-control | service/method | method name when request is throttled |
grpc-server-baggage | Got baggage header: | baggage header |
The gRPC driver provides log fields hiding in request-response logs. You need to do the following:
1) Add userver/field_options.proto to your service. Generate a library from this proto file and link against it in your CMakeLists.txt
.
2) Import userver/field_options.proto in your proto file.
3) Use [(userver.field).secret = true]
opposite to the filds that you want to hide. In the following example the fields password
and secret_code
will be hidden in the logs:
grpc-core is a lower level library, its logs are forwarded to the userver default logger. In this process only error level logs get through from grpc-core to the userver default logger if the default settings are used. However, the default settings can be overridden and more verbose logging can be achieved.
To do this you need to change the value of native-log-level
in the static config file in the components grpc-client-common
and grpc-server
:
There are 3 possible logging levels: error
, info
, debug
. If logging level is set in several components then the most verbose logging level is used.
gRPC generic API allows to call and accept RPCs with dynamic service and method names. The other side will see this as a normal RPC, it does not need to use generic API.
Intended mainly for use in proxies. Metadata can be used to proxy the request without parsing it.
See details in:
Full example showing the usage of both:
Based on:
grpc.client.by-destination
grpc.server.by-destination
grpc.server.total
Each metric has the following labels:
grpc_service
- fully qualified grpc (proto) service namegrpc_method
- fully qualified grpc method namegrpc_destination
= grpc_service/grpc_method
grpc_destination_full
= client_name/grpc_service/grpc_method
(only for client metrics)These are the metrics provided for each gRPC method:
timings.1min
— time from RPC start to finish (utils::statistics::Percentile
)status
with label grpc_code=STATUS_CODE_NAME
— RPCs that finished with specified status codes, one metric per gRPC status. Zero status
metrics are omitted, except for OK
and UNKNOWN
metrics, which are always written.cancelled
— RPCs that were interrupted due to task cancellation. (Not to be confused with RPCs finished with CANCELLED
status.) Server-side, this means that the client dropped the RPC or called TryCancel
. Client-side, this likely means that either the parent handler was interrupted, or the RPC was dropped as unnecessary. See ugrpc::client::RpcCancelledError and ugrpc::server::RpcInterruptedErrorcancelled-by-deadline-propagation
— RPCs, the handling of which was interrupted because the deadline specified in the request was reached. (Available for both server and client-side.) See also userver deadline propagationnetwork-error
— other RPCs that finished abruptly without a status, see ugrpc::client::RpcInterruptedError and ugrpc::server::RpcInterruptedErrorabandoned-error
— RPCs that we forgot to Finish
(always a bug in ugrpc
usage). Such RPCs also separately report the status or network error that occurred during the automatic request terminationdeadline-propagated
— RPCs, for which deadline was specified. See also userver deadline propagationrps
— requests per second: eps
— server errors per second UNKNOWN
DATA_LOSS
UNIMPLEMENTED
INTERNAL
UNAVAILABLE
network-error
is not accounted in eps
, because either the client is responsible for the server dropping the request (TryCancel
, deadline), or it is truly a network error, in which case it's typically helpful for troubleshooting to say that there are issues not with the uservice process itself, but with the infrastructureactive
— The number of currently active RPCs (created and not finished)An example of userver gRPC metrics.