gRPC Server Example
Overview
Section titled “Overview”go-zero provides a gRPC server that provides:
- Service discovery capability (etcd as registration centre)
- Load Balancer(p2c algorithms)
- Node Affinity
- Multi-node direct connection mode
- Timeout processing
- Traffic limiting, breaking
- Authentication capacity
- Exception Capture
Examples
Section titled “Examples”In go-zero, we can use goctl to quickly scaffold a gRPC service or create a gRPC service example using goctl’s zero-code-generation feature.
:::tip Tips Quickly generate and start a goctl gRPC service example — see Quick Start Microservice. :::
We’re here to create a full gRPC service with a proto.
1. Create a service directory and initialize the go module project
Section titled “1. Create a service directory and initialize the go module project”$ mkdir demo && cd demo$ go mod init demo2. Quickly generate a proto file
Section titled “2. Quickly generate a proto file”$ goctl rpc -o greet.proto3. Proto generate gRPC services
Section titled “3. Proto generate gRPC services”$ goctl rpc protoc greet.proto --go_out=. --go-grpc_out=. --zrpc_out=.:::tip Tips
- For goctl installation, see Goctl Installation.
- For the rpc code generation command, see goctl rpc.
- For proto-related questions, see Proto Code Generating FAQ. :::
4. Layout
Section titled “4. Layout”demo├── etc│ └── greet.yaml├── go.mod├── greet│ ├── greet.pb.go│ └── greet_grpc.pb.go├── greet.go├── greet.proto├── greetclient│ └── greet.go└── internal ├── config │ └── config.go ├── logic │ └── pinglogic.go ├── server │ └── greetserver.go └── svc └── servicecontext.go
8 directories, 11 files:::tip hint For the service directory structure, see Project Structure. :::
5. Discovery/direct service mode
Section titled “5. Discovery/direct service mode”In go-zero we support the etcd service registration and direct connection mode and we only adjust the static configuration files in the etc directory.
:::tip hint For gRPC service configuration, see gRPC Service Configuration.
Beyond the built-in etcd support, the community provides adapters for nacos, consul, and more. See zero-contrib service registries for details. :::
etcd Service Registration To use etcd as a registry, simply add the etcd configuration to the static configuration file:
Name: greet.rpcListenOn: 0.0.0.0:8080Etcd: Hosts: - 127.0.0.1:2379 Key: greet.rpcThe service is registered with the key greet.rpc, which we can see in etcd by the following method:
$ etcdctl get --prefix greet.rpcgreet.rpc/7587870460981677828192.168.72.53:8080Since the key registered by etcd is greet.rpc, from the business presentation layer, it is a key registered to etcd, but go-zero is actually storing the key with an etcd
The go-zero layer is actually storing the key with a tenant id of etcd, so during service discovery, it will also fetch all available ip nodes with the etcdctl get --prefix command.
Direct Connection Mode By contrast, using direct connection mode removes the etcd configuration. go-zero auto-detects this and uses direct connections instead. Minimum configuration:
Name: greet.rpcListenOn: 0.0.0.0:8080Stubi implementation
Section titled “Stubi implementation”The code generated by goctl does not require the user to implement the stub. The goctl tool will help you to implement all of this, referencing the following:
// Code generated by goctl. DO NOT EDIT.// Source: greet.proto
package server
import ( "context"
"demo/greet" "demo/internal/logic" "demo/internal/svc")
type GreetServer struct { svcCtx *svc.ServiceContext greet.UnimplementedGreetServer}
func NewGreetServer(svcCtx *svc.ServiceContext) *GreetServer { return &GreetServer{ svcCtx: svcCtx, }}
func (s *GreetServer) Ping(ctx context.Context, in *greet.Request) (*greet.Response, error) { l := logic.NewPingLogic(ctx, s.svcCtx) return l.Ping(in)}Writing business code
Section titled “Writing business code”Once the code is generated with goctl, we simply need to fill in our business code in the log file, reference business code (grey bottom texture part):
package logic
import ( "context"
"demo/greet" "demo/internal/svc"
"github.com/zeromicro/go-zero/core/logx")
type PingLogic struct { ctx context.Context svcCtx *svc.ServiceContext logx.Logger}
func NewPingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PingLogic { return &PingLogic{ ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx), }}
func (l *PingLogic) Ping(in *greet.Request) (*greet.Response, error) { return &greet.Response{ Pong: "pong", }, nil}8. Enable gRPC debug switch
Section titled “8. Enable gRPC debug switch”gRPC provides debugging capabilities so that we can debug with tools like grpcurl. In go-zero, it is recommended to turn it on in development and test environments, and off in pre-production and official environments,So we configure the environment mode in the static configuration file as dev or test (default is dev environment), the relevant code is as follows:
demo/greet.go
package main...func main() { flag.Parse()
var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { greet.RegisterGreetServer(grpcServer, server.NewGreetServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode { reflection.Register(grpcServer) } }) ...}demo/etc/greet.yaml
Name: greet.rpcListenOn: 0.0.0.0:8080Mode: devEtcd: Hosts: - 127.0.0.1:2379 Key: greet.rpc9. Middleware Usage
Section titled “9. Middleware Usage”Built Middleware
Section titled “Built Middleware”go-zero rpc ships with a rich set of built-in interceptors. See serverinterceptors for the full list.
- StreamAuthorizeInterceptor|UnaryAuthorizeInterceptor
- StreamBreakerInterceptor|UnaryBreakerInterceptor
- UnaryPrometheusInterceptor
- StreamRecoverInterceptor|UnaryRecoverInterceptor
- UnarySheddingInterceptor
- UnaryStatInterceptor
- UnaryTimeoutInterceptor
- StreamTraceInterceptor|UnaryTraceInterceptor
In the above built-in interceptors, trace, metrics, stats, recovery, and circuit-breaker interceptors can be toggled individually; others are always enabled. For configuration details, see service configuration.
Custom Middleware
Section titled “Custom Middleware”package main...var configFile = flag.String("f", "etc/greet.yaml", "the config file")
func main() { flag.Parse()
var c config.Config conf.MustLoad(*configFile, &c) ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) { greet.RegisterGreetServer(grpcServer, server.NewGreetServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode { reflection.Register(grpcServer) } }) defer s.Stop()
s.AddUnaryInterceptors(exampleUnaryInterceptor) s.AddStreamInterceptors(exampleStreamInterceptor)
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn) s.Start()}
func exampleUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { // TODO: fill your logic here return handler(ctx, req)}func exampleStreamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { // TODO: fill your logic here return handler(srv, ss)}10. Metadata transfer
Section titled “10. Metadata transfer”See gRPC Metadata for reference.