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。那么将负载不均衡
posted @ 2022-12-05 23:06  Jeff的技术栈  阅读(866)  评论(0编辑  收藏  举报
回顶部