服务示例

概述

go-zero 提供了 gRPC server 能力,其提供了:

  1. 服务发现能力(etcd 作为注册中心)
  2. 负载均衡能力(p2c 算法)
  3. 节点亲和性处理
  4. 多节点直连支持
  5. 超时处理
  6. 限流,熔断能力
  7. 鉴权能力
  8. 异常捕获

使用示例

在 go-zero 中,我们可以使用 goctl 来快速生成一个 gRPC 服务,也可以通过 goctl 0 代码生成一个 gRPC 服务示例。

服务示例 - 图1温馨提示

通过 goctl 快速生成并启动一个 gRPC 服务示例可以参考 《快速开始•微服务篇》

我们这里以一个 proto 来创建一个完整的 gRPC 服务。

1. 创建服务目录,初始化 go module 工程

  1. $ mkdir demo && cd demo
  2. $ go mod init demo

2. 快速生成一个 proto 文件

  1. $ goctl rpc -o greet.proto

3. 根据 proto 生成 gRPC 服务

  1. $ goctl rpc protoc greet.proto --go_out=. --go-grpc_out=. --zrpc_out=.
服务示例 - 图2温馨提示
  1. goctl 安装请参考 《goctl 安装》
  2. rpc 代码生成指令教程请参考 《goctl rpc》
  3. proto 使用相关问题请参考 《proto 代码生成常见问题》

4. 参考目录结构

  1. demo
  2. ├── etc
  3. └── greet.yaml
  4. ├── go.mod
  5. ├── greet
  6. ├── greet.pb.go
  7. └── greet_grpc.pb.go
  8. ├── greet.go
  9. ├── greet.proto
  10. ├── greetclient
  11. └── greet.go
  12. └── internal
  13. ├── config
  14. └── config.go
  15. ├── logic
  16. └── pinglogic.go
  17. ├── server
  18. └── greetserver.go
  19. └── svc
  20. └── servicecontext.go
  21. 8 directories, 11 files
服务示例 - 图3温馨提示

服务目录结构介绍请参考 《项目结构》

5. 服务发现/直连模式

在 go-zero 中,支持 etcd 服务注册和直连模式,我们仅对 etc 目录下的静态配置文件稍作调整即可。

服务示例 - 图4温馨提示

gRPC 服务配置可参考 《gRPC 服务配置》

除了 go-zero rpc 内置的 ectd 作为服务注册组件外,社区还提供了对 nacos,consul 等的服注册支持,详情可参考 更多服务注册组件

  • etcd 服务注册
  • 直连模式

使用 etcd作为注册中心,只需要在静态配置文件中添加 etcd 配置即可,最简参考配置如下(灰色底纹部分):

demo/etc/greet.yaml

  1. Name: greet.rpc
  2. ListenOn: 0.0.0.0:8080
  3. Etcd:
  4. Hosts:
  5. - 127.0.0.1:2379
  6. Key: greet.rpc

服务注册的 key 为 greet.rpc,我们可以在 etcd 中通过一下方法查看到:

  1. $ etcdctl get --prefix greet.rpc
  2. greet.rpc/7587870460981677828
  3. 192.168.72.53:8080

由于 etcd 注册的 key 都是 greet.rpc, 从业务表现层来看,是将一个 key 注册到了 etcd,实则 go-zero 底层是将该 key 拼上了一个 etcd 的租户 id 来存储到 etcd 的,因此,在服务发现时,也会通过 etcdctl get --prefix 指令去获取所有可用的 ip 节点。

相反,使用直连模式则去除 etcd 配置即可,go-zero 自动识别,最简配置参考:

demo/etc/greet.yaml

  1. Name: greet.rpc
  2. ListenOn: 0.0.0.0:8080

6. stub 实现

通过 goctl 生成的代码不需要用户手动去实现 stub 方法了,goctl 工具会帮你全部实现掉,参考代码如下:

demo/internal/server/greetserver.go

  1. // Code generated by goctl. DO NOT EDIT.
  2. // Source: greet.proto
  3. package server
  4. import (
  5. "context"
  6. "demo/greet"
  7. "demo/internal/logic"
  8. "demo/internal/svc"
  9. )
  10. type GreetServer struct {
  11. svcCtx *svc.ServiceContext
  12. greet.UnimplementedGreetServer
  13. }
  14. func NewGreetServer(svcCtx *svc.ServiceContext) *GreetServer {
  15. return &GreetServer{
  16. svcCtx: svcCtx,
  17. }
  18. }
  19. func (s *GreetServer) Ping(ctx context.Context, in *greet.Request) (*greet.Response, error) {
  20. l := logic.NewPingLogic(ctx, s.svcCtx)
  21. return l.Ping(in)
  22. }

7. 编写业务代码

通过 goctl 生成代码后,我们只需要在 logic 文件中填写我们的业务代码即可,参考业务代码(灰色底纹部分):

demo/internal/logic/pinglogic.go

  1. package logic
  2. import (
  3. "context"
  4. "demo/greet"
  5. "demo/internal/svc"
  6. "github.com/zeromicro/go-zero/core/logx"
  7. )
  8. type PingLogic struct {
  9. ctx context.Context
  10. svcCtx *svc.ServiceContext
  11. logx.Logger
  12. }
  13. func NewPingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *PingLogic {
  14. return &PingLogic{
  15. ctx: ctx,
  16. svcCtx: svcCtx,
  17. Logger: logx.WithContext(ctx),
  18. }
  19. }
  20. func (l *PingLogic) Ping(in *greet.Request) (*greet.Response, error) {
  21. return &greet.Response{
  22. Pong: "pong",
  23. }, nil
  24. }

8. 开启 gRPC 调试开关

gRPC 提供了调试功能,以便于我们可以通过 grpcurl 等工具进行调试, 在 go-zero,建议在开发环境和测试环境开启,预生产环境和正式环境建议关闭,因此我们在静态配置文件中将环境模式配置为 dev 或者 test 时才会开启(默认为 dev 环境),相关代码如下:

  • demo/greet.go
  • demo/etc/greet.yaml
  1. package main
  2. ...
  3. func main() {
  4. flag.Parse()
  5. var c config.Config
  6. conf.MustLoad(*configFile, &c)
  7. ctx := svc.NewServiceContext(c)
  8. s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
  9. greet.RegisterGreetServer(grpcServer, server.NewGreetServer(ctx))
  10. if c.Mode == service.DevMode || c.Mode == service.TestMode {
  11. reflection.Register(grpcServer)
  12. }
  13. })
  14. ...
  15. }
  1. Name: greet.rpc
  2. ListenOn: 0.0.0.0:8080
  3. Mode: dev
  4. Etcd:
  5. Hosts:
  6. - 127.0.0.1:2379
  7. Key: greet.rpc

9. 中间件使用

内置中间件

go-zero rpc 内置了非常丰富的中间件,详情可查看serverinterceptors

  • 鉴权中间件:StreamAuthorizeInterceptor|UnaryAuthorizeInterceptor
  • 熔断中间件:StreamBreakerInterceptor|UnaryBreakerInterceptor
  • 指标统计中间件: UnaryPrometheusInterceptor
  • 异常捕获中间件:StreamRecoverInterceptor|UnaryRecoverInterceptor
  • 服务降载中间件:UnarySheddingInterceptor
  • 时长统计中间件:UnaryStatInterceptor
  • 超时控制中间件:UnaryTimeoutInterceptor
  • 链路追踪中间件:StreamTraceInterceptor|UnaryTraceInterceptor

在以上内置中间件中,链路追踪中间件、指标统计中间件、时长统计中间件、异常捕获中间件、熔断中间件可以通过配置来开启或关闭,其他中间件默认开启。 具体配置可参考服务配置

自定义中间件

  1. package main
  2. ...
  3. var configFile = flag.String("f", "etc/greet.yaml", "the config file")
  4. func main() {
  5. flag.Parse()
  6. var c config.Config
  7. conf.MustLoad(*configFile, &c)
  8. ctx := svc.NewServiceContext(c)
  9. s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
  10. greet.RegisterGreetServer(grpcServer, server.NewGreetServer(ctx))
  11. if c.Mode == service.DevMode || c.Mode == service.TestMode {
  12. reflection.Register(grpcServer)
  13. }
  14. })
  15. defer s.Stop()
  16. s.AddUnaryInterceptors(exampleUnaryInterceptor)
  17. s.AddStreamInterceptors(exampleStreamInterceptor)
  18. fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
  19. s.Start()
  20. }
  21. func exampleUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
  22. // TODO: fill your logic here
  23. return handler(ctx, req)
  24. }
  25. func exampleStreamInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
  26. // TODO: fill your logic here
  27. return handler(srv, ss)
  28. }

10. metadata 传值

参考 《Metadata》