图片来自alexchoffy

在现代应用程序中处理配置有很多好的方法。现在我们使用诸如配置文件、环境变量、命令行参数以及 CI 配置模式和即时配置文件构建、远程配置服务器、特定映射和绑定服务等东西,甚至更复杂的东西。

但目标是一样的——为应用程序提供配置,快速获取且易于使用。但是怎么做呢?

我们的应用程序如何使用配置

作为一个优秀的开发者,我总是含蓄地与之抗争。我不使用全局变量,我以可读的方式构建我的代码,并希望我的代码流动确定和清晰。我真的不喜欢一些东西——环境变量,因为它们和全局变量一样糟糕。但是,如果您在开始时阅读它们一次,您也可以以正确的方式处理它们。

因此,主要思想是从它们所在的位置(文件、环境、远程服务器等)获取应用程序设置,并通过应用程序分发它们,以使它们在需要的地方可用。简单的任务吧?但是仍然有很多不同的方法可以做到这一点。

许多方法,甚至更多工具

所以很清楚如何获取配置。每种方法都有自己的最佳实践。但是接下来该怎么办?没有共同的答案。

一些库(如viper将配置存储为全局区域中的键值集。非常有用,但非常隐含。在大型项目中,有时您可能会迷失一个或另一个变量如何进入此状态的方式。

其他库将所有内容放在一个存储桶中 - 环境、文件、命令行选项。即使有一个包含所有配置值的漂亮结构,对我来说仍然看起来不清楚和无用。通常,应用程序使用命令行方法(CLI 工具)或配置文件 + 环境方法(Web 服务、容器化应用程序等)。

有很多非常好的命令行参数处理库,比如go-flags,我认为没有理由将所有东西都放在一个工具中。

但是这两种方法都有同样的问题——里面有很多魔法。

魔术

Go 并不是适合所有人的语言。它有很多样板,类型系统不太容易原谅,OOP 的可能性非常有限。但是有一件好事是值得的——它是立即可读的,它没有内在的魔力。我喜欢这样。

但是反过来,使用配置通常很棘手。即使你定义了一个结构而不是使用一个不安全的映射,你仍然必须声明你使用了哪些环境变量。作为全局使用的环境变量,没有最糟糕的事情。但是即使结构中包含每个环境变量的显式映射,在应用程序之外仍然很难设置环境。有人(可能是您、其他开发人员、DevOps 或只是被迫这样做的人)需要找到使用的环境变量列表,然后正确使用它们。

好的,它们可以在文档或源代码中列出。但两者都很糟糕。文档可能已经过时,源代码需要时间和技巧来阅读。

在我看来,唯一正确的方法是在帮助输出中添加完整的环境变量列表,包括描述、默认值和其他有意义的信息。

我没有发现任何不复杂但仍然有用的东西,具有明确的配置设置和干净且信息丰富的帮助输出,所以我自己构建了它。

一种彻底统治它们的方法

因此,我分析了许多流行的配置管理库并决定构建自己的。为什么?有几个原因:

处理配置文件和环境变量,而不是命令行参数;

没有神奇的、明确的方式来阅读和使用配置;

没有隐式名称,所有映射都应使用标签完成;

好的和信息丰富的帮助输出;

易于与其他库集成。

我们在这里:cleanenv库现已发布并在生产中得到验证。

让我们谈谈它的主要特点。

显式配置结构

cleanenv 库背后的主要思想是让一切变得明确和干净。没有全局状态,没有封装的地图,没有神奇的背景更新,也没有任何其他隐藏的工作。

一切都变得清晰可见。这就是为什么选择结构化格式作为配置基础的原因。用于解析配置文件和环境变量的结构(如您所愿)。它也是编写配置文档的好地方。

显式命名和选项

下一个“不神奇”的步骤是变量名称的明确性。没有像基于嵌套结构名称的复杂环境名称生成器这样的东西 - 您必须按原样设置名称,因此您可以使用搜索轻松找到它们。

对于文件解析,我们使用与相应库相同的方法 - JSON、YAML、TOML 等。

下面是一个简单的服务器配置结构示例:

type ConfigDatabase struct {
    Port     string `yml:"port" env:"PORT" env-default:"5432"`
    Host     string `yml:"host" env:"HOST" env-default:"localhost"`
    Name     string `yml:"name" env:"NAME" env-default:"postgres"`
    User     string `yml:"user" env:"USER" env-default:"user"`
    Password string `yml:"password" env:"PASSWORD"`
}

进入全屏模式 退出全屏模式

envenv-default

支持的数据类型有:

  • 个整数;

  • 个浮点数;

  • 个字符串;

  • 个布尔值;

  • 个数组(带有可自定义的分隔符);

  • 个地图(带有可自定义的分隔符);

可读帮助输出

现代可容器化应用程序使用环境变量作为主要配置。因此,应用程序最多可以有数百个依赖的变量。常见的问题是变量的确切列表通常是不确定的或过时的(甚至有价值,它们通过应用程序分发并在一些意想不到的地方被读取)。

要修复 cleanenv 库包含将带有描述的结构良好的环境变量列表添加到帮助输出中的可能性:

import github.com/ilyakaznacheev/cleanenv

type ConfigServer struct {
    Port     string `env:"PORT" env-description:"server port"`
    Host     string `env:"HOST" env-description:"server host"`
}

var cfg ConfigRemote

help, err := cleanenv.GetDescription(&cfg, nil)
if err != nil {
    ...
}

进入全屏模式 退出全屏模式

您将获得以下信息:

Environment variables:
  PORT  server port
  HOST  server host

进入全屏模式 退出全屏模式

它将帮助您使文档与您的应用程序同步,而无需额外的文件。

积分

简单就是一切。但我不仅想提供一个简单的工具,而且还想使它易于与其他代码一起使用。

flag
type config struct {
    Port     string `env:"PORT" env-description:"server port" env-default:"5432"`
    Host     string `env:"HOST" env-description:"server host" env-default:"localhost"`
    Name     string `env:"NAME" env-description:"server name" env-default:"postgres"`
    User     string `env:"USER" env-description:"server username" env-default:"user"`
    Password string `env:"PASSWORD" env-description:"server password"`
}

var (
    cfg     config
    cfgPath string
)

fset := flag.NewFlagSet("My app", flag.ContinueOnError)
fset.StringVar(&cfgPath, "cfg", "", "path to config file")

fset.Usage = cleanenv.FUsage(fset.Output(), &cfg, nil, fset.Usage)

fset.Parse(os.Args[1:])

进入全屏模式 退出全屏模式

go run your_app.go -h
& go run your_app.go -h
Usage of My app:
  -cfg string
        path to config file

Environment variables:
  PORT string
        server port (default "5432")
  HOST string
        server host (default "localhost")
  NAME string
        server name (default "postgres")
  USER string
        server username (default "user")
  PASSWORD string
        server password

进入全屏模式 退出全屏模式

做更多的可能

这很好,但你可能需要更多。例如,您可能希望从远程服务器或其他工具获取配置,或更新它们。为此,您可以使用库的增强功能。有一些方法可以编写自己的逻辑来从自己的源中读取数据。在文档中阅读更多内容。

结论

所以cleanenv库并不是每把锁的钥匙。它绝对不是万能的工具。但它的设计目的是做一个简单、干净和可读的,但如果你需要的话,足够灵活。

GitHub 徽标ilyakaznacheev/cleanenv

✨干净简约的Golang环境配置阅读器

所以,如果它对你有帮助,我很高兴。我在我的项目中积极使用它,因此它是有效的。

此外,请随时请求您认为可能有帮助的任何功能。

并保持清洁!