go-zero
目录
1.文档-github的demo地址
https://go-zero.dev/cn/docs/introduction //新
https://legacy.go-zero.dev/cn/goctl.html //老
model地址:
https://github.com/guyouyin123/bs_gozero
ServiceAccount
2.准备工作-环境安装
//下载go-zero go mo tidy
//下载goctl
go install github.com/zeromicro/go-zero/tools/goctl@latest
ps: 安装好goctl后,通过以下命令自动下载以下相关依赖。goctl版本大于1.3.3
goctl env check -i -f
//下载protoc
https://github.com/protocolbuffers/protobuf/releases
//下载protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
//下载protoc-gen-go-grpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
//工具
https://github.com/Mikaelemmmm/go-zero-looklook
3.goctl命令
3.1 goctl api-api文件生成代码
//goctl命令,api文件生成代码
goctl api go -api *.api -dir ../rpc_demo --style=goZero
说明:
--style=goZero//生成代码命名规范:goZero表示驼峰,gozero表示小写,go_zero表示下划线
../rpc_demo //生成代码存放路径
*.api //api文件
go //生成go代码
3.2 goctl rpc protoc-proto文件生成代码
//goctl命令,proto:文件生成代码
goctl rpc protoc *.proto --go_out=../rpc_demo --go-grpc_out=../rpc_demo --zrpc_out=../rpc_demo --style=goZero
goctl rpc protoc *.proto --go_out=../ --go-grpc_out=../ --zrpc_out=../ --style=goZero
3.3 goctl docker-生成Dockerfile文件
//goctl命令,生成Dockerfile
goctl docker -go user.go
3.4 goctl kube deploy-生成k8s部署文件
//生成k8s部署文件
goctl kube deploy --name user --namespace sg-bs -image user:latest -o user.yml -port 8080 -nodePort 31001
3.5 goctl model-通过表反向自动生成model模型--封装shell脚本
userModel.sh chmod 777 userModel.sh--封装shell脚本
#!/usr/bin/env bash
# 使用方法:./genModel.sh 库名 表名
# ./genModel.sh bsmaster users
# ./genModel.sh bsmaster bike
# 再将./genModel下的文件剪切到对应服务的model目录里面,记得改package
#生成的表名
tables=$2
#表生成的genmodel目录
modeldir=./${tables}Model
# 数据库配置
host=192.168.0.214
port=32432
dbname=$1 # 库名
username=root
passwd=123456
echo "开始创建库:$dbname 的表:$tables"
goctl model mysql datasource -url="${username}:${passwd}@tcp(${host}:${port})/${dbname}" -table="${tables}" -dir="${modeldir}" -cache=true --style=goZero
#-cache=false 如果需要redis则打开
3.6 goctl api doc-生成Markdown文档
goctl api doc --dir ./
3.7 goctl api 生成swagger文档
安装:go install github.com/zeromicro/goctl-swagger@latest
使用:goctl api plugin -plugin goctl-swagger="swagger -filename swagger.json" -api *.api -dir .
4. grpcui独立调试rpc工具
grpcui工具独立调试grpc接口
//下载安装工具
go install github.com/fullstorydev/grpcui/cmd/grpcui@latest
//启动工具
grpcui -plaintext localhost:8080
gRPC Web UI available at http://127.0.0.1:60551/...
5. validator参数校验
go get github.com/go-playground/validator/v10
//api文件
type (
UserInfoRes {
UserId int `json:"userId" validate:"gte=30,lte=100"` // validate参数校验
}
UserInfoReq {
UserId int `json:"userId"`
Name string `json:"name"`
}
)
//handler文件
if err := validator.New().StructCtx(r.Context(),req);err!=nil{
httpx.Error(w, err)
return
}
6.api文件编写(api直接调用)
demo.api
syntax = "v1"
info(
title: "user service"
desc: "user api"
author: "Jeff"
email: "xxx@.com"
version: "v1"
)
type (
UserInfoRes {
UserId int64 `json:"userId"`
}
UserInfoReq {
UserId int64 `json:"userId"`
Name string `json:"name"`
}
UserInsertInfoRes {
Name string `json:"name"`
}
)
@server(
group : user //文件夹分组,handler和logic分组放
prefix: dev/v1 //路由分组,全路径:dev/v1/user/info
middleware: LetMiddleware //局部中间件
)
service user {
@doc "获取用户信息"
@handler userInfo
post /user/getInfo (UserInfoRes) returns (UserInfoReq)
@doc "新增用户信息"
@handler insertInfo
post /user/insertInfo (UserInsertInfoRes) returns ()
}
type (
BikeInfoRes {
BikeId int64 `json:"bikeId"`
}
BikeInfoReq {
BikeId int64 `json:"bikeId"`
BikeName string `json:"BikeIame"`
}
)
@server(
group : rpc //文件夹分组,handler和logic分组放
prefix: dev/v1 //路由分组,全路径:dev/v1/user/info
middleware: LetMiddleware //局部中间件
)
service user {
@doc "获取车辆信息,api->rpc user服务通过rpc调用bike服务"
@handler BikeInfo
post /bike/rpc/getInfo (BikeInfoRes) returns (BikeInfoReq)
}
7.proto文件编写(rpc调用)
demo.proto
syntax = "proto3";
option go_package = "./pb";
package pb;
message GetBikeInfoRes {
int64 id = 1;
}
message GetBikeInfoReq {
int64 id = 1;
string BikeName = 2;
}
service RpcBikeService {
rpc RpcGetBikeInfo (GetBikeInfoRes) returns (GetBikeInfoReq);
}
8.项目启动配置方式
使用默认配置:使用etc/user-api.yaml
go run user.go
使用制定配置:-f 配置文件路径
go run user.go -f 配置文件路径
9.配置
db初始化
//main.go中
if err := svc.InitMysql(c); err != nil {
logx.Errorf("Fatal initMysql:%s", err.Error())
return
} //初始化Mysql
func InitMysql(c config.Config) error {
url := c.Mysql.BsMaster.Url
maxopen := c.Mysql.BsMaster.Maxopen
maxidle := c.Mysql.BsMaster.Maxidle
db, err := mysql.InitMysqlDb(url, maxopen, maxidle)
if err != nil {
logx.Errorf("Fatal initMysql:%s", err.Error())
return err
}
bsDb = db
return nil
}
func InitMysqlDb(url string, maxopen, maxidle int) (db *gorm.DB, err error) {
db, err = gorm.Open("mysql", url)
if err != nil {
logx.Errorf("mysql open faild,err=%v", err.Error())
return
}
sqlDb := db.DB()
sqlDb.SetMaxOpenConns(maxopen / 2)
sqlDb.SetMaxIdleConns(maxidle / 2)
err = sqlDb.Ping()
if err != nil {
logx.Errorf("mysqlDb ping faild,err=%v", err.Error())
return
}
db.LogMode(true)
return
}
rpc服务在api中注册
sac/serviceContext.go
type ServiceContext struct {
Config config.Config
Option Option
LetMiddleware rest.Middleware //局部中间件
RpcBikeServiceClient rpcbikeservice.RpcBikeService //Bike服务Rpc注册
}
type Option struct {
UserTmpl user.Object
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
LetMiddleware: middleware.NewLetMiddleware().Handle, //局部中间件初始化
Option: Option{UserTmpl: user.New(user.Option{Db: bsDb.DB(), DbGorm: bsDb})},
RpcBikeServiceClient: rpcbikeservice.NewRpcBikeService(zrpc.MustNewClient(c.RpcBikeServiceConf)),
}
}
log日志配置
etc/user.yaml
日志包代码:svc下初始化的时候config结构体
#日志配置
Log:
ServiceName: user #服务名
Mode: file # console(终端),file(文件),volume(k8s挂载) 3种打印模式,一般k8s的话也采用console,然后收集控制台日志
Level: debug #debug,info,error,severe 日志输出等级
Path: ./logs # 如果Mode为file,log存储路径
Encoding: plain #json一行不好看,plain
Compress: True #几天的日志打压缩包
KeepDays: 3 #保留几天的日志
禁用每分钟打印日志
//main.go中
logx.DisableStat() //禁用每分钟打印的监控日志,建议打开
rpc拦截器(中间件)
api服务(rpc客户端)
//svc/serviceContext.go
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
LetMiddleware: middleware.NewLetMiddleware().Handle, //局部中间件初始化
Option: Option{UserTmpl: user.New(user.Option{Db: bsDb.DB(), DbGorm: bsDb})},
RpcBikeServiceClient: rpcbikeservice.NewRpcBikeService(zrpc.MustNewClient(c.RpcBikeServiceConf, zrpc.WithUnaryClientInterceptor(SetHeader))), //rpc客户端拦截器
}
}
func SetHeader(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
fmt.Println("====rpc客户端拦截器之前====")
fmt.Println("====req:", req)
fmt.Println("====method:", method)
md := metadata.New(map[string]string{"userName": "Jeff"}) //传值,类似于网管传业务需要端数据
ctx = metadata.NewOutgoingContext(ctx, md)
err := invoker(ctx, method, req, reply, cc, opts...)
if err != nil {
return err
}
fmt.Println("====rpc客户端拦截器之后====")
return nil
}
rpc服务(rpc服务端)
func (l *RpcGetBikeInfoLogic) RpcGetBikeInfo(in *pb.GetBikeInfoRes) (*pb.GetBikeInfoReq, error) {
// todo: add your logic here and delete this line
//从拦截器中获取值,类似于中间件
if md, ok := metadata.FromIncomingContext(l.ctx); ok {
tmp := md.Get("userName") //类似于网管,接收业务传过来端值
fmt.Println("rpc客户端传过来端值:", tmp)
}
bikeInfo := l.svcCtx.Option.BikeTmpl.QueryByBid(in.Id)
if bikeInfo == nil {
return nil, errors.New("数据库没有查到哦")
}
return &pb.GetBikeInfoReq{
Id: bikeInfo.Id,
BikeName: bikeInfo.BikeName,
}, nil
}
10.api与rpc服务发现方式
默认直连方式
api与rpc服务发现-etcd方式
#rpc服务的配置添加:
Etcd:
Hosts:
- 0.0.0.0:2379
Key: bike.rpc #服务发现的key
#api服务的配置添加:
# rpc的配置
RpcBikeServiceConf:
Etcd: # etcd方式:通过etcd自动负载均衡方式
Hosts:
- 0.0.0.0:2379
Key: bike.rpc
api与rpc服务发现-直连方式
//rpc服务的配置:etcd的配置注释掉
默认直连方式
# rpc的配置
RpcBikeServiceConf:
Endpoints: # 直连方式:需要维护Endpoints数组保持负载均衡
- 0.0.0.0:8081 #rpc服务ListenOn=8081
ps:缺点,可能负载不均衡。因为api服务不能感知rpc服务副本的变化。k8s的hpa自动伸缩。需要手动添加api服务的Endpoints,然后重新发布
api与rpc服务发现-k8s方式
# rpc的配置
RpcBikeServiceConf:
Target: k8s://namespace/rpc_service_name:port # k8s方式:k8s的service负载
ps:如果使用了k8s,但是服务发现使用了Endpoints。那么将负载不均衡
选择了IT,必定终身学习