本文旨在介绍 GoFrame 框架对微服务-单仓管理( mono-repo)模式的支持,指导开发者如何在微服务-单仓管理( mono-repo)模式下进行代码开发和分工协作。

一、前置阅读

在开始本章节之前,建议先了解一下单体仓库( monolith)、微服务-多仓管理( multi-repo)、微服务-单仓管理( mono-repo)的基本概念以及各自的优缺点: 单体仓库与多仓库都有哪些优势劣势,微服务选择哪种方案比较好?

代码仓库的管理约束并不属于框架职责的一部分, GoFrame 框架的脚手架本身也支持两种仓库项目初始化的命令 - 单仓库( mono-repo)、多仓库( monolith/multi-repo),以满足不同团队的需求。具体选择哪种代码仓库管理模式由开发团队根据自身需求、场景、习惯来自行选择。

微服务大仓管理模式 - 图1提示

为简化和清晰微服务-单仓管理( mono-repo)的描述,后续我们统一以 大仓管理 来指代微服务-单仓管理( mono-repo)模式。

二、大仓管理

1、仓库职责范围的划分

通过前置阅读的文章大家也知道,这个世界上没有银弹,大仓有优点也有缺点,其中最明显的缺点就是 权限的管控 以及 仓库的膨胀。为了更好地管理代码仓库,避免这两点缺陷带来更高的成本,我们建议尽可能减小大仓中的微服务规模。至于仓库中需要维护哪些微服务,需要根据服务之间的协作频率来决定。

1)当团队内部的协作频率高于团队间协作频率时

  • 典型的场景是针对 非微服务化架构的产品,可以将服务管理的权限职责按照各个业务团队进行划分。这样团队内部可以将分散的若干服务通过统一的代码仓库维护起来,充分利用大仓管理的优势,提高团队内部的开发和维护效率。
  • 另外一种场景是业务的微服务数量本身不多(例如 50 个以内),这个时候也可以合并成一个大仓进行管理。需要注意,大仓管理的服务数量并不是由组织架构中的人员数量来决定的。

2)当多个团队之间的多个服务协作频率非常高时

当业务的微服务数量比较多,并且各个服务之间的交互协作比较频繁,那么可以考虑将这些服务合并到大仓中进行管理,可以极大提高协作效率。这种情况大部分出现在微服务在同一产品线、跨团队但不跨中心或部门的情况下。但由于涉及跨多个团队的协作,这对人员的组织架构管理有一定要求,需要由一定权限的管理者来推动。

微服务大仓管理模式 - 图2提示

微服务的管理并不仅是代码的组织管理,更是人员组织架构的管理。

2、大仓下的微服务间如何协作

1)代码可见性的管理

服务与服务之间能够暴露的仅仅是接口,也就是 API。各个服务内部的逻辑应当对外不可见。在 Golang 里面有很好的 internal 特性,正好可以满足可见性管理的要求。如下图大仓代码示例,在 app 目录下管理了若干的服务,每一个服务暴露了自身的 api 目录,供其他服务直接引用(提高服务间协作效率),但内部的业务逻辑包含在了 internal 目录下,对其他服务不可见(也就无法引用)。

微服务大仓管理模式 - 图3

2)服务间接口的调用

协议文件单独维护到各自服务目录下,如果涉及到协议文件编译,那么编译的文件也存放到自身的服务目录下。调用端不需要单独再对目标服务的协议文件重新编译管理。以 HTTP API 的接口定义为例,调用端可以直接引用目标端服务的 API 接口定义:(以下截图中 khaos-shark 为调用端, khaos-oss 为服务端)

微服务大仓管理模式 - 图4

针对于微服务间的 RPC 接口调用也是同样的道理:(以下截图中 user-api 为调用端, user-rpc 为服务端)

微服务大仓管理模式 - 图5

3)兼容性的严格要求

通过以上介绍可以发现,通过大仓的代码管理,使得大仓中所有服务的版本保持一致,每当依赖的服务 API 更新时,调用端服务(使用的 SDK)也将自动得到更新。这就要求仓库中所有服务的接口设计, 必须严格保证兼容性,否则接口间调用将会出现问题:轻者调用端服务编译失败需要调整代码,重者编译成功但运行时报错影响业务。此外,公共引用的大仓基础组件也会受到兼容性的影响。

保证兼容性代码设计的几个要点:

  • 不随意删减接口参数,修改参数名称、参数类型、参数校验逻辑。
  • 当接口必须要进行非兼容更新时,应当使用接口版本号来管理(如 v1, v2, v3...)。
  • 公共组件尽量使用稳定成熟的外部组件,如果是必要的自定义组件,需要保证对外暴露方法的兼容性。 举个例子:一些很基础的功能,比如说 json.Marshal&Unmarshal,有的人封装了一些库/函数,但是后面的人可能都不知道这个库,也不太信任这个函数,就会又重新写一个…久而久之这些库/函数却又无人维护。

3、大仓下的微服务容器化支持

1)镜像仓库的统一管理

分散的镜像仓库将会降低服务容器化的管理维护效率。为便于统一服务容器化管理,我们建议大仓下的服务使用统一的镜像仓库。镜像仓库的地址统一维护到各个服务下的工具配置文件中:

微服务大仓管理模式 - 图6

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/

微服务大仓管理模式 - 图7

  • 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)更多指令

更多的指令支持请参考框架官网工具介绍章节: 开发工具