snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
This commit is contained in:
183
Documentation/anti-patterns.md
Normal file
183
Documentation/anti-patterns.md
Normal file
@@ -0,0 +1,183 @@
|
||||
## Anti-Patterns of Client creation
|
||||
|
||||
### How to properly create a `ClientConn`: `grpc.NewClient`
|
||||
|
||||
[`grpc.NewClient`](https://pkg.go.dev/google.golang.org/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`](https://pkg.go.dev/google.golang.org/grpc#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`](https://pkg.go.dev/google.golang.org/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 `DialOption`s 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
|
||||
`DialOption`s 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](https://github.com/grpc/grpc-go/blob/master/examples/features/retry/README.md)
|
||||
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)
|
||||
```
|
||||
75
Documentation/benchmark.md
Normal file
75
Documentation/benchmark.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# 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:
|
||||
|
||||
```
|
||||
$ go run google.golang.org/grpc/benchmark/benchmain/main.go \
|
||||
-workloads=unary \
|
||||
-reqPayloadCurveFiles=/path/to/csv \
|
||||
-respSizeBytes=1
|
||||
```
|
||||
80
Documentation/compression.md
Normal file
80
Documentation/compression.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Compression
|
||||
|
||||
The preferred method for configuring message compression on both clients and
|
||||
servers is to use
|
||||
[`encoding.RegisterCompressor`](https://godoc.org/google.golang.org/grpc/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`](https://godoc.org/google.golang.org/grpc#UseCompressor)
|
||||
`CallOption`. Remember that `CallOption`s may be turned into defaults for all
|
||||
calls from a `ClientConn` by using the
|
||||
[`WithDefaultCallOptions`](https://godoc.org/google.golang.org/grpc#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.
|
||||
1. If `WithCompressor` is used, messages will be compressed using that
|
||||
compressor implementation.
|
||||
1. 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.
|
||||
1. If a registered compressor matches the response's encoding, it will be used.
|
||||
1. 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.
|
||||
1. If a registered compressor matches the request's encoding, it will be used.
|
||||
1. 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.
|
||||
1. 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.
|
||||
1. Otherwise, no compression will be used for the outgoing response.
|
||||
39
Documentation/concurrency.md
Normal file
39
Documentation/concurrency.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Concurrency
|
||||
|
||||
In general, gRPC-go provides a concurrency-friendly API. What follows are some
|
||||
guidelines.
|
||||
|
||||
## Clients
|
||||
|
||||
A [ClientConn][client-conn] can safely be accessed concurrently. Using
|
||||
[helloworld][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][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][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][say-hello] 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][route-guide-stream]. Similar to clients,
|
||||
multiple services can be registered to the same server.
|
||||
|
||||
[helloworld]: https://github.com/grpc/grpc-go/blob/master/examples/helloworld/greeter_client/main.go#L43
|
||||
[client-conn]: https://godoc.org/google.golang.org/grpc#ClientConn
|
||||
[stream]: https://godoc.org/google.golang.org/grpc#Stream
|
||||
[say-hello]: https://github.com/grpc/grpc-go/blob/master/examples/helloworld/greeter_server/main.go#L41
|
||||
[route-guide-stream]: https://github.com/grpc/grpc-go/blob/master/examples/route_guide/server/server.go#L126
|
||||
[multiplex-example]: https://github.com/grpc/grpc-go/tree/master/examples/features/multiplex
|
||||
146
Documentation/encoding.md
Normal file
146
Documentation/encoding.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# 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`). `Codec`s 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`](https://godoc.org/google.golang.org/grpc/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"
|
||||
```
|
||||
|
||||
`Codec`s, 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 `CallOption`s may be converted into `DialOption`s 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. `import`ing 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](https://github.com/grpc/grpc-go/issues/1824)), 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.Writer`s
|
||||
and `io.Reader`s, respectively. (The form of `Compress` and `Decompress` were
|
||||
chosen to most closely match Go's standard package
|
||||
[implementations](https://golang.org/pkg/compress/) of compressors). Like
|
||||
`Codec`s, `Compressor`s 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`](https://godoc.org/google.golang.org/grpc/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"
|
||||
```
|
||||
|
||||
`Compressor`s, 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 `CallOption`s may be converted into `DialOption`s 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. `import`ing 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`.
|
||||
83
Documentation/grpc-auth-support.md
Normal file
83
Documentation/grpc-auth-support.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Authentication
|
||||
|
||||
As outlined in the
|
||||
[gRPC authentication guide](https://grpc.io/docs/guides/auth.html), 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](https://github.com/grpc/grpc-go/tree/master/examples/features/authentication).
|
||||
|
||||
### Validating a token on the server
|
||||
|
||||
Clients may use
|
||||
[metadata.MD](https://godoc.org/google.golang.org/grpc/metadata#MD) to store
|
||||
tokens and other authentication-related data. To gain access to the
|
||||
`metadata.MD` object, a server may use
|
||||
[metadata.FromIncomingContext](https://godoc.org/google.golang.org/grpc/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](https://godoc.org/google.golang.org/grpc/metadata#New).
|
||||
|
||||
It is possible to configure token validation for all RPCs using an interceptor.
|
||||
A server may configure either a
|
||||
[grpc.UnaryInterceptor](https://godoc.org/google.golang.org/grpc#UnaryInterceptor)
|
||||
or a
|
||||
[grpc.StreamInterceptor](https://godoc.org/google.golang.org/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](https://godoc.org/google.golang.org/grpc#WithPerRPCCredentials).
|
||||
Alternatively, a client may also use the `grpc.CallOption`
|
||||
[grpc.PerRPCCredentials](https://godoc.org/google.golang.org/grpc#PerRPCCredentials)
|
||||
on each invocation of an RPC.
|
||||
|
||||
To create a `credentials.PerRPCCredentials`, use
|
||||
[oauth.TokenSource](https://godoc.org/google.golang.org/grpc/credentials/oauth#TokenSource).
|
||||
Note, the OAuth2 implementation of `grpc.PerRPCCredentials` requires a client to
|
||||
use
|
||||
[grpc.WithTransportCredentials](https://godoc.org/google.golang.org/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))
|
||||
```
|
||||
244
Documentation/grpc-metadata.md
Normal file
244
Documentation/grpc-metadata.md
Normal file
@@ -0,0 +1,244 @@
|
||||
# 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:
|
||||
|
||||
- [Unary RPC](https://grpc.io/docs/guides/concepts.html#unary-rpc)
|
||||
- [Server streaming RPC](https://grpc.io/docs/guides/concepts.html#server-streaming-rpc)
|
||||
- [Client streaming RPC](https://grpc.io/docs/guides/concepts.html#client-streaming-rpc)
|
||||
- [Bidirectional streaming RPC](https://grpc.io/docs/guides/concepts.html#bidirectional-streaming-rpc)
|
||||
|
||||
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](../examples/features/metadata/client/main.go).
|
||||
|
||||
### 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](../examples/features/metadata/server/main.go).
|
||||
|
||||
### 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](../examples/features/metadata_interceptor/server/main.go).
|
||||
|
||||
[FromIncomingContext]: <https://pkg.go.dev/google.golang.org/grpc/metadata#FromIncomingContext>
|
||||
[SetHeader]: <https://godoc.org/google.golang.org/grpc#SetHeader>
|
||||
[SetTrailer]: https://godoc.org/google.golang.org/grpc#SetTrailer
|
||||
[FromOutgoingContext]: https://pkg.go.dev/google.golang.org/grpc/metadata#FromOutgoingContext
|
||||
[ServerStream]: https://godoc.org/google.golang.org/grpc#ServerStream
|
||||
[grpc]: https://godoc.org/google.golang.org/grpc
|
||||
[ClientStream]: https://godoc.org/google.golang.org/grpc#ClientStream
|
||||
[Header]: https://godoc.org/google.golang.org/grpc#Header
|
||||
[Trailer]: https://godoc.org/google.golang.org/grpc#Trailer
|
||||
[CallOption]: https://godoc.org/google.golang.org/grpc#CallOption
|
||||
[metadata]: https://godoc.org/google.golang.org/grpc/metadata
|
||||
57
Documentation/keepalive.md
Normal file
57
Documentation/keepalive.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# 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](https://godoc.org/google.golang.org/grpc/keepalive) as a [dial
|
||||
option](https://godoc.org/google.golang.org/grpc#WithKeepaliveParams).
|
||||
|
||||
## 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](https://godoc.org/google.golang.org/grpc/keepalive#ServerParameters)
|
||||
for details.
|
||||
|
||||
### Enforcement policy
|
||||
|
||||
[Enforcement
|
||||
policy](https://godoc.org/google.golang.org/grpc/keepalive#EnforcementPolicy) 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
|
||||
49
Documentation/log_levels.md
Normal file
49
Documentation/log_levels.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Log Levels
|
||||
|
||||
This document describes the different log levels supported by the grpc-go
|
||||
library, and under what conditions they should be used.
|
||||
|
||||
### Info
|
||||
|
||||
Info messages are for informational purposes and may aid in the debugging of
|
||||
applications or the gRPC library.
|
||||
|
||||
Examples:
|
||||
- The name resolver received an update.
|
||||
- The balancer updated its picker.
|
||||
- Significant gRPC state is changing.
|
||||
|
||||
At verbosity of 0 (the default), any single info message should not be output
|
||||
more than once every 5 minutes under normal operation.
|
||||
|
||||
### Warning
|
||||
|
||||
Warning messages indicate problems that are non-fatal for the application, but
|
||||
could lead to unexpected behavior or subsequent errors.
|
||||
|
||||
Examples:
|
||||
- Resolver could not resolve target name.
|
||||
- Error received while connecting to a server.
|
||||
- Lost or corrupt connection with remote endpoint.
|
||||
|
||||
### Error
|
||||
|
||||
Error messages represent errors in the usage of gRPC that cannot be returned to
|
||||
the application as errors, or internal gRPC-Go errors that are recoverable.
|
||||
|
||||
Internal errors are detected during gRPC tests and will result in test failures.
|
||||
|
||||
Examples:
|
||||
- Invalid arguments passed to a function that cannot return an error.
|
||||
- An internal error that cannot be returned or would be inappropriate to return
|
||||
to the user.
|
||||
|
||||
### Fatal
|
||||
|
||||
Fatal errors are severe internal errors that are unrecoverable. These lead
|
||||
directly to panics, and are avoided as much as possible.
|
||||
|
||||
Example:
|
||||
- Internal invariant was violated.
|
||||
- User attempted an action that cannot return an error gracefully, but would
|
||||
lead to an invalid state if performed.
|
||||
25
Documentation/proxy.md
Normal file
25
Documentation/proxy.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Proxy
|
||||
|
||||
HTTP CONNECT proxies are supported by default in gRPC. The proxy address can be
|
||||
specified by the environment variables `HTTPS_PROXY` and `NO_PROXY`. (Note that
|
||||
these environment variables are case insensitive.)
|
||||
|
||||
**NOTE**: Using CONNECT proxies via https is not supported. gRPC performs a
|
||||
plaintext CONNECT handshake to establish a tunnel and does not support the
|
||||
additional encryption required to secure the initial connection to the proxy
|
||||
itself.
|
||||
|
||||
When using TLS, the gRPC traffic is encrypted end-to-end between the client and
|
||||
the destination server. Even when using a CONNECT proxy without https, the
|
||||
security is not compromised, as the proxy only sees the destination address and
|
||||
cannot intercept the encrypted gRPC data.
|
||||
|
||||
## Custom proxy
|
||||
|
||||
Currently, proxy support is implemented in the default dialer. It does one more
|
||||
handshake (a CONNECT handshake in the case of HTTP CONNECT proxy) on the
|
||||
connection before giving it to gRPC.
|
||||
|
||||
If the default proxy doesn't work for you, replace the default dialer with your
|
||||
custom proxy dialer. This can be done using
|
||||
[`WithContextDialer`](https://pkg.go.dev/google.golang.org/grpc#WithContextDialer).
|
||||
68
Documentation/rpc-errors.md
Normal file
68
Documentation/rpc-errors.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# RPC Errors
|
||||
|
||||
All service method handlers should return `nil` or errors from the
|
||||
`status.Status` type. Clients have direct access to the errors.
|
||||
|
||||
Upon encountering an error, a gRPC server method handler should create a
|
||||
`status.Status`. In typical usage, one would use [status.New][new-status]
|
||||
passing in an appropriate [codes.Code][code] as well as a description of the
|
||||
error to produce a `status.Status`. Calling [status.Err][status-err] converts
|
||||
the `status.Status` type into an `error`. As a convenience method, there is also
|
||||
[status.Error][status-error] which obviates the conversion step. Compare:
|
||||
|
||||
```
|
||||
st := status.New(codes.NotFound, "some description")
|
||||
err := st.Err()
|
||||
|
||||
// vs.
|
||||
|
||||
err := status.Error(codes.NotFound, "some description")
|
||||
```
|
||||
|
||||
## Adding additional details to errors
|
||||
|
||||
In some cases, it may be necessary to add details for a particular error on the
|
||||
server side. The [status.WithDetails][with-details] method exists for this
|
||||
purpose. Clients may then read those details by first converting the plain
|
||||
`error` type back to a [status.Status][status] and then using
|
||||
[status.Details][details].
|
||||
|
||||
## Example
|
||||
|
||||
The [example][example] demonstrates the API discussed above and shows how to add
|
||||
information about rate limits to the error message using `status.Status`.
|
||||
|
||||
To run the example, first start the server:
|
||||
|
||||
```
|
||||
$ go run examples/rpc_errors/server/main.go
|
||||
```
|
||||
|
||||
In a separate session, run the client:
|
||||
|
||||
```
|
||||
$ go run examples/rpc_errors/client/main.go
|
||||
```
|
||||
|
||||
On the first run of the client, all is well:
|
||||
|
||||
```
|
||||
2018/03/12 19:39:33 Greeting: Hello world
|
||||
```
|
||||
|
||||
Upon running the client a second time, the client exceeds the rate limit and
|
||||
receives an error with details:
|
||||
|
||||
```
|
||||
2018/03/19 16:42:01 Quota failure: violations:<subject:"name:world" description:"Limit one greeting per person" >
|
||||
exit status 1
|
||||
```
|
||||
|
||||
[status]: https://godoc.org/google.golang.org/grpc/status#Status
|
||||
[new-status]: https://godoc.org/google.golang.org/grpc/status#New
|
||||
[code]: https://godoc.org/google.golang.org/grpc/codes#Code
|
||||
[with-details]: https://godoc.org/google.golang.org/grpc/internal/status#Status.WithDetails
|
||||
[details]: https://godoc.org/google.golang.org/grpc/internal/status#Status.Details
|
||||
[status-err]: https://godoc.org/google.golang.org/grpc/internal/status#Status.Err
|
||||
[status-error]: https://godoc.org/google.golang.org/grpc/status#Error
|
||||
[example]: https://github.com/grpc/grpc-go/tree/master/examples/features/error_details
|
||||
171
Documentation/server-reflection-tutorial.md
Normal file
171
Documentation/server-reflection-tutorial.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# gRPC Server Reflection Tutorial
|
||||
|
||||
gRPC Server Reflection provides information about publicly-accessible gRPC
|
||||
services on a server, and assists clients at runtime to construct RPC requests
|
||||
and responses without precompiled service information. It is used by
|
||||
[gRPCurl](https://github.com/fullstorydev/grpcurl), which can be used to
|
||||
introspect server protos and send/receive test RPCs.
|
||||
|
||||
## Enable Server Reflection
|
||||
|
||||
gRPC-go Server Reflection is implemented in package
|
||||
[reflection](https://github.com/grpc/grpc-go/tree/master/reflection). To enable
|
||||
server reflection, you need to import this package and register reflection
|
||||
service on your gRPC server.
|
||||
|
||||
For example, to enable server reflection in `example/helloworld`, we need to
|
||||
make the following changes:
|
||||
|
||||
```diff
|
||||
--- a/examples/helloworld/greeter_server/main.go
|
||||
+++ b/examples/helloworld/greeter_server/main.go
|
||||
@@ -40,6 +40,7 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
pb "google.golang.org/grpc/examples/helloworld/helloworld"
|
||||
+ "google.golang.org/grpc/reflection"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -61,6 +62,8 @@ func main() {
|
||||
}
|
||||
s := grpc.NewServer()
|
||||
pb.RegisterGreeterService(s, &pb.GreeterService{SayHello: sayHello})
|
||||
+ // Register reflection service on gRPC server.
|
||||
+ reflection.Register(s)
|
||||
if err := s.Serve(lis); err != nil {
|
||||
log.Fatalf("failed to serve: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
An example server with reflection registered can be found at
|
||||
`examples/features/reflection/server`.
|
||||
|
||||
## gRPCurl
|
||||
|
||||
After enabling Server Reflection in a server application, you can use gRPCurl
|
||||
to check its services. gRPCurl is built with Go and has packages available.
|
||||
Instructions on how to install and use gRPCurl can be found at
|
||||
[gRPCurl Installation](https://github.com/fullstorydev/grpcurl#installation).
|
||||
|
||||
## Use gRPCurl to check services
|
||||
|
||||
First, start the helloworld server in grpc-go directory:
|
||||
|
||||
```sh
|
||||
$ cd <grpc-go-directory>/examples
|
||||
$ go run features/reflection/server/main.go
|
||||
```
|
||||
|
||||
output:
|
||||
```sh
|
||||
server listening at [::]:50051
|
||||
```
|
||||
|
||||
After installing gRPCurl, open a new terminal and run the commands from the new
|
||||
terminal.
|
||||
|
||||
**NOTE:** gRPCurl expects a TLS-encrypted connection by default. For all of
|
||||
the commands below, use the `-plaintext` flag to use an unencrypted connection.
|
||||
|
||||
### List services and methods
|
||||
|
||||
The `list` command lists services exposed at a given port:
|
||||
|
||||
- List all the services exposed at a given port
|
||||
|
||||
```sh
|
||||
$ grpcurl -plaintext localhost:50051 list
|
||||
```
|
||||
|
||||
output:
|
||||
```sh
|
||||
grpc.examples.echo.Echo
|
||||
grpc.reflection.v1alpha.ServerReflection
|
||||
helloworld.Greeter
|
||||
```
|
||||
|
||||
- List all the methods of a service
|
||||
|
||||
The `list` command lists methods given the full service name (in the format of
|
||||
\<package\>.\<service\>).
|
||||
|
||||
```sh
|
||||
$ grpcurl -plaintext localhost:50051 list helloworld.Greeter
|
||||
```
|
||||
|
||||
output:
|
||||
```sh
|
||||
helloworld.Greeter.SayHello
|
||||
```
|
||||
|
||||
### Describe services and methods
|
||||
|
||||
- Describe all services
|
||||
|
||||
The `describe` command inspects a service given its full name (in the format
|
||||
of \<package\>.\<service\>).
|
||||
|
||||
```sh
|
||||
$ grpcurl -plaintext localhost:50051 describe helloworld.Greeter
|
||||
```
|
||||
|
||||
output:
|
||||
```sh
|
||||
helloworld.Greeter is a service:
|
||||
service Greeter {
|
||||
rpc SayHello ( .helloworld.HelloRequest ) returns ( .helloworld.HelloReply );
|
||||
}
|
||||
```
|
||||
|
||||
- Describe all methods of a service
|
||||
|
||||
The `describe` command inspects a method given its full name (in the format of
|
||||
\<package\>.\<service\>.\<method\>).
|
||||
|
||||
```sh
|
||||
$ grpcurl -plaintext localhost:50051 describe helloworld.Greeter.SayHello
|
||||
```
|
||||
|
||||
output:
|
||||
```sh
|
||||
helloworld.Greeter.SayHello is a method:
|
||||
rpc SayHello ( .helloworld.HelloRequest ) returns ( .helloworld.HelloReply );
|
||||
```
|
||||
|
||||
### Inspect message types
|
||||
|
||||
We can use the `describe` command to inspect request/response types given the
|
||||
full name of the type (in the format of \<package\>.\<type\>).
|
||||
|
||||
- Get information about the request type
|
||||
|
||||
```sh
|
||||
$ grpcurl -plaintext localhost:50051 describe helloworld.HelloRequest
|
||||
```
|
||||
|
||||
output:
|
||||
```sh
|
||||
helloworld.HelloRequest is a message:
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
}
|
||||
```
|
||||
|
||||
### Call a remote method
|
||||
|
||||
We can send RPCs to a server and get responses using the full method name (in
|
||||
the format of \<package\>.\<service\>.\<method\>). The `-d <string>` flag
|
||||
represents the request data and the `-format text` flag indicates that the
|
||||
request data is in text format.
|
||||
|
||||
- Call a unary method
|
||||
|
||||
```sh
|
||||
$ grpcurl -plaintext -format text -d 'name: "gRPCurl"' \
|
||||
localhost:50051 helloworld.Greeter.SayHello
|
||||
```
|
||||
|
||||
output:
|
||||
```sh
|
||||
message: "Hello gRPCurl"
|
||||
```
|
||||
34
Documentation/versioning.md
Normal file
34
Documentation/versioning.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Versioning and Releases
|
||||
|
||||
Note: This document references terminology defined at http://semver.org.
|
||||
|
||||
## Release Frequency
|
||||
|
||||
Regular MINOR releases of gRPC-Go are performed every six weeks. Patch releases
|
||||
to the previous two MINOR releases may be performed on demand or if serious
|
||||
security problems are discovered.
|
||||
|
||||
## Versioning Policy
|
||||
|
||||
The gRPC-Go versioning policy follows the Semantic Versioning 2.0.0
|
||||
specification, with the following exceptions:
|
||||
|
||||
- A MINOR version will not _necessarily_ add new functionality.
|
||||
|
||||
- MINOR releases will not break backward compatibility, except in the following
|
||||
circumstances:
|
||||
|
||||
- An API was marked as EXPERIMENTAL upon its introduction.
|
||||
- An API was marked as DEPRECATED in the initial MAJOR release.
|
||||
- An API is inherently flawed and cannot provide correct or secure behavior.
|
||||
|
||||
In these cases, APIs MAY be changed or removed without a MAJOR release.
|
||||
Otherwise, backward compatibility will be preserved by MINOR releases.
|
||||
|
||||
For an API marked as DEPRECATED, an alternative will be available (if
|
||||
appropriate) for at least three months prior to its removal.
|
||||
|
||||
## Release History
|
||||
|
||||
Please see our release history on GitHub:
|
||||
https://github.com/grpc/grpc-go/releases
|
||||
Reference in New Issue
Block a user