本文旨在介绍 GoFrame
框架对微服务-单仓管理( mono-repo
)模式的支持,指导开发者如何在微服务-单仓管理( mono-repo
)模式下进行代码开发和分工协作。
一、前置阅读
在开始本章节之前,建议先了解一下单体仓库( monolith
)、微服务-多仓管理( multi-repo
)、微服务-单仓管理( mono-repo
)的基本概念以及各自的优缺点: 单体仓库与多仓库都有哪些优势劣势,微服务选择哪种方案比较好?
代码仓库的管理约束并不属于框架职责的一部分, GoFrame
框架的脚手架本身也支持两种仓库项目初始化的命令 - 单仓库( mono-repo
)、多仓库( monolith/multi-repo
),以满足不同团队的需求。具体选择哪种代码仓库管理模式由开发团队根据自身需求、场景、习惯来自行选择。
为简化和清晰微服务-单仓管理( mono-repo
)的描述,后续我们统一以 大仓管理 来指代微服务-单仓管理( mono-repo
)模式。
二、大仓管理
1、仓库职责范围的划分
通过前置阅读的文章大家也知道,这个世界上没有银弹,大仓有优点也有缺点,其中最明显的缺点就是 权限的管控 以及 仓库的膨胀。为了更好地管理代码仓库,避免这两点缺陷带来更高的成本,我们建议尽可能减小大仓中的微服务规模。至于仓库中需要维护哪些微服务,需要根据服务之间的协作频率来决定。
1)当团队内部的协作频率高于团队间协作频率时
- 典型的场景是针对 非微服务化架构的产品,可以将服务管理的权限职责按照各个业务团队进行划分。这样团队内部可以将分散的若干服务通过统一的代码仓库维护起来,充分利用大仓管理的优势,提高团队内部的开发和维护效率。
- 另外一种场景是业务的微服务数量本身不多(例如
50
个以内),这个时候也可以合并成一个大仓进行管理。需要注意,大仓管理的服务数量并不是由组织架构中的人员数量来决定的。
2)当多个团队之间的多个服务协作频率非常高时
当业务的微服务数量比较多,并且各个服务之间的交互协作比较频繁,那么可以考虑将这些服务合并到大仓中进行管理,可以极大提高协作效率。这种情况大部分出现在微服务在同一产品线、跨团队但不跨中心或部门的情况下。但由于涉及跨多个团队的协作,这对人员的组织架构管理有一定要求,需要由一定权限的管理者来推动。
微服务的管理并不仅是代码的组织管理,更是人员组织架构的管理。
2、大仓下的微服务间如何协作
1)代码可见性的管理
服务与服务之间能够暴露的仅仅是接口,也就是 API
。各个服务内部的逻辑应当对外不可见。在 Golang
里面有很好的 internal
特性,正好可以满足可见性管理的要求。如下图大仓代码示例,在 app
目录下管理了若干的服务,每一个服务暴露了自身的 api
目录,供其他服务直接引用(提高服务间协作效率),但内部的业务逻辑包含在了 internal
目录下,对其他服务不可见(也就无法引用)。
2)服务间接口的调用
协议文件单独维护到各自服务目录下,如果涉及到协议文件编译,那么编译的文件也存放到自身的服务目录下。调用端不需要单独再对目标服务的协议文件重新编译管理。以 HTTP API
的接口定义为例,调用端可以直接引用目标端服务的 API
接口定义:(以下截图中 khaos-shark
为调用端, khaos-oss
为服务端)
针对于微服务间的 RPC
接口调用也是同样的道理:(以下截图中 user-api
为调用端, user-rpc
为服务端)
3)兼容性的严格要求
通过以上介绍可以发现,通过大仓的代码管理,使得大仓中所有服务的版本保持一致,每当依赖的服务 API
更新时,调用端服务(使用的 SDK
)也将自动得到更新。这就要求仓库中所有服务的接口设计, 必须严格保证兼容性,否则接口间调用将会出现问题:轻者调用端服务编译失败需要调整代码,重者编译成功但运行时报错影响业务。此外,公共引用的大仓基础组件也会受到兼容性的影响。
保证兼容性代码设计的几个要点:
- 不随意删减接口参数,修改参数名称、参数类型、参数校验逻辑。
- 当接口必须要进行非兼容更新时,应当使用接口版本号来管理(如
v1, v2, v3...
)。 - 公共组件尽量使用稳定成熟的外部组件,如果是必要的自定义组件,需要保证对外暴露方法的兼容性。 举个例子:一些很基础的功能,比如说
json.Marshal&Unmarshal
,有的人封装了一些库/函数,但是后面的人可能都不知道这个库,也不太信任这个函数,就会又重新写一个...久而久之这些库/函数却又无人维护。
3、大仓下的微服务容器化支持
1)镜像仓库的统一管理
分散的镜像仓库将会降低服务容器化的管理维护效率。为便于统一服务容器化管理,我们建议大仓下的服务使用统一的镜像仓库。镜像仓库的地址统一维护到各个服务下的工具配置文件中:
2)统一编译、提交指令
框架提供了常用的指令来实现程序的编译、镜像的编译、镜像的提交。
make build
编译程序,生成二进制文件。
更多介绍请参考文档: 交叉编译-build
make image
编译程序并编译镜像,生成 Docker
镜像。
通过 make image TAG=xxx
可以指定编译生成的镜像标签名称。
更多介绍请参考文档: 镜像编译-docker
make image.push
编译程序、编译镜像并推送镜像到已配置好的镜像仓库。
通过 make image.push TAG=xxx
可以指定编译生成的镜像标签名称。
3)统一部署、调试指令
框架提供了常用的指令来实现 Kubernetes
集群的容器化部署,以及一体化编译部署的开发指令。
make deploy TAG=xxx
部署当前服务到本地 kubeconfig
已连接的 kubernetes
集群中,其中的 TAG
用于指定 deploy
目录下的 overlays
目录。管理部署 yaml
文件使用的是行业内常用的 kustomize
工具,具体介绍文档请参考: https://kubernetes.io/zh-cn/docs/tasks/manage-kubernetes-objects/kustomization/
make image.push deploy TAG=xxx
该命令是开发调试指令,用于一条指令 编译二进制文件、编译并推送 Docker
镜像、部署 Kubernetes
应用并重启应用。
4、大仓下的框架其他指令
框架针对于项目工程管理提供了丰富的工具指令支持,这些指令往往需要再特定的服务目录下执行,例如 ./app/服务名称
1) make cli
用于升级本地的框架 CLI
到最新稳定版本。
2) make up
用于升级本地的框架到最新社区稳定版本。
更多介绍请参考文档: 框架升级-up
3) make dao
用于生成 DAO/Entity/DO
代码文件。
更多介绍请参考文档: 数据规范-gen dao
4) make service
用于解析 logic
目录并自动生成内调用接口。该指令在 Goland IDE
下往往使用自动化的 Watcher
文件变动来自动生成,具体请参考官方文档。
更多介绍请参考文档: 模块规范-gen service
5) make enums
用于解析指定代码目录(默认为 api
目录)并自动生成 enums
加载代码。
更多介绍请参考文档: 枚举维护-gen enums
6)更多指令
更多的指令支持请参考框架官网工具介绍章节: 开发工具