grpc-go Documentation

The Go language implementation of gRPC. HTTP/2 based RPC

RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy grpc-go on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name grpc-go --restart always grpc/grpc-go:latest

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:

go
import "google.golang.org/grpc"

Note: If you are trying to access grpc-go from China, see the
FAQ below.

Learn more

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:

console
$ 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 replace feature of go mod to create aliases for golang.org packages. In your project's directory:

    sh
    go mod edit -replace=google.golang.org/grpc=github.com/grpc/grpc-go@latest
    go mod tidy
    go mod vendor
    go build -mod=vendor

    Again, 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 using
go get google.golang.org/grpc.

How to turn on logging

The default logger is controlled by environment variables. Turn everything on
like this:

console
$ export GRPC_GO_LOG_VERBOSITY_LEVEL=99
$ export GRPC_GO_LOG_SEVERITY_LEVEL=info

The 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:

  1. mis-configured transport credentials, connection failed on handshaking
  2. bytes disrupted, possibly by a proxy in between
  3. server shutdown
  4. 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 corresponding
Unimplemented<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.:

sh
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 in
Register<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 a
ClientConn 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, and
FailOnNonTempDialError.

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 three
DialOptions that are only supported by Dial because they only affect the
behavior of Dial itself. WithBlock causes Dial to wait until the
ClientConn 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 a
ClientConn 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 and
WaitForStateChange 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 status field 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 an INVALID_ARGUMENT status code, that probably means
    your service has a bug (otherwise it shouldn't have triggered this error), in
    which case INTERNAL is 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:

go
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:

go
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:

go
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:

bash
$ go run google.golang.org/grpc/benchmark/benchmain/main.go \
    -workloads=streaming \
    -reqSizeBytes=1024 \
    -respSizeBytes=1024 \
    -compression=gzip

Pass 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:

csv
1,32,12.5
128,256,12.5
1024,2048,25.0

Assume that benchmain is invoked like so:

bash
$ go run google.golang.org/grpc/benchmark/benchmain/main.go \
    -workloads=unary \
    -reqPayloadCurveFiles=/path/to/csv \
    -respPayloadCurveFiles=/path/to/csv

This 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:

bash
$ 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/csv4

You may also combine PayloadCurveFiles with SizeBytes options. For example:

text
$ go run google.golang.org/grpc/benchmark/benchmain/main.go \
    -workloads=unary \
    -reqPayloadCurveFiles=/path/to/csv \
    -respSizeBytes=1

File: Documentation/compression.md

Compression

The preferred method for configuring message compression on both clients and
servers is to use
encoding.RegisterCompressor
to register an implementation of a compression algorithm. See
grpc/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 the
UseCompressor
CallOption. Remember that CallOptions may be turned into defaults for all
calls from a ClientConn by using the
WithDefaultCallOptions
DialOption. 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:

go
func WithCompressor(grpc.Compressor) DialOption {}
func WithDecompressor(grpc.Decompressor) DialOption {}
func UseCompressor(name) CallOption {}

For outgoing requests, the following rules are applied in order:

  1. If UseCompressor is 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.
  2. If WithCompressor is used, messages will be compressed using that
    compressor implementation.
  3. Otherwise, outbound messages will be uncompressed.

For incoming responses, the following rules are applied in order:

  1. If WithDecompressor is used and it matches the message's encoding, it will
    be used.
  2. If a registered compressor matches the response's encoding, it will be used.
  3. Otherwise, the stream will be closed and an Unimplemented status error will
    be returned to the application.

Server-Side

There are two legacy functions to configure compression:

go
func RPCCompressor(grpc.Compressor) ServerOption {}
func RPCDecompressor(grpc.Decompressor) ServerOption {}

For incoming requests, the following rules are applied in order:

  1. If RPCDecompressor is used and that decompressor matches the request's
    encoding: it will be used.
  2. If a registered compressor matches the request's encoding, it will be used.
  3. Otherwise, an Unimplemented status will be returned to the client.

For outgoing responses, the following rules are applied in order:

  1. If RPCCompressor is used, that compressor will be used to compress all
    response messages.
  2. 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.
  3. 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 or
RecvMsg 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:

go
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 in
encoding/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 another
Codec from a client or server:

go
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, the
CallOption CallContentSubtype should be used as follows:

go
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:

go
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 to
application/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). Like
Codecs, 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:

go
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 in
encoding/gzip.

Using a `Compressor`

By default, gRPC does not register or use any compressors. To use a
Compressor from a client or server:

go
package myclient

import _ "google.golang.org/grpc/encoding/gzip"

Compressors, by definition, must be symmetric, so the same desired
Compressor 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:

go
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:

go
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

go
conn, err := grpc.NewClient(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")))

Enabling TLS on a gRPC server

go
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 the
metadata.MD object, a server may use
metadata.FromIncomingContext.
With a reference to metadata.MD on the server, one needs to simply look up the
authorization 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 the
grpc.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)

go
conn, err := grpc.NewClient(serverAddr, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), grpc.WithPerRPCCredentials(oauth.NewComputeEngine()))

JWT

go
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:

go
type MD map[string][]string

Metadata 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:

go
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:

go
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:

go
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 using
AppendToOutgoingContext. 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.

go
// 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:

go
// 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:

go
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 trailer

Streaming 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 function
Header and Trailer in interface ClientStream:

go
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

go
func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
    md, ok := metadata.FromIncomingContext(ctx)
    // do something with metadata
}

Streaming call

go
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:

go
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:

go
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