gRPC 服务端示例
go-zero 提供了 gRPC server 能力,其提供了:
- 服务发现能力(etcd 作为注册中心)
- 负载均衡能力(p2c 算法)
- 节点亲和性处理
- 多节点直连支持
- 超时处理
- 限流,熔断能力
- 鉴权能力
- 异常捕获
在 go-zero 中,我们可以使用 goctl 来快速生成一个 gRPC 服务,也可以通过 goctl 0 代码生成一个 gRPC 服务示例。
:::tip 温馨提示 通过 goctl 快速生成并启动一个 gRPC 服务示例可以参考 《快速开始•微服务篇》 :::
我们这里以一个 proto 来创建一个完整的 gRPC 服务。
1. 创建服务目录,初始化 go module 工程
Section titled “1. 创建服务目录,初始化 go module 工程”$ mkdir demo && cd demo$ go mod init demo2. 快速生成一个 proto 文件
Section titled “2. 快速生成一个 proto 文件”$ goctl rpc -o greet.proto3. 根据 proto 生成 gRPC 服务
Section titled “3. 根据 proto 生成 gRPC 服务”$ goctl rpc protoc greet.proto --go_out=. --go-grpc_out=. --zrpc_out=.:::tip 温馨提示
- goctl 安装请参考 《goctl 安装》
- rpc 代码生成指令教程请参考 《goctl rpc》
- proto 使用相关问题请参考 《proto 代码生成常见问题》 :::
4. 参考目录结构
Section titled “4. 参考目录结构”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 温馨提示 服务目录结构介绍请参考 《项目结构》 :::
5. 服务发现/直连模式
Section titled “5. 服务发现/直连模式”在 go-zero 中,支持 etcd 服务注册和直连模式,我们仅对 etc 目录下的静态配置文件稍作调整即可。
:::tip 温馨提示 gRPC 服务配置可参考 《gRPC 服务配置》
除了 go-zero rpc 内置的 ectd 作为服务注册组件外,社区还提供了对 nacos,consul 等的服注册支持,详情可参考 更多服务注册组件 :::
etcd 服务注册 使用 etcd 作为注册中心,只需要在静态配置文件中添加 etcd 配置即可,最简参考配置如下(灰色底纹部分):
Name: greet.rpcListenOn: 0.0.0.0:8080Etcd: Hosts: - 127.0.0.1:2379 Key: greet.rpc服务注册的 key 为 greet.rpc,我们可以在 etcd 中通过一下方法查看到:
$ etcdctl get --prefix greet.rpcgreet.rpc/7587870460981677828192.168.72.53:8080由于 etcd 注册的 key 都是 greet.rpc, 从业务表现层来看,是将一个 key 注册到了 etcd,实则 go-zero 底层是将该 key 拼上了一个 etcd
的租户 id 来存储到 etcd 的,因此,在服务发现时,也会通过 etcdctl get --prefix 指令去获取所有可用的 ip 节点。
直连模式 相反,使用直连模式则去除 etcd 配置即可,go-zero 自动识别,最简配置参考:
Name: greet.rpcListenOn: 0.0.0.0:80806. stub 实现
Section titled “6. stub 实现”通过 goctl 生成的代码不需要用户手动去实现 stub 方法了,goctl 工具会帮你全部实现掉,参考代码如下:
// 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)}7. 编写业务代码
Section titled “7. 编写业务代码”通过 goctl 生成代码后,我们只需要在 logic 文件中填写我们的业务代码即可,参考业务代码(灰色底纹部分):
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. 开启 gRPC 调试开关
Section titled “8. 开启 gRPC 调试开关”gRPC 提供了调试功能,以便于我们可以通过 grpcurl 等工具进行调试,
在 go-zero,建议在开发环境和测试环境开启,预生产环境和正式环境建议关闭,因此我们在静态配置文件中将环境模式配置为 dev 或者 test 时才会开启(默认为 dev 环境),相关代码如下:
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. 中间件使用
Section titled “9. 中间件使用”go-zero rpc 内置了非常丰富的中间件,详情可查看serverinterceptors
- 鉴权中间件:StreamAuthorizeInterceptor|UnaryAuthorizeInterceptor
- 熔断中间件:StreamBreakerInterceptor|UnaryBreakerInterceptor
- 指标统计中间件: UnaryPrometheusInterceptor
- 异常捕获中间件:StreamRecoverInterceptor|UnaryRecoverInterceptor
- 服务降载中间件:UnarySheddingInterceptor
- 时长统计中间件:UnaryStatInterceptor
- 超时控制中间件:UnaryTimeoutInterceptor
- 链路追踪中间件:StreamTraceInterceptor|UnaryTraceInterceptor
在以上内置中间件中,链路追踪中间件、指标统计中间件、时长统计中间件、异常捕获中间件、熔断中间件可以通过配置来开启或关闭,其他中间件默认开启。 具体配置可参考服务配置
自定义中间件
Section titled “自定义中间件”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 传值
Section titled “10. metadata 传值”参考 《Metadata》