Skip to content

gRPC Server Example

go-zero provides a gRPC server that provides:

  1. Service discovery capability (etcd as registration centre)
  2. Load Balancer(p2c algorithms)
  3. Node Affinity
  4. Multi-node direct connection mode
  5. Timeout processing
  6. Traffic limiting, breaking
  7. Authentication capacity
  8. Exception Capture

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”
Terminal window
$ mkdir demo && cd demo
$ go mod init demo
$ goctl rpc -o greet.proto
Terminal window
$ goctl rpc protoc greet.proto --go_out=. --go-grpc_out=. --zrpc_out=.

:::tip Tips

  1. For goctl installation, see Goctl Installation.
  2. For the rpc code generation command, see goctl rpc.
  3. For proto-related questions, see Proto Code Generating FAQ. :::
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. :::

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:

demo/etc/greet.yaml
Name: greet.rpc
ListenOn: 0.0.0.0:8080
Etcd:
Hosts:
- 127.0.0.1:2379
Key: greet.rpc

The service is registered with the key greet.rpc, which we can see in etcd by the following method:

Terminal window
$ etcdctl get --prefix greet.rpc
greet.rpc/7587870460981677828
192.168.72.53:8080

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

demo/etc/greet.yaml
Name: greet.rpc
ListenOn: 0.0.0.0:8080

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:

demo/internal/server/greetserver.go
// 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)
}

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

demo/internal/logic/pinglogic.go
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
}

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.rpc
ListenOn: 0.0.0.0:8080
Mode: dev
Etcd:
Hosts:
- 127.0.0.1:2379
Key: greet.rpc

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.

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)
}

See gRPC Metadata for reference.