TypechoJoeTheme

Toasobi的博客

go-zero 从零到etcd注册

本文最后更新于2022年09月15日,已超过734天没有更新。如果文章内容或图片资源失效,请留言反馈,我会及时处理,谢谢!

本文章手把手教学从go-zero环境搭建到调用rpc并注册etcd。为了更加直观,我决定实现一个用户查询的方法并连接数据库,模拟真实场景下的go-zero开发。

  • 第一步:生成api业务代码

首先看下分包

一般分包采用这种格式,这里简单编写了一下api文件,包含作者信息,请求体和响应体以及需要注册的路由。
之后我们便可以通过api文件通过一行代码用goctl生成代码了(没有goctl的去github下拉取即可)

@生成api业务代码 : 进入"服务api目录下,执行下面命令
goctl api go -api *.api -dir ../ --style=goZero //这里注意可能会报错,因为可能要明确指定api文件名字
生成完之后自动生成的文件基本上除了逻辑都最好不要动了

生成代码如下:

补充:1.基于user.go生成dockerfile:goctl docker -go user.go

     2.根据api文件生成md文件:**goctl api doc --dir ./** (进入到user-api文件夹中)


  • 第二步:编写proto生成pb文件,生成user-rpc

我们现在项目下新建一个叫user-rpc的文件夹,在该文件下下面新建一个pb文件夹,里面新建user.proto文件,开始编写
下面是分包情况和代码

之后我们生成pb文件,使用下面一行代码
goctl rpc protoc user.proto --go_out=../ --go-grpc_out=../ --zrpc_out=../ --style=goZero
注意!这个命令需要管理员权限,如果你的编译器没有管理员权限,可以直接在管理员权限下的cmd中cd到该项目的pb目录执行
以下是成功之后的分包情况

  • 第三步:数据库建表并链接到编译器生成model代码

先瞅一眼数据库情况(user表)

建完表之后便可以编写代码关联了。这里我使用的是sh文件关联,这种方式关联出来的代码齐全。
代码如下

<div># 使用方法:
# ./genModel.sh usercenter user
# ./genModel.sh usercenter user_auth
# 再将./genModel下的文件剪切到对应服务的model目录里面,记得改package

#生成的表名
tables=$2
#表生成的genmodel目录
modeldir=./genModel

# 数据库配置
host=127.0.0.1
port=3306
dbname=$1
username=root
passwd=268366

echo "开始创建库:$dbname 的表:$2"
goctl model mysql datasource -url="${username}:${passwd}@tcp(${host}:${port})/${dbname}" -table="${tables}"  -dir="${modeldir}" --style=goZero</div>

注:$1,$2表示参数,在输入指令之后输入这两个参数的值即可
这里我们暂时不用cache(redis)缓存,有需要的可以在本人博客中查找使用方法。

写完了之后我们执行改文件,在windows下要想执行sh文件可以使用git。在git下载目录处找到bin目录,里面有一个sh.exe,点击执行。
使用cd命令,转到你需要执行sh文件的目录,然后执行./filename。比如**d:\project\test.sh。
cd d:\project
./test.sh**

下面是完成后的情况

检查无误后就可以在user-api下建一个model文件夹并且把genModel里的东西拖到里面了

  • 第四步:链接数据库并编写逻辑

首先现在user-api的yaml文件中配置mysql。

接着要去config.go映射

然后去svc下初始化配置(注意userModel的类型是那个生成的接口集)

最后在logic编写逻辑就好了!(findone方法是model自动生成的嗷)

<div>func (l *UserInfoLogic) UserInfo(req *types.UserInfoReq) (resp *types.UserInfoResp, err error) {

  // todo: add your logic here and delete this line

  user, err := l.svcCtx.UserModel.FindOne(l.ctx, req.UserId)

  //细节

  if err != nil && err != model.ErrNotFound {

​    fmt.Println(err)

​    return nil, errors.New("查询数据失败")

  }

  if user == nil {

​    return nil, errors.New("用户不存在")

  }



  return &types.UserInfoResp{

​    UserId:  user.Id,

​    Nickname: user.Nickname,

  }, nil

}</div>

可以使用postman测试一下
注:运行时internal文件夹可能会报错,因为这名字特殊,是内部类,需要访问权限。解决办法是直接去对应文件夹使用go run指令运行,而不是直接点运行的按钮。

  • 第五步:测试user-rpc

开启步骤的前置任务:拉取grpcui。这个插件可以帮助我们在脱离api的情况下独立测试grpc运行情况。

首先还是看yaml文件

注意一定要添加Mode这个字段,因为在user.go中注册需要这一个字段
注意监听地址全部改为0.0.0.0,因为需要监听多个地址同一端口。

其他暂时不用动

咱们先简单测试一下,暂时不连数据库,logic代码如下

<div>func (l *GetUserInfoLogic) GetUserInfo(in *pb.GetUserInfoReq) (*pb.GetUserInfoResp, error) {
    // todo: add your logic here and delete this line
    m := map[int]string{
        1: "张三",
        2: "赵六",
    }

    nickname := "unknown"
    if name, ok := m[int(in.Id)]; ok {
        nickname = name
    }
    return &pb.GetUserInfoResp{
        Id:       in.Id,
        Nickname: nickname,
    }, nil
}</div>

然后启动user.go
启动grpcui:grpcui -plaintext localhost: 8080 //端口这里填grpc端口,记得开etcd

完成!

  • 第六步:api调用rpc

首先修改一下user-api的config.go

zrpc.RpcClientConf其实就是已经写好的grpc客户端的配置,我们取个名字叫UserRpcConf

接着在yaml文件添加UserRpcConf并且编写一下配置,大致如下:

其实就是从user-rpc那里复制粘贴来的,注意key两边一定要相等了

再下面我们去svc中初始化它,因为我们的logic需要用到

一个客户端对应一个server端的框架就已经构建好了

最后就是修改logic代码了,因为rpc那里的代码没用到数据库,咱们这里也暂时不用了,代码如下

<div>func (l *UserInfoLogic) UserInfo(req *types.UserInfoReq) (resp *types.UserInfoResp, err error) {
    // todo: add your logic here and delete this line
    userResp, err := l.svcCtx.UserRpcClient.GetUserInfo(l.ctx, &pb.GetUserInfoReq{
        Id: req.UserId,
    })
    if err != nil {
        return nil, err
    }

    return &types.UserInfoResp{
        UserId:   userResp.Id,
        Nickname: userResp.Nickname,
    }, nil
}</div>

这里的代码其实很简单,就是嵌套了grpc传输,前端传过来的数据通过grpc传输到rpc的logic层处理最后用api的type返回给前端

到这里就已经写完了,我们先启动rpc的user.go,再启动api的user.go,用postman测试,结果如下(为了好分辨rpc那里的map的key值做了更改)

  • 第七步:rpc使用数据库

这一步很简单,就是把api的model复制粘贴到rpc中,再和api一样的配置文件,编写config.go,初始化svc,调用即可
rpclogic代码示例:

<div>func (l *GetUserInfoLogic) GetUserInfo(in *pb.GetUserInfoReq) (*pb.GetUserInfoResp, error) {
    // todo: add your logic here and delete this line

    user, err := l.svcCtx.UserModel.FindOne(l.ctx, in.Id)

    if err != nil && err != model.ErrNotFound {
        fmt.Println(err)
        return nil, errors.New("查询数据失败")
    }
    if user == nil {
        return nil, errors.New("用户不存在")
    }

    return &pb.GetUserInfoResp{
        Id:       in.Id,
        Nickname: user.Nickname,
    }, nil
}</div>
  • 总结

其实到这里我们已经完成了用gozero通过etcd的方式完成业务编写了。至于api与rpc直接的3种调用方式,使用redis缓存,配置log,添加中间件以及group管理handler等等内容,我会放在这个专题下记录内容

朗读
赞(0)
评论 (0)