我们目前见到的 Go 后端服务,基本上可以分为 API 服务和非 API 服务两类。
API 服务:通过对外提供 HTTP/RPC 接口来完成指定的功能。比如订单服务,通过调用创建订单的 API 接口,来创建商品订单。 非 API 服务:通过监听、定时运行等方式,而不是通过 API 调用来完成某些任务。比如数据处理服务,定时从 Redis 中获取数据,处理后存入后端存储中。再比如消息处理服务,监听消息队列(如 NSQ/Kafka/RabbitMQ),收到消息后进行处理。 对于 API 服务和非 API 服务来说,它们的启动流程基本一致,都可以分为三步: 1、应用框架的构建,这是最基础的一步。 2、应用初始化。 3、服务启动。
图中,命令行程序、命令行参数解析和配置文件解析,是所有服务都需要具备的功能,这些功能有机结合到一起,共同构成了应用框架。
所以,我们要构建的任何一个应用程序,至少要具备命令行程序、命令行参数解析和配置文件解析这 3 种功能。
命令行程序:用来启动一个应用。命令行程序需要实现诸如应用描述、help、参数校验等功能。根据需要,还可以实现命令自动补全、打印命令行参数等高级功能。 命令行参数解析:用来在启动时指定应用程序的命令行参数,以控制应用的行为。 配置文件解析:用来解析不同格式的配置文件。 另外,上述 3 类功能跟业务关系不大,可以抽象成一个统一的框架。应用初始化、创建 API/非 API 服务、启动服务,跟业务联系比较紧密,难以抽象成一个统一的框架。
App 包设计和实现 pkg/app 目录下的 5 个主要文件是 app.go、cmd.go、config.go、flag.go、options.go,分别实现了应用程序框架中的应用、命令行程序、命令行参数解析、配置文件解析和命令行选项 5 个部分,具体关系如下图所示: 应用由命令行程序、命令行参数解析、配置文件解析三部分组成,命令行参数解析功能通过命令行选项来构建,二者通过接口解耦合。
一个应用框架由命令、命令行参数解析、配置文件解析 3 部分功能组成,我们可以通过 Cobra 来构建命令,通过 Pflag 来解析命令行参数,通过 Viper 来解析配置文件。一个项目,可能包含多个应用,这些应用都需要通过 Cobra、Viper、Pflag 来构建。为了不重复造轮子,简化应用的构建,我们可以将这些功能实现为一个 Go 包,方便直接调用构建应用。
项目的应用可以通过:AppBuilder 包来构建的,在构建时,调用 App 包提供的 NewApp 函数,来构建一个应用:
func main() {
opts := myoptions.NewMyOptions()
builder := app.NewAppBuilder()
app := builder.
WithName("myapp").
WithShortName("myapp").
WithDesc("this is my app").
WithOptions(opts).
WithRun(func(basename string) error {
return nil
}).
Build()
app.Run()
}
在构建应用时,只需要提供应用简短/详细描述、应用二进制文件名称和命令行选项即可。App 包会根据 Builder 提供的 Flags() 方法,来给应用添加命令行选项。命令行选项中提供了 -c, --config 选项来指定配置文件,App 包也会加载并解析这个配置文件,并将配置文件和命令行选项相同配置项进行 Merge,最终将配置项的值保存在传入的 Options 变量中,供业务代码使用。