File: README.md
gRPC-Go
The Go implementation of gRPC: A high performance, open source, general
RPC framework that puts mobile and HTTP/2 first. For more information see the
Go gRPC docs, or jump directly into the quick start.
Prerequisites
Installation
Simply add the following import to your code, and then go [build|run|test]
will automatically fetch the necessary dependencies:
import "google.golang.org/grpc"Note: If you are trying to access
grpc-gofrom China, see the
FAQ below.
Learn more
- Go gRPC docs, which include a quick start and API
reference among other resources - Low-level technical docs from this repository
- Performance benchmark
- Examples
- Contribution guidelines
FAQ
I/O Timeout Errors
The golang.org domain may be blocked from some countries. go get usually
produces an error like the following when this happens:
$ go get -u google.golang.org/grpc
package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)To build Go code, there are several options:
Set up a VPN and access google.golang.org through that.
With Go module support: it is possible to use the
replacefeature ofgo modto create aliases for golang.org packages. In your project's directory:shgo mod edit -replace=google.golang.org/grpc=github.com/grpc/grpc-go@latest go mod tidy go mod vendor go build -mod=vendorAgain, this will need to be done for all transitive dependencies hosted on
golang.org as well. For details, refer to golang/go issue
#28652.
Compiling error, undefined: grpc.SupportPackageIsVersion
Please update to the latest version of gRPC-Go usinggo get google.golang.org/grpc.
How to turn on logging
The default logger is controlled by environment variables. Turn everything on
like this:
$ export GRPC_GO_LOG_VERBOSITY_LEVEL=99
$ export GRPC_GO_LOG_SEVERITY_LEVEL=infoThe RPC failed with error `"code = Unavailable desc = transport is closing"`
This error means the connection the RPC is using was closed, and there are many
possible reasons, including:
- mis-configured transport credentials, connection failed on handshaking
- bytes disrupted, possibly by a proxy in between
- server shutdown
- Keepalive parameters caused connection shutdown, for example if you have
configured your server to terminate connections regularly to trigger DNS
lookups.
If this is the case, you may want to increase your
MaxConnectionAgeGrace,
to allow longer RPC calls to finish.
It can be tricky to debug this because the error happens on the client side but
the root cause of the connection being closed is on the server side. Turn on
logging on both client and server, and see if there are any transport
errors.
File: cmd/protoc-gen-go-grpc/README.md
protoc-gen-go-grpc
This tool generates Go language bindings of services in protobuf definition
files for gRPC. For usage information, please see our quick start
guide.
Future-proofing services
By default, to register services using the methods generated by this tool, the
service implementations must embed the correspondingUnimplemented<ServiceName>Server for future compatibility. This is a behavior
change from the grpc code generator previously included with protoc-gen-go.
To restore this behavior, set the option require_unimplemented_servers=false.
E.g.:
protoc --go-grpc_out=. --go-grpc_opt=require_unimplemented_servers=false[,other options...] \Note that this is not recommended, and the option is only provided to restore
backward compatibility with previously-generated code.
When embedding the Unimplemented<ServiceName>Server in a struct that
implements the service, it should be embedded by value instead of as a
pointer. If it is embedded as a pointer, it must be assigned to a valid,
non-nil pointer or else unimplemented methods would panic when called. This is
tested at service registration time, and will lead to a panic inRegister<ServiceName>Server if it is not embedded properly.
File: Documentation/anti-patterns.md
Anti-Patterns of Client creation
How to properly create a `ClientConn`: `grpc.NewClient`
grpc.NewClient is the
function in the gRPC library that creates a virtual connection from a client
application to a gRPC server. It takes a target URI (which represents the name
of a logical backend service and resolves to one or more physical addresses) and
a list of options, and returns aClientConn object that
represents the virtual connection to the server. The ClientConn contains one
or more actual connections to real servers and attempts to maintain these
connections by automatically reconnecting to them when they break. NewClient
was introduced in gRPC-Go v1.63.
The wrong way: `grpc.Dial`
grpc.Dial is a deprecated
function that also creates the same virtual connection pool as grpc.NewClient.
However, unlike grpc.NewClient, it immediately starts connecting and supports
a few additional DialOptions that control this initial connection attempt.
These are: WithBlock, WithTimeout, WithReturnConnectionError, andFailOnNonTempDialError.
That grpc.Dial creates connections immediately is not a problem in and of
itself, but this behavior differs from how gRPC works in all other languages,
and it can be convenient to have a constructor that does not perform I/O. It
can also be confusing to users, as most people expect a function called Dial
to create a connection which may need to be recreated if it is lost.
grpc.Dial uses "passthrough" as the default name resolver for backward
compatibility while grpc.NewClient uses "dns" as its default name resolver.
This subtle difference is important to legacy systems that also specified a
custom dialer and expected it to receive the target string directly.
For these reasons, using grpc.Dial is discouraged. Even though it is marked
as deprecated, we will continue to support it until a v2 is released (and no
plans for a v2 exist at the time this was written).
Especially bad: using deprecated `DialOptions`
FailOnNonTempDialError, WithBlock, and WithReturnConnectionError are threeDialOptions that are only supported by Dial because they only affect the
behavior of Dial itself. WithBlock causes Dial to wait until theClientConn reports its State as connectivity.Connected. The other two deal
with returning connection errors before the timeout (WithTimeout or on the
context when using DialContext).
The reason these options can be a problem is that connections with aClientConn are dynamic -- they may come and go over time. If your client
successfully connects, the server could go down 1 second later, and your RPCs
will fail. "Knowing you are connected" does not tell you much in this regard.
Additionally, all RPCs created on an "idle" or a "connecting" ClientConn
will wait until their deadline or until a connection is established before
failing. This means that you don't need to check that a ClientConn is "ready"
before starting your RPCs. By default, RPCs will fail if the ClientConn
enters the "transient failure" state, but setting WaitForReady(true) on a
call will cause it to queue even in the "transient failure" state, and it will
only ever fail due to a deadline, a server response, or a connection loss after
the RPC was sent to a server.
Some users of Dial use it as a way to validate the configuration of their
system. If you wish to maintain this behavior but migrate to NewClient, you
can call GetState, then Connect if the state is Idle andWaitForStateChange until the channel is connected. However, if this fails,
it does not mean that your configuration was bad - it could also mean the
service is not reachable by the client due to connectivity reasons.
Best practices for error handling in gRPC
Instead of relying on failures at dial time, we strongly encourage developers to
rely on errors from RPCs. When a client makes an RPC, it can receive an error
response from the server. These errors can provide valuable information about
what went wrong, including information about network issues, server-side errors,
and incorrect usage of the gRPC API.
By handling errors from RPCs correctly, developers can write more reliable and
robust gRPC applications. Here are some best practices for error handling in
gRPC:
- Always check for error responses from RPCs and handle them appropriately.
- Use the
statusfield of the error response to determine the type of error
that occurred. - When retrying failed RPCs, consider using the built-in retry mechanism
provided by gRPC-Go, if available, instead of manually implementing retries.
Refer to the gRPC-Go retry example
documentation
for more information. Note that this is not a substitute for client-side
retries as errors that occur after an RPC starts on a server cannot be
retried through gRPC's built-in mechanism. - If making an outgoing RPC from a server handler, be sure to translate the
status code before returning the error from your method handler. For example,
if the error is anINVALID_ARGUMENTstatus code, that probably means
your service has a bug (otherwise it shouldn't have triggered this error), in
which caseINTERNALis more appropriate to return back to your users.
Example: Handling errors from an RPC
The following code snippet demonstrates how to handle errors from an RPC in
gRPC:
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
res, err := client.MyRPC(ctx, &MyRequest{})
if err != nil {
// Handle the error appropriately,
// log it & return an error to the caller, etc.
log.Printf("Error calling MyRPC: %v", err)
return nil, err
}
// Use the response as appropriate
log.Printf("MyRPC response: %v", res)To determine the type of error that occurred, you can use the status field of
the error response:
resp, err := client.MakeRPC(context.TODO(), request)
if err != nil {
if status, ok := status.FromError(err); ok {
// Handle the error based on its status code
if status.Code() == codes.NotFound {
log.Println("Requested resource not found")
} else {
log.Printf("RPC error: %v", status.Message())
}
} else {
// Handle non-RPC errors
log.Printf("Non-RPC error: %v", err)
}
return
}
// Use the response as needed
log.Printf("Response received: %v", resp)Example: Using a backoff strategy
When retrying failed RPCs, use a backoff strategy to avoid overwhelming the
server or exacerbating network issues:
var res *MyResponse
var err error
retryableStatusCodes := map[codes.Code]bool{
codes.Unavailable: true, // etc
}
// Retry the RPC a maximum number of times.
for i := 0; i < maxRetries; i++ {
// Make the RPC.
res, err = client.MyRPC(context.TODO(), &MyRequest{})
// Check if the RPC was successful.
if !retryableStatusCodes[status.Code(err)] {
// The RPC was successful or errored in a non-retryable way;
// do not retry.
break
}
// The RPC is retryable; wait for a backoff period before retrying.
backoff := time.Duration(i+1) * time.Second
log.Printf("Error calling MyRPC: %v; retrying in %v", err, backoff)
time.Sleep(backoff)
}
// Check if the RPC was successful after all retries.
if err != nil {
// All retries failed, so handle the error appropriately
log.Printf("Error calling MyRPC: %v", err)
return nil, err
}
// Use the response as appropriate.
log.Printf("MyRPC response: %v", res)File: Documentation/benchmark.md
Benchmark
gRPC-Go comes with a set of benchmarking utilities to measure performance.
These utilities can be found in the benchmark directory within the project's
root directory.
The main utility, aptly named benchmain, supports a host of configurable
parameters to simulate various environments and workloads. For example, if your
server's workload is primarily streaming RPCs with large messages with
compression turned on, invoking benchmain in the following way may closely
simulate your application:
$ go run google.golang.org/grpc/benchmark/benchmain/main.go \
-workloads=streaming \
-reqSizeBytes=1024 \
-respSizeBytes=1024 \
-compression=gzipPass the -h flag to the benchmain utility to see other flags and workloads
that are supported.
Varying Payload Sizes (Weighted Random Distribution)
The benchmain utility supports two flags, -reqPayloadCurveFiles and-respPayloadCurveFiles, that can be used to specify histograms representing
a weighted random distribution of request and response payload sizes,
respectively. This is useful to simulate workloads with arbitrary payload
sizes.
The options take a comma-separated list of file paths as value. Each file must
be a valid CSV file with three columns in each row. Each row represents a range
of payload sizes (first two columns) and the weight associated with that range
(third column). For example, consider the below file:
1,32,12.5
128,256,12.5
1024,2048,25.0Assume that benchmain is invoked like so:
$ go run google.golang.org/grpc/benchmark/benchmain/main.go \
-workloads=unary \
-reqPayloadCurveFiles=/path/to/csv \
-respPayloadCurveFiles=/path/to/csvThis tells the benchmain utility to generate unary RPC requests with a 25%
probability of payload sizes in the ranges 1-32 bytes, 25% probability in the
128-256 bytes range, and 50% probability in the 1024-2048 bytes range. RPC
requests outside these ranges will not be generated.
You may specify multiple CSV files delimited by a comma. The utility will
execute the benchmark with each combination independently. That is, the
following command will execute four benchmarks:
$ go run google.golang.org/grpc/benchmark/benchmain/main.go \
-workloads=unary \
-reqPayloadCurveFiles=/path/to/csv1,/path/to/csv2 \
-respPayloadCurveFiles=/path/to/csv3,/path/to/csv4You may also combine PayloadCurveFiles with SizeBytes options. For example:
$ go run google.golang.org/grpc/benchmark/benchmain/main.go \
-workloads=unary \
-reqPayloadCurveFiles=/path/to/csv \
-respSizeBytes=1File: Documentation/compression.md
Compression
The preferred method for configuring message compression on both clients and
servers is to useencoding.RegisterCompressor
to register an implementation of a compression algorithm. Seegrpc/encoding/gzip/gzip.go for an example of how to implement one.
Once a compressor has been registered on the client-side, RPCs may be sent using
it via theUseCompressorCallOption. Remember that CallOptions may be turned into defaults for all
calls from a ClientConn by using theWithDefaultCallOptionsDialOption. If UseCompressor is used and the corresponding compressor has
not been installed, an Internal error will be returned to the application
before the RPC is sent.
Server-side, registered compressors will be used automatically to decode request
messages and encode the responses. Servers currently always respond using the
same compression method specified by the client. If the corresponding
compressor has not been registered, an Unimplemented status will be returned
to the client.
Deprecated API
There is a deprecated API for setting compression as well. It is not
recommended for use. However, if you were previously using it, the following
section may be helpful in understanding how it works in combination with the new
API.
Client-Side
There are two legacy functions and one new function to configure compression:
func WithCompressor(grpc.Compressor) DialOption {}
func WithDecompressor(grpc.Decompressor) DialOption {}
func UseCompressor(name) CallOption {}For outgoing requests, the following rules are applied in order:
- If
UseCompressoris used, messages will be compressed using the compressor
named.- If the compressor named is not registered, an Internal error is returned
back to the client before sending the RPC. - If UseCompressor("identity"), no compressor will be used, but "identity"
will be sent in the header to the server.
- If the compressor named is not registered, an Internal error is returned
- If
WithCompressoris used, messages will be compressed using that
compressor implementation. - Otherwise, outbound messages will be uncompressed.
For incoming responses, the following rules are applied in order:
- If
WithDecompressoris used and it matches the message's encoding, it will
be used. - If a registered compressor matches the response's encoding, it will be used.
- Otherwise, the stream will be closed and an
Unimplementedstatus error will
be returned to the application.
Server-Side
There are two legacy functions to configure compression:
func RPCCompressor(grpc.Compressor) ServerOption {}
func RPCDecompressor(grpc.Decompressor) ServerOption {}For incoming requests, the following rules are applied in order:
- If
RPCDecompressoris used and that decompressor matches the request's
encoding: it will be used. - If a registered compressor matches the request's encoding, it will be used.
- Otherwise, an
Unimplementedstatus will be returned to the client.
For outgoing responses, the following rules are applied in order:
- If
RPCCompressoris used, that compressor will be used to compress all
response messages. - If compression was used for the incoming request and a registered compressor
supports it, that same compression method will be used for the outgoing
response. - Otherwise, no compression will be used for the outgoing response.
File: Documentation/concurrency.md
Concurrency
In general, gRPC-go provides a concurrency-friendly API. What follows are some
guidelines.
Clients
A ClientConn can safely be accessed concurrently. Using
helloworld as an example, one could share the ClientConn across
multiple goroutines to create multiple GreeterClient types. In this case,
RPCs would be sent in parallel. GreeterClient, generated from the proto
definitions and wrapping ClientConn, is also concurrency safe, and may be
directly shared in the same way. Note that, as illustrated in
the multiplex example, other Client types may share a
single ClientConn as well.
Streams
When using streams, one must take care to avoid calling either SendMsg orRecvMsg multiple times against the same Stream from different
goroutines. In other words, it's safe to have a goroutine calling SendMsg and
another goroutine calling RecvMsg on the same stream at the same time. But it
is not safe to call SendMsg on the same stream in different goroutines, or to
call RecvMsg on the same stream in different goroutines.
Servers
Each RPC handler attached to a registered server will be invoked in its own
goroutine. For example, SayHello will be invoked in its own
goroutine. The same is true for service handlers for streaming RPCs, as seen
in the route guide example here. Similar to clients,
multiple services can be registered to the same server.
File: Documentation/encoding.md
Encoding
The gRPC API for sending and receiving is based upon messages. However,
messages cannot be transmitted directly over a network; they must first be
converted into bytes. This document describes how gRPC-Go converts messages
into bytes and vice-versa for the purposes of network transmission.
Codecs (Serialization and Deserialization)
A Codec contains code to serialize a message into a byte slice (Marshal) and
deserialize a byte slice back into a message (Unmarshal). Codecs are
registered by name into a global registry maintained in the encoding package.
Implementing a `Codec`
A typical Codec will be implemented in its own package with an init function
that registers itself, and is imported anonymously. For example:
package proto
import "google.golang.org/grpc/encoding"
func init() {
encoding.RegisterCodec(protoCodec{})
}
// ... implementation of protoCodec ...For an example, gRPC's implementation of the proto codec can be found inencoding/proto.
Using a `Codec`
By default, gRPC registers and uses the "proto" codec, so it is not necessary to
do this in your own code to send and receive proto messages. To use anotherCodec from a client or server:
package myclient
import _ "path/to/another/codec"Codecs, by definition, must be symmetric, so the same desired Codec should
be registered in both client and server binaries.
On the client-side, to specify a Codec to use for message transmission, theCallOption CallContentSubtype should be used as follows:
response, err := myclient.MyCall(ctx, request, grpc.CallContentSubtype("mycodec"))As a reminder, all CallOptions may be converted into DialOptions that become
the default for all RPCs sent through a client using grpc.WithDefaultCallOptions:
myclient := grpc.NewClient(target, grpc.WithDefaultCallOptions(grpc.CallContentSubtype("mycodec")))When specified in either of these ways, messages will be encoded using this
codec and sent along with headers indicating the codec (content-type set toapplication/grpc+<codec name>).
On the server-side, using a Codec is as simple as registering it into the
global registry (i.e. importing it). If a message is encoded with the content
sub-type supported by a registered Codec, it will be used automatically for
decoding the request and encoding the response. Otherwise, for
backward-compatibility reasons, gRPC will attempt to use the "proto" codec. In
an upcoming change (tracked in this
issue), such requests will be
rejected with status code Unimplemented instead.
Compressors (Compression and Decompression)
Sometimes, the resulting serialization of a message is not space-efficient, and
it may be beneficial to compress this byte stream before transmitting it over
the network. To facilitate this operation, gRPC supports a mechanism for
performing compression and decompression.
A Compressor contains code to compress and decompress by wrapping io.Writers
and io.Readers, respectively. (The form of Compress and Decompress were
chosen to most closely match Go's standard package
implementations of compressors). LikeCodecs, Compressors are registered by name into a global registry maintained
in the encoding package.
Implementing a `Compressor`
A typical Compressor will be implemented in its own package with an init
function that registers itself, and is imported anonymously. For example:
package gzip
import "google.golang.org/grpc/encoding"
func init() {
encoding.RegisterCompressor(compressor{})
}
// ... implementation of compressor ...An implementation of a gzip compressor can be found inencoding/gzip.
Using a `Compressor`
By default, gRPC does not register or use any compressors. To use aCompressor from a client or server:
package myclient
import _ "google.golang.org/grpc/encoding/gzip"Compressors, by definition, must be symmetric, so the same desiredCompressor should be registered in both client and server binaries.
On the client-side, to specify a Compressor to use for message transmission,
the CallOption UseCompressor should be used as follows:
response, err := myclient.MyCall(ctx, request, grpc.UseCompressor("gzip"))As a reminder, all CallOptions may be converted into DialOptions that become
the default for all RPCs sent through a client using grpc.WithDefaultCallOptions:
myclient := grpc.NewClient(target, grpc.WithDefaultCallOptions(grpc.UseCompressor("gzip")))When specified in either of these ways, messages will be compressed using this
compressor and sent along with headers indicating the compressor
(content-coding set to <compressor name>).
On the server-side, using a Compressor is as simple as registering it into the
global registry (i.e. importing it). If a message is compressed with the
content coding supported by a registered Compressor, it will be used
automatically for decompressing the request and compressing the response.
Otherwise, the request will be rejected with status code Unimplemented.
File: Documentation/grpc-auth-support.md
Authentication
As outlined in the
gRPC authentication guide, there are a
number of different mechanisms for asserting identity between a client and
server. We'll present some code-samples here demonstrating how to provide TLS
support encryption and identity assertions as well as passing OAuth2 tokens to
services that support it.
Enabling TLS on a gRPC client
conn, err := grpc.NewClient(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")))Enabling TLS on a gRPC server
creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
if err != nil {
log.Fatalf("Failed to generate credentials %v", err)
}
lis, err := net.Listen("tcp", ":0")
server := grpc.NewServer(grpc.Creds(creds))
...
server.Serve(lis)OAuth2
For an example of how to configure client and server to use OAuth2 tokens, see
here.
Validating a token on the server
Clients may use
metadata.MD to store
tokens and other authentication-related data. To gain access to themetadata.MD object, a server may use
metadata.FromIncomingContext.
With a reference to metadata.MD on the server, one needs to simply look up theauthorization key. Note, all keys stored within metadata.MD are normalized
to lowercase. See here.
It is possible to configure token validation for all RPCs using an interceptor.
A server may configure either a
grpc.UnaryInterceptor
or a
grpc.StreamInterceptor.
Adding a token to all outgoing client RPCs
To send an OAuth2 token with each RPC, a client may configure thegrpc.DialOption
grpc.WithPerRPCCredentials.
Alternatively, a client may also use the grpc.CallOption
grpc.PerRPCCredentials
on each invocation of an RPC.
To create a credentials.PerRPCCredentials, use
oauth.TokenSource.
Note, the OAuth2 implementation of grpc.PerRPCCredentials requires a client to
use
grpc.WithTransportCredentials
to prevent any insecure transmission of tokens.
Authenticating with Google
Google Compute Engine (GCE)
conn, err := grpc.NewClient(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), grpc.WithPerRPCCredentials(oauth.NewComputeEngine()))JWT
jwtCreds, err := oauth.NewServiceAccountFromFile(*serviceAccountKeyFile, *oauthScope)
if err != nil {
log.Fatalf("Failed to create JWT credentials: %v", err)
}
conn, err := grpc.NewClient(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), grpc.WithPerRPCCredentials(jwtCreds))File: Documentation/grpc-metadata.md
Metadata
gRPC supports sending metadata between client and server.
This doc shows how to send and receive metadata in gRPC-go.
Background
Four kinds of service method:
And concept of metadata.
Constructing metadata
A metadata can be created using package metadata.
The type MD is actually a map from string to a list of strings:
type MD map[string][]stringMetadata can be read like a normal map.
Note that the value type of this map is []string,
so that users can attach multiple values using a single key.
Creating a new metadata
A metadata can be created from a map[string]string using function New:
md := metadata.New(map[string]string{"key1": "val1", "key2": "val2"})Another way is to use Pairs.
Values with the same key will be merged into a list:
md := metadata.Pairs(
"key1", "val1",
"key1", "val1-2", // "key1" will have map value []string{"val1", "val1-2"}
"key2", "val2",
)Note: all the keys will be automatically converted to lowercase,
so "key1" and "kEy1" will be the same key and their values will be merged into the same list.
This happens for both New and Pairs.
Storing binary data in metadata
In metadata, keys are always strings. But values can be strings or binary data.
To store binary data value in metadata, simply add "-bin" suffix to the key.
The values with "-bin" suffixed keys will be encoded when creating the metadata:
md := metadata.Pairs(
"key", "string value",
"key-bin", string([]byte{96, 102}), // this binary data will be encoded (base64) before sending
// and will be decoded after being transferred.
)Sending and receiving metadata - client side
Client side metadata sending and receiving examples are available
here.
Sending metadata
There are two ways to send metadata to the server. The recommended way is to append kv pairs to the context usingAppendToOutgoingContext. This can be used with or without existing metadata on the context. When there is no prior
metadata, metadata is added; when metadata already exists on the context, kv pairs are merged in.
// create a new context with some metadata
ctx := metadata.AppendToOutgoingContext(ctx, "k1", "v1", "k1", "v2", "k2", "v3")
// later, add some more metadata to the context (e.g. in an interceptor)
ctx := metadata.AppendToOutgoingContext(ctx, "k3", "v4")
// make unary RPC
response, err := client.SomeRPC(ctx, someRequest)
// or make streaming RPC
stream, err := client.SomeStreamingRPC(ctx)Alternatively, metadata may be attached to the context using NewOutgoingContext. However, this
replaces any existing metadata in the context, so care must be taken to preserve the existing
metadata if desired. This is slower than using AppendToOutgoingContext. An example of this
is below:
// create a new context with some metadata
md := metadata.Pairs("k1", "v1", "k1", "v2", "k2", "v3")
ctx := metadata.NewOutgoingContext(context.Background(), md)
// later, add some more metadata to the context (e.g. in an interceptor)
send, _ := metadata.FromOutgoingContext(ctx)
newMD := metadata.Pairs("k3", "v3")
ctx = metadata.NewOutgoingContext(ctx, metadata.Join(send, newMD))
// make unary RPC
response, err := client.SomeRPC(ctx, someRequest)
// or make streaming RPC
stream, err := client.SomeStreamingRPC(ctx)Receiving metadata
Metadata that a client can receive includes header and trailer.
Unary call
Header and trailer sent along with a unary call can be retrieved using function
Header and Trailer in CallOption:
var header, trailer metadata.MD // variable to store header and trailer
r, err := client.SomeRPC(
ctx,
someRequest,
grpc.Header(&header), // will retrieve header
grpc.Trailer(&trailer), // will retrieve trailer
)
// do something with header and trailerStreaming call
For streaming calls including:
- Server streaming RPC
- Client streaming RPC
- Bidirectional streaming RPC
Header and trailer can be retrieved from the returned stream using functionHeader and Trailer in interface ClientStream:
stream, err := client.SomeStreamingRPC(ctx)
// retrieve header
header, err := stream.Header()
// retrieve trailer
trailer := stream.Trailer()Sending and receiving metadata - server side
Server side metadata sending and receiving examples are available
here.
Receiving metadata
To read metadata sent by the client, the server needs to retrieve it from RPC
context using FromIncomingContext.
If it is a unary call, the RPC handler's context can be used.
For streaming calls, the server needs to get context from the stream.
Unary call
func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
md, ok := metadata.FromIncomingContext(ctx)
// do something with metadata
}Streaming call
func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error {
md, ok := metadata.FromIncomingContext(stream.Context()) // get context from stream
// do something with metadata
}Sending metadata
Unary call
To send header and trailer to client in unary call, the server can call
SetHeader and SetTrailer functions in module grpc.
These two functions take a context as the first parameter.
It should be the RPC handler's context or one derived from it:
func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
// create and set header
header := metadata.Pairs("header-key", "val")
grpc.SetHeader(ctx, header)
// create and set trailer
trailer := metadata.Pairs("trailer-key", "val")
grpc.SetTrailer(ctx, trailer)
}Streaming call
For streaming calls, header and trailer can be sent using function
SetHeader and SetTrailer in interface ServerStream:
func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error {
// create and set header
header := metadata.Pairs("header-key", "val")
stream.SetHeader(header)
// create and set trailer
trailer := metadata.Pairs("trailer-key", "val")
stream.SetTrailer(trailer)
}Important
Do not use
FromOutgoingContext on the server to write metadata to be sent to the client.
FromOutgoingContext is for client-side use only.
Updating metadata from a server interceptor
An example for updating metadata from a server interceptor is
available here.
File: Documentation/keepalive.md
Keepalive
gRPC sends http2 pings on the transport to detect if the connection is down. If
the ping is not acknowledged by the other side within a certain period, the
connection will be closed. Note that pings are only necessary when there's no
activity on the connection.
For how to configure keepalive, see
https://godoc.org/google.golang.org/grpc/keepalive for the options.
Why do I need this?
Keepalive can be useful to detect TCP level connection failures. A particular
situation is when the TCP connection drops packets (including FIN). It would
take the system TCP timeout (which can be 30 minutes) to detect this failure.
Keepalive would allow gRPC to detect this failure much sooner.
Another usage is (as the name suggests) to keep the connection alive. For
example in cases where the L4 proxies are configured to kill "idle" connections.
Sending pings would make the connections not "idle".
What should I set?
It should be sufficient for most users to set client
parameters as a dial
option.
What will happen?
(The behavior described here is specific for gRPC-go, it might be slightly
different in other languages.)
When there's no activity on a connection (note that an ongoing stream results in
no activity when there's no message being sent), after Time, a ping will
be sent by the client and the server will send a ping ack when it gets the ping.
Client will wait for Timeout, and check if there's any activity on the
connection during this period (a ping ack is an activity).
What about server side?
Server has similar Time and Timeout settings as client. Server can also
configure connection max-age. See server
parameters
for details.
Enforcement policy
Enforcement
policy is
a special setting on server side to protect server from malicious or misbehaving
clients.
Server sends GOAWAY with ENHANCE_YOUR_CALM and close the connection when bad
behaviors are detected:
- Client sends too frequent pings
- Client sends pings when there's no stream and this is disallowed by server
config