【问题标题】:How to handle configuration in Go [closed]如何在 Go 中处理配置 [关闭]
【发布时间】:2013-05-04 03:51:43
【问题描述】:

我是 Go 编程的新手,我想知道:处理 Go 程序的配置参数的首选方法是什么(可能使用 properties 文件或 ini 文件,在其他情况下)?

【问题讨论】:

标签: go configuration-files


【解决方案1】:

JSON 格式非常适合我。这 标准库提供了编写缩进数据结构的方法,所以 可读。

另见this golang-nuts thread

JSON 的好处是解析和人类可读/可编辑相当简单 同时为列表和映射提供语义(可以变得非常方便),这 许多 ini 类型的配置解析器并非如此。

示例用法:

conf.json

{
    "Users": ["UserA","UserB"],
    "Groups": ["GroupA"]
}

读取配置的程序

import (
    "encoding/json"
    "os"
    "fmt"
)

type Configuration struct {
    Users    []string
    Groups   []string
}

file, _ := os.Open("conf.json")
defer file.Close()
decoder := json.NewDecoder(file)
configuration := Configuration{}
err := decoder.Decode(&configuration)
if err != nil {
  fmt.Println("error:", err)
}
fmt.Println(configuration.Users) // output: [UserA, UserB]

【讨论】:

  • 似乎 JSON 是当前替代方案中最不坏的。我研究了 go-yaml,这是一项勇敢的努力,但我认为缺乏文档表明我应该寻找其他地方。 goini 似乎是一个简单易用的库来处理 Windows ini 文件。已经提出了一种称为 TOML 的新格式,但它也是has problems。此时我会坚持使用 JSON 或 ini
  • YAML 支持 cmets,如果你想在配置文件的任何地方添加注释。
  • 对于那些阅读这篇文章并沿着那条路走下去的人,请注意:JSON 缺乏 cmets 使其不适合人类可用的配置文件 (imo)。它是一种数据交换格式 - 您可能会发现失去在配置文件中编写有用/描述性 cmets 的能力会损害可维护性(“为什么激活此设置?”、“它有什么作用?”、“它的有效值是什么? ?”等)。
  • Ahhh - 我在我的代码中尝试过,但忘记用大写字母定义结构属性(未导出) - 这花费了我一个小时的生命。也许其他人犯了同样的错误>被警告;D
  • 你可能应该在检查打开错误后defer file.Close()
【解决方案2】:

另一种选择是使用TOML,这是一种由 Tom Preston-Werner 创建的类似 INI 的格式。我built a Go parser for it 那是extensively tested。您可以像这里提出的其他选项一样使用它。例如,如果你在something.toml中有这个TOML数据

Age = 198
Cats = [ "Cauchy", "Plato" ]
Pi = 3.14
Perfection = [ 6, 28, 496, 8128 ]
DOB = 1987-07-05T05:45:00Z

然后你可以用类似的东西把它加载到你的 Go 程序中

type Config struct {
    Age int
    Cats []string
    Pi float64
    Perfection []int
    DOB time.Time
}

var conf Config
if _, err := toml.DecodeFile("something.toml", &conf); err != nil {
    // handle error
}

【讨论】:

  • 我喜欢 TOML,因为它让我可以在换行符上或在配置设置的行尾编写 cmets。我不能用 JSON 做到这一点。
  • 每次配置更新都需要更新代码,这很烦人。
  • 每一种配置方法都可以。您的程序如何知道新配置?
  • @BurntSushi5 代码不关心的 Toml 文件中会不会有额外的字段?我的意思是,新版本的配置文件可以与旧版本的代码一起使用吗?在我的情况下,忽略未使用的配置选项是可以的。
  • 我喜欢它。干得好。我个人认为管理员或客户更改 TOML 文件比更改 JSON 文件更容易。
【解决方案3】:

Viper 是一个支持 JSON、YAML 和 TOML 的 golang 配置管理系统。看起来很有趣。

【讨论】:

  • 特别适用于 12 因子应用程序12factor.net
  • 在 Go 中使用 gonfig 进行 JSON 配置。 github.com/eduardbcom/gonfig
  • 不要使用 Viper,它不是线程安全的,差点把我炒了。
  • @Dr.eel 在不同的 goroutine 中分别尝试 viper.GetBool("abc") 和 Viper.Set("abc", false)。
  • 您的配置可能应该在程序开始时加载,然后将值传递给您的依赖项。在单独的 goroutine 中加载配置可能是错误的方法。我永远不会期望配置加载库可以安全地并发使用。
【解决方案4】:

我通常将 JSON 用于更复杂的数据结构。缺点是你很容易得到一堆代码来告诉用户错误在哪里,各种边缘情况以及什么不是。

对于基本配置(api 密钥、端口号等),gcfg 软件包让我非常幸运。它基于 git config 格式。

来自文档:

示例配置:

; Comment line
[section]
name = value # Another comment
flag # implicit value for bool is true

转到结构:

type Config struct {
    Section struct {
            Name string
            Flag bool
    }
}

以及阅读它所需的代码:

var cfg Config
err := gcfg.ReadFileInto(&cfg, "myconfig.gcfg")

它还支持切片值,因此您可以允许多次指定一个键和其他类似的功能。

【讨论】:

  • gcfg的原作者停止了该项目并开始另一个相关的sconf
【解决方案5】:

只需使用标准的go flagsiniflags

转旗

import "flag"
var nFlag = flag.Int("n", 1234, "help message for flag n")

iniflags

package main

import (
  "flag"
  ...
  "github.com/vharitonsky/iniflags"
  ...
)

var (
  flag1 = flag.String("flag1", "default1", "Description1")
  ...
  flagN = flag.Int("flagN", 123, "DescriptionN")
)

func main() {
  iniflags.Parse()  // use instead of flag.Parse()
}

标准 go 标志有以下好处:

  • 惯用语。
  • 易于使用。可以轻松添加标志并将其分散在项目使用的任意包中。
  • 标志对默认值和描述具有开箱即用的支持。
  • 标志提供带有默认值和描述的标准“帮助”输出。

标准 go 标志的唯一缺点 - 当您的应用中使用的标志数量变得过多时会出现管理问题。

Iniflags 优雅地解决了这个问题:只需修改主包中的两行,它就神奇地获得了从 ini 文件中读取标志值的支持。可以通过在命令行中传递新值来覆盖 ini 文件中的标志。

详情请参阅https://groups.google.com/forum/#!topic/golang-nuts/TByzyPgoAQE

【讨论】:

  • 我开始为我一直在从事的项目(我的第一个从头开始的 golang 项目)使用标志,但我想知道如何处理诸如测试之类的事情?例如,这是一个 api 客户端,我想使用标志,但它似乎会使我的测试过于复杂(go test 不允许我传递标志)而配置文件不会。
  • 从测试中设置标志很容易:*FlagName = value
  • 如果这里有详细的示例代码显示一个工作示例将会非常有帮助:)
  • 当您需要与用其他语言编写的其他应用程序共享配置时,这不是一个好主意。
  • 会建议使用 pflags 而不是 flags。 pflags 使用的是 posix 标准
【解决方案6】:

我已经开始使用Gcfg,它使用类似 Ini 的文件。很简单——如果你想要一些简单的东西,这是一个不错的选择。

这是我当前使用的加载代码,它具有默认设置并允许覆盖我的一些配置的命令行标志(未显示):

package util

import (
    "code.google.com/p/gcfg"
)

type Config struct {
    Port int
    Verbose bool
    AccessLog string
    ErrorLog string
    DbDriver string
    DbConnection string
    DbTblPrefix string
}

type configFile struct {
    Server Config
}

const defaultConfig = `
    [server]
    port = 8000
    verbose = false
    accessLog = -
    errorLog  = -
    dbDriver     = mysql
    dbConnection = testuser:TestPasswd9@/test
    dbTblPrefix  =
`

func LoadConfiguration(cfgFile string, port int, verbose bool) Config {
    var err error
    var cfg configFile

    if cfgFile != "" {
        err = gcfg.ReadFileInto(&cfg, cfgFile)
    } else {
        err = gcfg.ReadStringInto(&cfg, defaultConfig)
    }

    PanicOnError(err)

    if port != 0 {
        cfg.Server.Port = port
    }
    if verbose {
        cfg.Server.Verbose = true
    }

    return cfg.Server
}

【讨论】:

  • 这不正是 Ask 已经提到的吗?
【解决方案7】:

看看gonfig

// load
config, _ := gonfig.FromJson(myJsonFile)
// read with defaults
host, _ := config.GetString("service/host", "localhost")
port, _ := config.GetInt("service/port", 80)
test, _ := config.GetBool("service/testing", false)
rate, _ := config.GetFloat("service/rate", 0.0)
// parse section into target structure
config.GetAs("service/template", &template)

【讨论】:

  • 这个很好,因为我不必在 go 中重新定义整个配置结构
【解决方案8】:

【讨论】:

  • Viper + Cobra 是一个强大的组合。
【解决方案9】:

使用toml喜欢这篇文章Reading config files the Go way

【讨论】:

    【解决方案10】:

    我用golang写了一个简单的ini配置库。

    https://github.com/c4pt0r/cfg

    goroutine 安全,易于使用

    package cfg
    import (
        "testing"
    )
    
    func TestCfg(t *testing.T) {
        c := NewCfg("test.ini")
        if err := c.Load() ; err != nil {
            t.Error(err)
        }
        c.WriteInt("hello", 42)
        c.WriteString("hello1", "World")
    
        v, err := c.ReadInt("hello", 0)
        if err != nil || v != 42 {
            t.Error(err)
        }
    
        v1, err := c.ReadString("hello1", "")
        if err != nil || v1 != "World" {
            t.Error(err)
        }
    
        if err := c.Save(); err != nil {
            t.Error(err)
        }
    }
    

    ===================更新========================

    最近我需要一个支持section的INI解析器,我写了一个简单的包:

    github.com/c4pt0r/cfg
    

    你可以像使用“flag”包一样解析INI:

    package main
    
    import (
        "log"
        "github.com/c4pt0r/ini"
    )
    
    var conf = ini.NewConf("test.ini")
    
    var (
        v1 = conf.String("section1", "field1", "v1")
        v2 = conf.Int("section1", "field2", 0)
    )
    
    func main() {
        conf.Parse()
    
        log.Println(*v1, *v2)
    }
    

    【讨论】:

      【解决方案11】:

      您可能还对go-libucl 感兴趣,这是一组用于 UCL(通用配置语言)的 Go 绑定。 UCL 有点像 JSON,但对人类有更好的支持:它支持 cmets 和人类可读的结构,如 SI 乘数(10k、40M 等),并且样板文件少一些(例如,键周围的引号)。如果您已经熟悉的话,它实际上非常接近 nginx 配置文件格式。

      【讨论】:

        【解决方案12】:

        我同意nemo,我写了一个小工具来让这一切变得非常简单。

        bitbucket.org/gotamer/cfg是一个json配置包

        • 您将应用程序中的配置项定义为结构。
        • 结构中的 json 配置文件模板会在第一次运行时保存
        • 您可以将运行时修改保存到配置中

        查看 doc.go 示例

        【讨论】:

          【解决方案13】:

          我试过 JSON。有效。但我讨厌必须创建我可能设置的确切字段和类型的结构。对我来说那是一种痛苦。我注意到这是我能找到的所有配置选项使用的方法。也许我在动态语言方面的背景让我对这种冗长的好处视而不见。我制作了一个新的简单配置文件格式,以及一个更动态的 lib 用于读取它。

          https://github.com/chrisftw/ezconf

          我对 Go 世界很陌生,所以它可能不是 Go 方式。但它确实有效,速度非常快,而且使用起来超级简单。

          优点

          • 超级简单
          • 更少的代码

          缺点

          • 没有数组或映射类型
          • 非常扁平的文件格式
          • 非标准配置文件
          • 确实有一个内置的小约定,如果我现在在 Go 社区中普遍不赞成的话。 (在config目录中查找配置文件)

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2015-08-02
            • 2017-03-27
            • 2011-11-26
            • 1970-01-01
            • 2012-11-14
            • 2013-08-30
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多