|
| 1 | +# gRPC Server Module |
| 2 | + |
| 3 | +[](https://github.com/ankorstore/yokai/actions/workflows/grpcserver-ci.yml) |
| 4 | +[](https://goreportcard.com/report/github.com/ankorstore/yokai/grpcserver) |
| 5 | +[](https://app.codecov.io/gh/ankorstore/yokai/tree/main/grpcserver) |
| 6 | +[](https://deps.dev/go/github.com%2Fankorstore%2Fyokai%2Fgrpcserver) |
| 7 | +[](https://pkg.go.dev/github.com/ankorstore/yokai/grpcserver) |
| 8 | + |
| 9 | +> gRPC server module based on [gRPC-Go](https://github.com/grpc/grpc-go). |
| 10 | +
|
| 11 | +<!-- TOC --> |
| 12 | + |
| 13 | +* [Installation](#installation) |
| 14 | +* [Documentation](#documentation) |
| 15 | + * [Usage](#usage) |
| 16 | + * [Add-ons](#add-ons) |
| 17 | + * [Reflection](#reflection) |
| 18 | + * [Panic recovery](#panic-recovery) |
| 19 | + * [Logger interceptor](#logger-interceptor) |
| 20 | + * [Healthcheck service](#healthcheck-service) |
| 21 | + |
| 22 | +<!-- TOC --> |
| 23 | + |
| 24 | +## Installation |
| 25 | + |
| 26 | +```shell |
| 27 | +go get github.com/ankorstore/yokai/grpcserver |
| 28 | +``` |
| 29 | + |
| 30 | +## Documentation |
| 31 | + |
| 32 | +### Usage |
| 33 | + |
| 34 | +This module provides a [GrpcServerFactory](factory.go), allowing to build an `grpc.Server` instance. |
| 35 | + |
| 36 | +```go |
| 37 | +package main |
| 38 | + |
| 39 | +import ( |
| 40 | + "github.com/ankorstore/yokai/grpcserver" |
| 41 | + "google.golang.org/grpc" |
| 42 | +) |
| 43 | + |
| 44 | +var server, _ = grpcserver.NewDefaultGrpcServerFactory().Create() |
| 45 | + |
| 46 | +// equivalent to: |
| 47 | +var server, _ = grpcserver.NewDefaultGrpcServerFactory().Create( |
| 48 | + grpcserver.WithServerOptions([]grpc.ServerOption{}), // no specific server options by default |
| 49 | + grpcserver.WithReflection(false), // reflection disabled by default |
| 50 | +) |
| 51 | +``` |
| 52 | + |
| 53 | +See [gRPC-Go documentation](https://github.com/grpc/grpc-go) for more details. |
| 54 | + |
| 55 | +### Add-ons |
| 56 | + |
| 57 | +This module provides several add-ons ready to use to enrich your gRPC server. |
| 58 | + |
| 59 | +#### Reflection |
| 60 | + |
| 61 | +This module provides the possibility to easily |
| 62 | +enable [gRPC server reflection](https://github.com/grpc/grpc/blob/master/doc/server-reflection.md), disabled by default: |
| 63 | + |
| 64 | +```go |
| 65 | +package main |
| 66 | + |
| 67 | +import ( |
| 68 | + "github.com/ankorstore/yokai/grpcserver" |
| 69 | +) |
| 70 | + |
| 71 | +func main() { |
| 72 | + server, _ := grpcserver.NewDefaultGrpcServerFactory().Create( |
| 73 | + grpcserver.WithReflection(true), |
| 74 | + ) |
| 75 | +} |
| 76 | +``` |
| 77 | + |
| 78 | +Reflection usage is helpful for developing or testing your gRPC services, but it is not recommended for production |
| 79 | +usage. |
| 80 | + |
| 81 | +#### Panic recovery |
| 82 | + |
| 83 | +This module provides an [GrpcPanicRecoveryHandler](panic.go), compatible with |
| 84 | +the [recovery interceptor](https://github.com/grpc-ecosystem/go-grpc-middleware/tree/main/interceptors/recovery), to |
| 85 | +automatically recover from panics in your gRPC services: |
| 86 | + |
| 87 | +```go |
| 88 | +package main |
| 89 | + |
| 90 | +import ( |
| 91 | + "github.com/ankorstore/yokai/grpcserver" |
| 92 | + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/recovery" |
| 93 | + "google.golang.org/grpc" |
| 94 | +) |
| 95 | + |
| 96 | +func main() { |
| 97 | + handler := grpcserver.NewGrpcPanicRecoveryHandler() |
| 98 | + |
| 99 | + server, _ := grpcserver.NewDefaultGrpcServerFactory().Create( |
| 100 | + grpcserver.WithServerOptions( |
| 101 | + grpc.UnaryInterceptor(recovery.UnaryServerInterceptor(recovery.WithRecoveryHandlerContext(handler.Handle(false)))), |
| 102 | + grpc.StreamInterceptor(recovery.StreamServerInterceptor(recovery.WithRecoveryHandlerContext(handler.Handle(false)))), |
| 103 | + ), |
| 104 | + ) |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | +You can also use `Handle(true)` to append on the handler gRPC response and logs more information about the panic and the debug stack ( |
| 109 | +not suitable for production). |
| 110 | + |
| 111 | +#### Logger interceptor |
| 112 | + |
| 113 | +This module provides a [GrpcLoggerInterceptor](logger.go) to automatically log unary and streaming RPCs calls (status, |
| 114 | +type, duration, etc.), compatible with the [log module](https://github.com/ankorstore/yokai/tree/main/log). |
| 115 | + |
| 116 | +Using this interceptor will also provide a logger instance in the context, that you can retrieve with |
| 117 | +the [CtxLogger](context.go) method to produce correlated logs from your gRPC services. |
| 118 | + |
| 119 | +```go |
| 120 | +package main |
| 121 | + |
| 122 | +import ( |
| 123 | + "github.com/ankorstore/yokai/generate/uuid" |
| 124 | + "github.com/ankorstore/yokai/grpcserver" |
| 125 | + "github.com/ankorstore/yokai/log" |
| 126 | + "google.golang.org/grpc" |
| 127 | +) |
| 128 | + |
| 129 | +func main() { |
| 130 | + logger, _ := log.NewDefaultLoggerFactory().Create() |
| 131 | + |
| 132 | + loggerInterceptor := grpcserver.NewGrpcLoggerInterceptor(uuid.NewDefaultUuidGenerator, logger) |
| 133 | + |
| 134 | + server, _ := grpcserver.NewDefaultGrpcServerFactory().Create( |
| 135 | + grpcserver.WithServerOptions( |
| 136 | + grpc.UnaryInterceptor(loggerInterceptor.UnaryInterceptor()), |
| 137 | + grpc.StreamInterceptor(loggerInterceptor.StreamInterceptor()), |
| 138 | + ), |
| 139 | + ) |
| 140 | +} |
| 141 | +``` |
| 142 | + |
| 143 | +The interceptor will automatically enrich each log records with the `x-request-id` fetch from the context metadata in |
| 144 | +the field `requestID`. |
| 145 | + |
| 146 | +You can specify additional metadata to add to logs records: |
| 147 | + |
| 148 | +- the key is the metadata name to fetch |
| 149 | +- the value is the log field to fill |
| 150 | + |
| 151 | +```go |
| 152 | +loggerInterceptor.Metadata( |
| 153 | + map[string]string{ |
| 154 | + "x-some-metadata": "someMetadata", |
| 155 | + "x-other-metadata": "otherMetadata", |
| 156 | + }, |
| 157 | +) |
| 158 | +``` |
| 159 | + |
| 160 | +You can also specify a list of gRPC methods to exclude from logging: |
| 161 | + |
| 162 | +```go |
| 163 | +loggerInterceptor.Exlude("/test.Service/Unary", "/test.Service/Bidi") |
| 164 | +``` |
| 165 | + |
| 166 | +Note: even if excluded, failing gRPC methods calls will still be logged for observability purposes. |
| 167 | + |
| 168 | +#### Healthcheck service |
| 169 | + |
| 170 | +This module provides a [GrpcHealthCheckService](healthcheck.go), compatible with |
| 171 | +the [healthcheck module](https://github.com/ankorstore/yokai/tree/main/healthcheck): |
| 172 | + |
| 173 | +```go |
| 174 | +package main |
| 175 | + |
| 176 | +import ( |
| 177 | + "probes" |
| 178 | + |
| 179 | + "github.com/ankorstore/yokai/grpcserver" |
| 180 | + "github.com/ankorstore/yokai/healthcheck" |
| 181 | + "google.golang.org/grpc/health/grpc_health_v1" |
| 182 | +) |
| 183 | + |
| 184 | +func main() { |
| 185 | + checker, _ := healthcheck.NewDefaultCheckerFactory().Create( |
| 186 | + healthcheck.WithProbe(probes.SomeProbe()), // register for startup, liveness and readiness |
| 187 | + healthcheck.WithProbe(probes.SomeOtherProbe(), healthcheck.Liveness), // register for liveness only |
| 188 | + ) |
| 189 | + |
| 190 | + server, _ := grpcserver.NewDefaultGrpcServerFactory().Create() |
| 191 | + |
| 192 | + grpc_health_v1.RegisterHealthServer(server, grpcserver.NewGrpcHealthCheckService(checker)) |
| 193 | +} |
| 194 | +``` |
| 195 | + |
| 196 | +This will expose the [Check and Watch](https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto) RPCs, suitable for [k8s startup, readiness or liveness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). |
| 197 | + |
| 198 | +The checker will: |
| 199 | + |
| 200 | +- run the `liveness` probes checks if the request service name contains `liveness` (like `kubernetes::liveness`) |
| 201 | +- or run the `readiness` probes checks if the request service name contains `readiness` (like `kubernetes::readiness`) |
| 202 | +- or run the `startup` probes checks otherwise |
0 commit comments