go-zero
系列视频教程【Mikaelemmmm
】整理,后面章节需要结合go-zero-looklook
的代码。Mikaelemmmm
的github
上还有别的好的代码示例可以研究下。
注意:标※的章节个人认为是比较重要,需要了解的。
1、zero-gorm:https://github.com/yfanswer/zero-gorm
2、公众号:微服务实践
3、go-zero-example:
4、zero-conttrib:
5、微信社区群:
6、go-zero-issue:
sqlx切换成gorm的流程:
1、sqlx 切成gorm,同时结合sqlc;这种骚操作怎么改的,看到网上有同学这样干。
2、只需要把带缓存生成的model中,sqlx执行db部分换成gorm即可。
3、替换后不影响go-zero 中封装的数据库分布式事务,因为dtm支持gorm,可以看dtm官网。
Cache缓存的使用:
sqlc中包含sqlx。
Cache缓存其实用的就是Redis,可以使用同一个Redis,也可以分开。
Cache缓存的使用实际是为了处理Redis缓存击穿,这块的代码很有借鉴意义。
事务需要自己手动实现,特别是主子表同时插入的情况。
在中间件中处理数据时,不论是操作Redis、数据库,还是其他RPC,需要什么,在创建中间件时就New对应的环境即可。
全局中间件、局部中间件的使用顺序:
全局中间件→局部中间件1→局部中间件2→全局中间件
Mode:可以同时设置console、file,即同时输出到日志文件和控制台。
StackCooldownMillis:是设置stat日志统计输出间隔,表示间隔多久,打印出慢日志,比如运行了200ms,还没出结果,就打印慢查询,默认100。
zero-example中有文件上传的例子。
超时时间Timeout的配置位置。
1、启动类里各个配置的启动过程
2、golang的可变参数
3、threading.GoSafe(),go-zero封装的安全的协程创建,threading包有一些好用的方法值得研究借鉴。
使用postman测试go-zero的grpc服务时,必须在配置文件中加入Mode: dev,然后重启服务。
详见:编写简单的逻辑代码。
Mikaelemmmm自己封装了根据数据表生成.pb.go文件的工具,减少开发时间。
1、go-zero使用k8s时,服务发现不能使用直连方式,可能会导致负载不均衡。
2、api服务的yaml中ListenOn: 0.0.0.0:8080,和rpc服务的yaml中Host: 0.0.0.0,设置成0.0.0.0的话会自动获取内网IP,如果设置成固定内网ip的话就是直接写死了。
在api服务的yaml中将Etcd的配置删除,加上Endpoints即可。
1、有朋友说,基于k8s 服务发现 gin + grpc直接使用k8s默认负载均衡也可以,默认grpc在k8s中会导致负载不均衡,go-zero不是使用的默认k8s负载均衡方式(如果你用了直链协议就走了默认了,所以视频中我不推荐用直链,会导致负载不均衡) , 导致这种负载不均衡的原因是grpc底层http2的实现以及k8s的负载均衡有关系,如果默认使用直链方式也就是交给k8s自己去处理负载均衡,在http1.x短连接是没有问题的,grpc是使用http2实现的,它是长链接多路复用,后续发送的请求也都会打到相同pod,造成负载不均衡,所以go-zero内部已经自己实现了基于k8s的grpc负载均衡,拿到不同pod,把请求使用p2c方式分发到不同的pod,这样就均衡了,所以要使用go-zerok8s的协议,不能用直连协议,如果还不明白为什么默认grpc在k8s会负载不均衡可以google。【来自Up主本人评论】
在rpc服务端的启动类里加 s.AddUnaryInterceptors()
,里面的参数是具体的拦截器,需要注意的是可以重复声明多个不同功能的拦截器。
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
ucenter.RegisterUcenterSqlxServer(grpcServer, ucentersqlxServer.NewUcenterSqlxServer(ctx))
ucenter.RegisterUcenterGormServer(grpcServer, ucentergormServer.NewUcenterGormServer(ctx))
if c.Mode == service.DevMode || c.Mode == service.TestMode {
reflection.Register(grpcServer)
}
})
//s.AddUnaryInterceptors(func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
//})
s.AddUnaryInterceptors(TestServerInterceptor)
//调整RPC收到的消息体大小限制
s.AddOptions(grpc.MaxRecvMsgSize(1024 * 1024 * 1024)) // 1024 MB
//s.AddOptions(grpc.MaxSendMsgSize(1024 * 1024 * 1024)) // 1024 MB
defer s.Stop()
fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
s.Start()
}
// TestServerInterceptor 全局拦截器
func TestServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
fmt.Printf("TestServerInterceptor ====> Start \n")
fmt.Printf("req ====> %+v \n", req)
fmt.Printf("info ====> %+v \n", info)
resp, err = handler(ctx, req)
fmt.Printf("TestServerInterceptor ====> End \n")
return resp, err
}
rpc的客户端拦截器也就是go-zero的api服务端。
package svc
import (
"context"
"fmt"
"github.com/zeromicro/go-zero/rest"
"github.com/zeromicro/go-zero/zrpc"
"go-zero-micro/api/code/ucenterapi/internal/config"
"go-zero-micro/api/code/ucenterapi/internal/middleware"
"go-zero-micro/rpc/code/ucenter/client/ucentergorm"
"go-zero-micro/rpc/code/ucenter/client/ucentersqlx"
"google.golang.org/grpc"
)
type ServiceContext struct {
Config config.Config
Check rest.Middleware
UcenterGormRpc ucentergorm.UcenterGorm //gorm方式的接口
UcenterSqlxRpc ucentersqlx.UcenterSqlx //sqlx方式的接口
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
Check: middleware.NewCheckMiddleware().Handle,
UcenterGormRpc: ucentergorm.NewUcenterGorm(zrpc.MustNewClient(c.UCenterRpc)),
UcenterSqlxRpc: ucentersqlx.NewUcenterSqlx(zrpc.MustNewClient(c.UCenterRpc, zrpc.WithUnaryClientInterceptor(RpcClientInterceptor))),
}
}
// RpcClientInterceptor rpc的客户端拦截器
func RpcClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
fmt.Printf("RpcClientInterceptor ====> Start \n")
fmt.Printf("req ====> %+v \n", req)
err := invoker(ctx, method, req, reply, cc, opts...)
fmt.Printf("RpcClientInterceptor ====> End \n")
if err != nil {
return err
}
return nil
}
func interceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
md := metadata.New(map[string]string{"x": "xx"})
ctx = metadata.NewOutgoingContext(ctx, md)
//logx.Debug("调用rpc服务前")
err := invoker(ctx, method, req, reply, cc)
if err != nil {
return err
}
//logx.Debug("调用rpc服务后")
return nil
}
xxxlogic.go
代码逻辑中使用 if md, ok := metadata.FromIncomingContext(l.ctx); ok {
tmp := md.Get("user_id")
if len(tmp) > 0 {
uid, _ := strconv.ParseInt(tmp[0], 10, 64)
fmt.Printf("userId:%d", uid)
}
}
gorm:https://github.com/SpectatorNan/gorm-zero
需要注意的是:api
文件中多个service
定义的名称必须要一致。例如:
@server(
group: ucenter
prefix: /ucenter
)
service ucenter-api {
@doc(
summary: "1 校验账号是否已存在"
)
@handler getUserByAccount
get /getUserByAccount (UserSimpleModel) returns (BaseModel)
}
@server(
group: login
prefix: /login
)
service ucenter-api {
@doc(
summary: "9 用户账号密码登录"
)
@handler loginByPassword
post /loginByPassword (UserLoginPasswordModel) returns (UserLoginResp)
}
使用模板可以减少重复编写代码的,如model里的,api、rpc等。
1、链路追踪 我最新发的文章在go-zero公众号有,包括跟mq 打通的,熔断降级公众号也有。分布式事务可以看go-zero-looklook的文档中有一节专门讲go-zero对接dtm的,源码级别讲解,如果可以顺便给go-zero-looklook一个star支持【Up主本人评论】
2、asynq,基于redis的定时任务。
视频讲解内容,建议先看一下代码,提前了解一下代码和项目,最好是跟着代码自己亲自实践一遍,加深理解及掌握使用。