以我浅薄的见识而言,go zero在代码生成方面做的相当不错,而同时因为go zero一个框架包含了从http服务到RPC服务,再到数据库操作的全流程,所以利用goctl工具就能够帮助我们快速完成一个微服务项目的搭建。我想这也是众多小团队选择go zero框架的原因之一。
go-zero的文档对于新手而言,还是显得有些简略,因此本文将使用一个有登录与发帖功能的论坛服务器作为例子来较为详细地介绍应该如何从头建立一个go-zero微服务单体仓库项目。本文使用到的文件可以在我的仓库获取。
00 单体仓库
在介绍项目结构前,先介绍一下单体仓库(mono-repo)的概念。与单体仓库相对的,是多仓库(multi-repo)概念。 对于大单体项目而言,使用多少个仓库并不不是一个需要考虑的问题,但这两个概念主要是对于微服务架构的项目而言的。顾名思义,单体仓库即将项目的所有代码放在同一个仓库中,而多仓库则是以某种方式将代码分类放在多个仓库中,例如按服务划分。
使用单体仓库的优势:
- 易于理解项目整体。开发人员可以把整个项目加载到本地的 IDE 当中,进行 code review,方便开发人员把握整体的技术架构和业务目标。
- 易于集成和部署。所有的代码在一个仓库里面,不需要特别的集中管理和协调,也可以直接在本地部署调试。
- 易于重用。所有的代码都在一个仓库中,开发人员开发的时候比较容易发现和重用已有的代码,而不是去重复造轮子。
- 开发人员容易对现有代码进行重构,可以抽取出一些公共的功能进一步提升代码的质量和复用度。
- 易于规范代码。所有的代码在一个仓库当中就可以标准化依赖管理,集中开展 code review,规范化代码的风格。
但随着服务的不断增多,单体仓库方案会使得仓库十分的庞大。另外多仓库也有着自己的优势:
- 每一个服务都有一个独立的仓库,职责单一。
- 代码量和复杂性受控,服务由不同的团队独立维护、边界清晰。
- 单个服务也易于自治开发测试部署和扩展,不需要集中管理集中协调。
- 利于进行权限控制,可以针对单个仓库来分配权限,权限分配粒度比较细。
在实际工程中,有许多大型项目是存放在单体仓库中的,例如google的
具体是选择哪个方案还是要根据项目与团队的具体特点来决定。
01 项目结构
go zero文档中,其实就有关于项目结构的介绍:
本文使用的demo有/demo/user/login和/demo/comment/action两个路由,分别对应user微服务的login接口和comment微服务的comment_action接口。
而本文建立的demo的具体项目结构如下:
.
├── api_gateway
│ ├── api
│ ├── etc
│ ├── internal
│ │ ├── config
│ │ ├── handler
│ │ ├── logic
│ │ ├── svc
│ │ └── types
├── demo.sql
├── go.mod
├── go.sum
├── README.md
└── service
├── comment
│ ├── comment.go
│ ├── comment.proto
│ ├── commentservice
│ ├── etc
│ ├── internal
│ ├── model
│ └── pb
└── user
├── etc
├── internal
├── model
├── pb
├── user.go
├── user.proto
└── userservice
其中api_gateway对应的就是restful文件夹,存放api网关(http服务),而service下各个文件夹则是分别存放各个微服务(rpc服务)。
当然,项目结构可以是多样的,上面介绍的只是基于我对go zero文档内容的理解而使用的结构。
02 代码生成
准备
在正式代码生成前,我们需要准备三个东西:
1.xxx.api
.api文件用于生成http服务的代码。
而根据go-zero文档所说:“我们规定将所有 service 语法块声明的 HTTP 服务信息都放在 main api文件中,抽象结构体放在其他 api 文件中,然后在 main api 文件中引入其他 api 文件,这样可以让 main api 文件更加简洁,易于维护。”
因此本demo分了5个.api文件,分别是main.api, login.api ,comment_action.api, comment.api, user.api
2.xxx.proto
每个微服务都维护自己的xxx.proto文件,用于生成rpc服务的代码。我们的demo中有两个微服务,分别是user服务和comment服务,相应的也就有两个.proto文件:
user.proto
和
comment.proto
3.已经建立好的数据库(或者对应的xxx.sql文件)
用于生成数据持久化的代码。
目录构建
首先,新建一个demo文件夹作为我们的项目文件夹,本项目所有的代码都将放在该文件夹中。将上一章准备好的文件按以下结构放好:
demo
├── api_gateway
│ └── api
│ ├── comment.api
│ ├── login.api
│ └── main.api
└── service
├── comment
│ └── comment.proto
└── user
└── user.proto
代码生成
首先,在根目录下执行命令初始化项目:
go mod tidy
此时,demo根目录下会生成一个go.mod文件。
使用goctl工具可以帮助我们快速搭建起我们的项目框架。终端输出Done.表示生成成功。
api服务生成
cd进入./api_gateway文件夹,执行以下命令:
goctl api go -api ./api/main.api -dir .
rpc服务生成
cd 进入./service/user文件夹,执行以下命令:
goctl rpc protoc user.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.
cd 进入./service/comment文件夹,执行以下命令:
goctl rpc protoc comment.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.
完成以上步骤后,会生成一个./service/user/pb/user用于存放rpc通信接口相关代码。由于pb文件夹下只有一个user文件夹,因此我觉得这里可以优化一下,但暂未研究出来修改哪里可以使得这里不会冗余的文件夹。comment文件夹也同样如此。
数据库服务生成
cd 进入./service/comment文件夹,执行以下命令:
goctl model mysql datasource -dir=./model -t=user --url='root:password@tcp(127.0.0.1:3306)/demo'
cd 进入./service/user文件夹,执行以下命令:
goctl model mysql datasource -dir=./model -t=comment --url='root:password@tcp(127.0.0.1:3306)/demo'
注意命令里的地址要以数据库的名字结尾,不能加上额外的参数。
要在微服务中操作数据库,还需要配置./etc/user.yaml文件,./internal/config/config.go文件 和 ./internal/server/userserviceserver.go文件。
最后执行命令:
go mod tidy
完成上述步骤后,应该就能得到第一章中描述的项目结构,具体的结构与生成的文件可以查看仓库的after_gen分支。
重新生成
场景:
在开始开发我们程序的具体逻辑后,突然发现之前写的.proto文件或者.api文件有误,又或者在开发过程中需要修改某个表的某字段的类型。
解决方案:
在这些情况下,我们可以直接修改.proto文件、.api文件的错误内容或数据库表的字段类型,然后直接执行相应的命令就能自动修改需要修改的文件,而我们先前已经编写的逻辑是不会被刷新的(前提是我们的逻辑没有写在注释标明了DO NOT EDIT的文件中,这些文件在重新代码生成后是会被刷新的)。