前言
聽說下一個項目 可能要用微服務開發,趁着項目的空檔期,對於go微服務的框架進行了學習。目前go的微服務框架個人認爲處於百家齊放的時代,可能這也是go的生態的一個特點吧,也曾簡單用過go-miecro,gin+micro+gorm+mysql+redis 常見方案使用起來還是蠻順手的,可惜該框架成了個人倉庫,生成的依賴會出現引用錯誤,其他的都蠻ok的。對於go-zero的選擇,其實參考此篇文章:https://zhuanlan.zhihu.com/p/488233067 , 再加團隊溝通協商及個人私心(認爲其可以成爲趨勢及給自己加分)。 所以選擇的go 微服務框架爲go-zero。
版本淺介
go:1.17.6
protoc:3.20.1
goctl:1.3.8
mysql:8.0.29
redis:7.0.0
consul:1.12
demo 介紹
實現簡單的用戶表 增查
使用consul 替換etcd
相關接口
/api/user/login 登錄
�/api/user/register 註冊
�/api/user/listuser 查詢所有用戶
�/api/user/userinfo 查詢 某個用戶詳情
源碼:https://github.com/zisefeizhu/go-zero-demo
參考:
go-zero作者
https://www.cnblogs.com/kevinwan/category/2002486.html
go-zero 入門級demo
demo 注意點
常用命令
touch user.api
goctl api go -api ./user.api -dir .
goctl model mysql ddl -src user.sql -dir . -c
touch user.proto
goctl rpc protoc ./rpc/user.proto --go_out=./rpc/types --go-grpc_out=./rpc/types --zrpc_out=./rpc
1、consul 替換etcd
參考文章:https://github.com/zeromicro/zero-contrib/tree/main/zrpc/registry/consul
微服務
rpc/etcd/user.yaml
# consul 替換etcd
Consul:
Host: 127.0.0.1:8500 # consul endpoint
Key: user.rpc # 註冊到consul的服務名字
Meta:
Protocol: grpc
Tag:
- tag
- rpc
rpc/internal/config/config.go
type Config struct {
zrpc.RpcServerConf
Mysql struct {
DataSource string
}
// consul
Consul consul.Conf
CacheRedis cache.CacheConf
Salt string
}
rpc/user.go
import (
"github.com/zeromicro/zero-contrib/zrpc/registry/consul"
func main(){
......
err := consul.RegisterService(c.ListenOn, c.Consul)
if err != nil {
os.Exit(1)
}
defer s.Stop()
api
api/etcd/user.yaml
UserRpc:
Target: consul://127.0.0.1:8500/user.rpc?wait=14s
NonBlock: true
api/internalconfig/config.go
type Config struct {
rest.RestConf
Auth struct {
AccessSecret string
AccessExpire int64
}
UserRpc zrpc.RpcClientConf
}
api/user.go
import (
_ "github.com/zeromicro/zero-contrib/zrpc/registry/consul"
)
2、api層的user.api 書寫
例:
type (
// 用戶登錄
LoginRequest {
Mobile string `json:"mobile"`
Password string `json:"password"`
}
LoginResponse {
AccessToken string `json:"accessToken"`
AccessExpire int64 `json:"accessExpire"`
}
// 用戶登錄
// 用戶註冊
RegisterRequest {
Name string `json:"name"`
Gender int64 `json:"gender"`
Mobile string `json:"mobile"`
Password string `json:"password"`
}
RegisterResponse {
Id int64 `json:"id"`
Name string `json:"name"`
Gender int64 `json:"gender"`
Mobile string `json:"mobile"`
}
// 用戶註冊
// 用戶信息
UserInfoResponse {
Id int64 `json:"id"`
Name string `json:"name"`
Gender int64 `json:"gender"`
Mobile string `json:"mobile"`
}
// 列出所有用戶
ListUserResponse {
Id int64 `json:"id"`
Name string `json:"name"`
Gender int64 `json:"gender"`
Mobile string `json:"mobile"`
}
// 用戶信息
)
service User {
@handler Login
post /api/user/login(LoginRequest) returns (LoginResponse)
@handler Register
post /api/user/register(RegisterRequest) returns (RegisterResponse)
@handler ListUser
post /api/user/listuser returns (ListUserResponse)
}
@server(
jwt: Auth
)
service User {
@handler UserInfo
post /api/user/userinfo returns (UserInfoResponse)
}
3、model層
go-zero不會根據sql自動創建表結構,但是可以根據表結構創建dao層
通常只會生成:Insert / FindOne / FindOneByxxx/Update/Delete 方法,其他方法需要自己填充
例:
rpc/model/usermpdel_gen.go
添加FindList方法
userModel interface {
Insert(ctx context.Context, data *User) (sql.Result, error)
FindOne(ctx context.Context, id int64) (*User, error)
FindOneByMobile(ctx context.Context, mobile string) (*User, error)
FindList() ([]*User, error) // 新增
Update(ctx context.Context, newData *User) error
Delete(ctx context.Context, id int64) error
}
// 實現
func (m *defaultUserModel) FindList() ([]*User, error) {
var resp []*User
query := fmt.Sprintf("select %s from %s ", userRows, m.table)
err := m.QueryRowsNoCache(&resp, query)
switch err {
case nil:
return resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
4、rpc 業務邏輯實現
rpc 層重點關注rpc/internal/logic 目錄,內是業務的真正實現
以查所有用戶爲例
func (l *ListUserLogic) ListUser(in *user.ListUserRequest) (*user.ListUserResponse, error) {
results, err := l.svcCtx.UserModel.FindList()
if err != nil {
if err == model.ErrNotFound {
return nil, status.Error(100, "用戶不存在")
}
return nil, status.Error(500, err.Error())
}
resp := make([]*user.UserInfoResponse, 0, len(results))
for _, item := range results {
resp = append(resp, &user.UserInfoResponse{
Id: item.Id,
Name: item.Name,
Gender: item.Gender,
Mobile: item.Mobile,
})
}
return &user.ListUserResponse{
Data: resp,
}, nil
}
5、go-zero的proto 書寫
在proto中如果func 沒有參數,不能:grpc-go protobuf Empty ,而是定位爲空message
6、對於go-zero的還需改造點
1、配置文件讀取環境變量
2、設置context的超時時間
3、將go-zero裏的rpc和api分別build成可執行程序
4、等
總結
總的來說go-zero使用起來有一些束縛 ,不如micro 靈活隨意。不可否認的是go-zero封裝的相對更好,使開發者基本只用關注業務邏輯。
在業務全面容器化及以istio爲主的服務治理越來越主流的當下及未來:個人覺得在代碼層面實現服務治理能力似乎不能算是一種優勢。其實在以kubernetes 爲標準的容器深度發展的當下,客人認爲開發者應該回到重點關注業務的實現邏輯,而治理應下移到基礎設施層,即Kubernetes + 業務 + 服務治理(istio) 。