go-zero实战
文档地址
官方examples
前提:
安装 protoc, protoc-gen-go, goctl
api
- clone 项目或者 生成目录, init go mod
mkdir zeroService && cd zeroService && go mod init zeroService
- 限制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
- 创建api和rpc目录
mkdir rpc && mkdir api
- 创建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)
}
- 生成项目
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
- 修改配置文件
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 //新加
}
- 设计数据库
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)), //新加
}
}
- 实现业务逻辑, 两个接口, 用这种方式实现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
}
- 启动服务
go run user.go
//进行接口测试
curl -v http://127.0.0.1:48888/info\?rid\=1
rpc
- 编写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);
}
- 根据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
- 修改配置文件
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 //新加
}
- 注入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)), //新加
}
}
- 实现业务逻辑
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
}
- 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)), //新加
}
}
- 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
}
- 启动api 和 rpc 服务
go run user.go &
go run userservice.go &
//进行接口测试
- 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成功
//启动两个容器
总结
- goctl功能很好用
- 部分细节比较固定, 比如, mysql的time.Time有些情况不行, 大部分是ok, goctl docker功能参数太少了, 如果要使用的话可能需要改一改
- 看介绍, goctl model生成如果配置了缓存的话, 会自动缓存很多数据, 减轻数据库压力, 这一块算是个特色