go-zero实战

文档地址
官方examples
前提:
安装 protoc, protoc-gen-go, goctl

api

  1. clone 项目或者 生成目录, init go mod
mkdir zeroService && cd zeroService && go mod init zeroService
  1. 限制grpc版本, 打开go.mod 加入replace google.golang.org/grpc => google.golang.org/grpc v1.29.1
module zeroService

go 1.15

replace google.golang.org/grpc => google.golang.org/grpc v1.29.1
  1. 创建api和rpc目录
mkdir rpc && mkdir api
  1. 创建api文档, api语法
cd api
vim user.api

type HttpResponse {
	Code int         `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

type (
	RegisterReq {
		UserName string `form:"username"`
		Pwd      string `form:"pwd"`
		NickName string `form:"nickname"`
		Age      int    `form:"age"`
	}
	RegisterRsp {
		Rid int `json:"rid"`
	}
)

type (
	InfoReq {
		Rid int `form:"rid"`
	}
	InfoRsp {
		Rid      int    `json:"rid"`
		UserName string `json:"username"`
		Pwd      string `json:"pwd"`
		NickName string `json:"nickname"`
		Age      int    `json:"age"`
	}
)

// 用户相关api
service user-api{
	@doc "用户注册"
	@handler register
	post /register (RegisterReq) returns (HttpResponse)
	
	@doc "获取用户信息"
	@handler info
	get /info (InfoReq) returns (HttpResponse)
}


  1. 生成项目
goctl api go -api user.api -dir .


./
├── api
│   ├── etc
│   │   └── user-api.yaml //配置文件
│   ├── internal
│   │   ├── config
│   │   │   └── config.go
│   │   ├── handler
│   │   │   ├── infohandler.go
│   │   │   ├── registerhandler.go
│   │   │   └── routes.go
│   │   ├── logic
│   │   │   ├── infologic.go //业务逻辑
│   │   │   └── registerlogic.go //业务逻辑
│   │   ├── svc
│   │   │   └── servicecontext.go
│   │   └── types
│   │       └── types.go
│   ├── user.api
│   └── user.go
├── go.mod
└── rpc
  1. 修改配置文件
vim etc/user-api.yaml

Name: user-api
Host: 0.0.0.0
Port: 48888
DataSource: zmwb:realize2012@tcp(127.0.0.1:3306)/zero
Cache:
  - Host: 127.0.0.1:6379
Log:
  Model: console
  
vim internal/config/config.go

//加入下面两个配置声明
type Config struct {
	rest.RestConf
	DataSource string //新加
	Cache      cache.CacheConf //新加
}
  1. 设计数据库
CREATE TABLE `userinfo` (
`id` int(10) NOT NULL AUTO_INCREMENT,
`username` varchar(255) not null,
`nickname` varchar(255) not null,
`age` int(10) not null default 0,
`pwd` varchar(255) not null,
`created_at` datetime NOT NULL,
`updated_at` datetime DEFAULT NULL,
`deleted_at` datetime DEFAULT NULL,
key `n_idx` (`username`),
key `q_idx` (`age`, `nickname`),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


//生成model文件, 目录在api/model里
goctl model mysql datasource -url="zmwb:realize2012@tcp(127.0.0.1:3306)/zero" -table="*"  -dir="./model"

//修改服务上下文, 注入model
vim internal/svc/servicecontext.go


type ServiceContext struct {
	Config config.Config
	Model  model.UserinfoModel //新加
}

func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config: c,
		Model:  model.NewUserinfoModel(sqlx.NewMysql(c.DataSource)), //新加
	}
}

  1. 实现业务逻辑, 两个接口, 用这种方式实现info接口, 另一个接口后面用rpc方式实现
vim internal/logic/infologic.go

func (l *InfoLogic) Info(req types.InfoReq) (*types.HttpResponse, error) {
	// 加入实现逻辑
	userinfo, err := l.svcCtx.Model.FindOne(int64(req.Rid))
	rsp := types.HttpResponse{}
	if err != nil {
		rsp.Code = 2
		rsp.Msg = "not found"
		return &rsp, err
	}
	rsp.Data = types.InfoRsp{
		Rid:      int(userinfo.Id),
		UserName: userinfo.Username,
		NickName: userinfo.Nickname,
		Age:      int(userinfo.Age),
	}
	return &rsp, nil
}

  1. 启动服务
go run user.go

//进行接口测试
curl -v http://127.0.0.1:48888/info?rid=1

rpc

  1. 编写pb文件
cd ../rpc
vim userService.proto


syntax = "proto3";

package userService;


message RegisterRequest {
    string username = 1;
    string nickname = 2;
    string pwd = 3;
    int64  age = 4;
}

message RegisterResponse {
    int64 rid = 1;
}

service UserService {
    //注册
    rpc Register (RegisterRequest) returns (RegisterResponse);

}

  1. 根据pb文件生成rpc代码
goctl rpc proto -src userService.proto  -dir .

//执行后项目结构
./
├── api
│   ├── etc
│   │   └── user-api.yaml
│   ├── internal
│   │   ├── config
│   │   │   └── config.go
│   │   ├── handler
│   │   │   ├── infohandler.go
│   │   │   ├── registerhandler.go
│   │   │   └── routes.go
│   │   ├── logic
│   │   │   ├── infologic.go
│   │   │   └── registerlogic.go
│   │   ├── svc
│   │   │   └── servicecontext.go
│   │   ├── table.sql
│   │   └── types
│   │       └── types.go
│   ├── model
│   │   ├── userinfomodel.go
│   │   └── vars.go
│   ├── user.api
│   └── user.go
├── go.mod
├── go.sum
└── rpc
    ├── etc
    │   └── userservice.yaml
    ├── internal
    │   ├── config
    │   │   └── config.go
    │   ├── logic
    │   │   └── registerlogic.go
    │   ├── server
    │   │   └── userserviceserver.go
    │   └── svc
    │       └── servicecontext.go
    ├── userService
    │   └── userService.pb.go
    ├── userService.proto
    ├── userservice.go
    └── userserviceclient
        └── userservice.go
  1. 修改配置文件
vim etc/userservice.yaml

Name: userservice.rpc
ListenOn: 127.0.0.1:48080
Etcd:
  Hosts:
  - 127.0.0.1:2379 //可以指向自己部署的etcd, 用于服务发现
  Key: userservice.rpc
DataSource: zmwb:realize2012@tcp(127.0.0.1:3306)/zero
Cache:
  - Host: 127.0.0.1:6379
Log:
  Model: console

  
vim internal/config/config.go

//加入下面两个配置声明
type Config struct {
	zrpc.RpcServerConf
	DataSource string          //新加
	Cache      cache.CacheConf //新加
}
  1. 注入model等
//修改服务上下文, 注入model
vim internal/svc/servicecontext.go


type ServiceContext struct {
	Config config.Config
	Model  model.UserinfoModel //新加
}

func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config: c,
		Model:  model.NewUserinfoModel(sqlx.NewMysql(c.DataSource)), //新加
	}
}

  1. 实现业务逻辑
vim internal/logic/registerlogic.go

// 注册
func (l *RegisterLogic) Register(in *userService.RegisterRequest) (*userService.RegisterResponse, error) {
	user := model.Userinfo{
		Username: in.Username,
		Nickname: in.Nickname,
		Pwd: in.Pwd,
		Age: in.Age,
	}
	res, err := l.svcCtx.Model.Insert(user)
	if err != nil {
		return nil, err
	}
	rid, err := res.LastInsertId()
	if err != nil {
		return nil, err
	}
	return &userService.RegisterResponse{Rid: rid}, nil
}

  1. api 中注册rpc客户端
cd ../api

vim etc/user-api.yaml
//加入以下几行服务发现配置
Rpc:
  Etcd:
    Hosts:
    - 127.0.0.1:2379
    Key: userservice.rpc
	

vim internal/config/config.go 
type Config struct {
	rest.RestConf
	DataSource string
	Cache      cache.CacheConf
	Rpc        zrpc.RpcClientConf //新加
}

vim internal/svc/servicecontext.go

type ServiceContext struct {
	Config         config.Config
	Model          model.UserinfoModel
	UserServiceRpc userserviceclient.UserService //新加
}

func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config:         c,
		Model:          model.NewUserinfoModel(sqlx.NewMysql(c.DataSource)),
		UserServiceRpc: userserviceclient.NewUserService(zrpc.MustNewClient(c.Rpc)), //新加
	}
}

  1. api 中实现register逻辑
vim internal/logic/registerlogic.go 

func (l *RegisterLogic) Register(req types.RegisterReq) (*types.HttpResponse, error) {

	in := &userService.RegisterRequest{
		Username: req.UserName,
		Nickname: req.NickName,
		Pwd:      req.Pwd,
		Age:      int64(req.Age),
	}
	regRsp, err := l.svcCtx.UserServiceRpc.Register(l.ctx, in)
	if err != nil {
		return nil, err
	}
	rsp := types.HttpResponse{}

	rsp.Data = types.RegisterRsp{
		Rid: int(regRsp.Rid),
	}
	return &rsp, nil
}

  1. 启动api 和 rpc 服务
go run user.go &
go run userservice.go &

//进行接口测试
  1. docker 部署
goctl docker -go rpc/userService.go -port 48080
goctl docker -go api.user.go -port 48888
//会在两个目录中生成两个dockerfile
//build 两个镜像
docker build -t user.service:v1 
docker build -t user.api:v1 .
//build 失败, 看Dockerfile发现很多目录写死, 可能是要固定的机器上把目录生成好, 才能build成功

//启动两个容器

总结

  1. goctl功能很好用
  2. 部分细节比较固定, 比如, mysql的time.Time有些情况不行, 大部分是ok, goctl docker功能参数太少了, 如果要使用的话可能需要改一改
  3. 看介绍, goctl model生成如果配置了缓存的话, 会自动缓存很多数据, 减轻数据库压力, 这一块算是个特色
原文地址:https://www.cnblogs.com/leescre/p/14797326.html