文檔地址
官方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生成如果配置了緩存的話, 會自動緩存很多數據, 減輕數據庫壓力, 這一塊算是個特色