Cách xử lý cấu hình trong Go [kín]


284

Tôi mới tham gia lập trình Go và tôi tự hỏi: cách ưu tiên để xử lý các tham số cấu hình cho chương trình Go (loại nội dung nào người ta có thể sử dụng tệp thuộc tính hoặc tệp ini cho, trong các ngữ cảnh khác)?


Tôi cũng bắt đầu một chủ đề golang-nut có một vài ý tưởng bổ sung.
theglauber

2
Tôi có xu hướng sử dụng các kịch bản shell và các biến môi trường.
đúng

3
Tôi đã dành toàn bộ một bài đăng trên blog Cấu hình ứng dụng bền bỉ trong đó tôi đã giải thích cách thực hiện với các ví dụ cho hai định dạng phổ biến nhất: json và YAML. Các ví dụ đã sẵn sàng sản xuất.
upitau

Chỉ để ghi nhận có HCL từ HashiCorp hỗ trợ bình luận và tương thích JSON và UCL. github.com/hashicorp/hcl
Kaveh Shahbazian

Câu trả lời:


244

Các JSON định dạng làm việc cho tôi khá tốt. Thư viện chuẩn cung cấp các phương thức để viết cấu trúc dữ liệu thụt vào, vì vậy nó khá dễ đọc.

Xem thêm chủ đề golang-hạt này .

Lợi ích của JSON là khá đơn giản để phân tích cú pháp và có thể đọc / chỉnh sửa được con người trong khi cung cấp ngữ nghĩa cho danh sách và ánh xạ (có thể trở nên khá tiện dụng), điều này không xảy ra với nhiều trình phân tích cấu hình kiểu ini.

Ví dụ sử dụng:

conf.json :

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

Chương trình đọc cấu hình

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]

6
Có vẻ như JSON là kém nhất trong số các lựa chọn thay thế hiện tại. Tôi đã xem xét go-yaml và đó là một nỗ lực dũng cảm, nhưng tôi đã thiếu tài liệu như một dấu hiệu cho thấy tôi nên tìm ở nơi khác. goini dường như là một thư viện đơn giản và dễ dàng để xử lý các tệp Windows ini . Một định dạng mới gọi là TOML đã được đề xuất, nhưng nó cũng có vấn đề . Tại thời điểm này tôi sẽ gắn bó với JSON hoặc ini .
theglauber

6
YAML hỗ trợ bình luận, nếu bạn muốn thêm ghi chú ở mọi nơi trong tập tin cấu hình.
Ivan Black

42
Đối với những người đọc và đi theo tuyến đường đó, hãy cẩn thận: JSON thiếu bình luận làm cho nó không phù hợp với tệp cấu hình có thể sử dụng được của con người (imo). Đây là định dạng trao đổi dữ liệu - bạn có thể thấy mất khả năng viết nhận xét hữu ích / mô tả trong tệp cấu hình có thể ảnh hưởng đến khả năng bảo trì ("tại sao cài đặt này được kích hoạt?", "Nó làm gì?", "Giá trị hợp lệ của nó là gì? ?" Vân vân).
Dary Tâm trạng

6
Ahhh - Tôi đã thử điều đó trong mã của mình và quên xác định các thuộc tính struct bằng chữ in hoa (không xuất) - điều này khiến tôi mất một giờ trong cuộc đời. Có thể những người khác phạm lỗi tương tự> được cảnh báo; D
JohnGalt

6
Có lẽ bạn nên defer file.Close()sau khi kiểm tra lỗi mở
Gabriel

97

Một tùy chọn khác là sử dụng TOML , đây là định dạng giống INI được tạo bởi Tom Preston-Werner. Tôi đã xây dựng một trình phân tích cú pháp Go cho nó được thử nghiệm rộng rãi . Bạn có thể sử dụng nó như các tùy chọn khác được đề xuất ở đây. Ví dụ: nếu bạn có dữ liệu TOML này trongsomething.toml

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

Sau đó, bạn có thể tải nó vào chương trình Go của mình với nội dung như

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
}

18
Tôi thích TOML vì nó cho phép tôi viết bình luận trên dòng mới hoặc ở cuối cài đặt cấu hình dòng. Tôi không thể làm điều đó với JSON.
serserg

Mỗi bản cập nhật cấu hình yêu cầu cập nhật mã là những gì rất khó chịu.
hywak

4
Mọi cách tiếp cận để cấu hình. Làm thế nào khác chương trình của bạn sẽ nhận thức được cấu hình mới?
BurntSushi5

@ BurntSushi5 có thể có thêm các trường trong tệp Toml mà mã không quan tâm không? Ý tôi là, phiên bản mới hơn của tệp cấu hình có thể được sử dụng với phiên bản mã cũ hơn không? Trong trường hợp của tôi, bỏ qua các tùy chọn cấu hình không sử dụng.
1952500

2
tôi thích nó. Làm tốt lắm. Cá nhân tôi nghĩ rằng quản trị viên hoặc khách hàng dễ dàng thay đổi tệp TOML hơn JSON.
blndev

49

Viper là một hệ thống quản lý cấu hình golang hoạt động với JSON, YAML và TOML. Có vẻ khá thú vị.


1
Đặc biệt khả thi cho các ứng dụng 12factor 12factor.net
DerKnorr

Sử dụng gonfig cho cấu hình JSON trong Go. github.com/eduardbcom/gonfig
Eduard Bondarenko

1
Không sử dụng Viper, nó không an toàn cho chủ đề gần như đã bắn tôi.
igonejack

@igonejack Vui lòng cung cấp một ví dụ về việc Viper cắn bạn ở đâu?
Dr.eel

1
@ Dr.eel Hãy thử viper.GetBool ("abc") và Viper.Set ("abc", false) trong goroutine khác nhau.
igonejack

44

Tôi thường sử dụng JSON cho các cấu trúc dữ liệu phức tạp hơn. Nhược điểm là bạn dễ dàng kết thúc với một loạt mã để báo cho người dùng biết lỗi ở đâu, các trường hợp cạnh khác nhau và những gì không.

Đối với cấu hình cơ sở (khóa api, số cổng, ...) Tôi đã rất may mắn với gói gcfg . Nó dựa trên định dạng cấu hình git.

Từ tài liệu:

Cấu hình mẫu:

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

Đi cấu trúc:

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

Và mã cần thiết để đọc nó:

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

Nó cũng hỗ trợ các giá trị lát, vì vậy bạn có thể cho phép chỉ định một khóa nhiều lần và các tính năng hay khác như thế.


4
Tác giả ban đầu của gcfg đã ngừng dự án và bắt đầu một sconf khác có liên quan .
iwat

39

Chỉ cần sử dụng cờ đi tiêu chuẩn với iniflags .

Cờ cờ tiêu chuẩn có những lợi ích sau:

  • Thành ngữ.
  • Dễ sử dụng. Cờ có thể dễ dàng được thêm và phân tán trên các gói tùy ý mà dự án của bạn sử dụng.
  • Cờ có hỗ trợ ngoài luồng cho các giá trị và mô tả mặc định.
  • Cờ cung cấp đầu ra 'trợ giúp' tiêu chuẩn với các giá trị và mô tả mặc định.

Các cờ cờ tiêu chuẩn hạn chế duy nhất có - là các vấn đề về quản lý khi số lượng cờ được sử dụng trong ứng dụng của bạn trở nên quá lớn.

Iniflags giải quyết vấn đề này một cách tao nhã: chỉ cần sửa đổi hai dòng trong gói chính của bạn và nó sẽ hỗ trợ một cách kỳ diệu cho việc đọc các giá trị cờ từ tệp ini. Cờ từ các tệp ini có thể được ghi đè bằng cách chuyển các giá trị mới trong dòng lệnh.

Xem thêm https://groups.google.com/forum/#!topic/golang-nuts/TByzyPgoAQE để biết chi tiết.


Tôi đã bắt đầu sử dụng cờ cho một dự án mà tôi đang thực hiện (dự án golang đầu tiên của tôi), nhưng tôi tự hỏi làm thế nào để xử lý những thứ như các bài kiểm tra? Ví dụ: đây là ứng dụng khách api và tôi muốn sử dụng cờ, nhưng có vẻ như nó sẽ làm phức tạp quá trình kiểm tra của tôi ( go testkhông cho phép tôi chuyển cờ) trong khi tệp cấu hình sẽ không.
zachaysan

cài đặt cờ từ các bài kiểm tra rất dễ dàng:*FlagName = value
Steven Soroka

9
sẽ rất hữu ích nếu có mã ví dụ chi tiết ở đây hiển thị một ví dụ hoạt động :)
zero_cool

Không phải là một ý tưởng tốt khi bạn cần chia sẻ cấu hình với các phần khác của ứng dụng được viết bằng các ngôn ngữ khác.
Kirzilla

sẽ đề nghị sử dụng pflags thay vì cờ. pflags đang sử dụng tiêu chuẩn posix
Fjolnir Dvorak

12

Tôi đã bắt đầu sử dụng Gcfg sử dụng các tệp giống như Ini. Thật đơn giản - nếu bạn muốn một cái gì đó đơn giản, đây là một lựa chọn tốt.

Đây là mã tải tôi hiện đang sử dụng, có cài đặt mặc định và cho phép cờ dòng lệnh (không hiển thị) ghi đè lên một số cấu hình của tôi:

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
}

2
Đây không phải là chính xác những gì Ask đã đề cập?
nemo

8

có một cái nhìn tại 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)

Điều này là tốt, vì tôi không phải xác định lại toàn bộ cấu trúc cấu hình trong go
thanhpk



5

Tôi đã viết một thư viện cấu hình ini đơn giản trong golang.

https://github.com/c4pt0r/cfg

goroutine an toàn, dễ sử dụng

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)
    }
}

=================== Cập nhật =======================

Gần đây tôi cần một trình phân tích cú pháp INI với phần hỗ trợ và tôi viết một gói đơn giản:

github.com/c4pt0r/cfg

bạn có thể phân tích INI như sử dụng gói "cờ":

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)
}

4

Bạn cũng có thể quan tâm đến go-libucl , một tập hợp các ràng buộc Go cho UCL, Ngôn ngữ cấu hình phổ quát. UCL hơi giống JSON, nhưng với sự hỗ trợ tốt hơn cho con người: nó hỗ trợ các bình luận và các cấu trúc có thể đọc được của con người như hệ số nhân SI (10k, 40M, v.v.) và có một ít luồn lách hơn (ví dụ, trích dẫn xung quanh các phím). Nó thực sự khá gần với định dạng tệp cấu hình nginx, nếu bạn đã quen với điều đó.


2

Tôi đồng ý với nemo và tôi đã viết một công cụ nhỏ để làm cho tất cả thực sự dễ dàng.

bitbucket.org/gotamer/cfg là gói cấu hình json

  • Bạn xác định các mục cấu hình trong ứng dụng của mình dưới dạng cấu trúc.
  • Mẫu tệp cấu hình json từ cấu trúc của bạn được lưu trong lần chạy đầu tiên
  • Bạn có thể lưu các sửa đổi thời gian chạy vào cấu hình

Xem doc.go cho một ví dụ


1

Tôi đã thử JSON. Nó đã làm việc. Nhưng tôi ghét phải tạo cấu trúc của các trường và loại chính xác mà tôi có thể đang thiết lập. Đối với tôi đó là một nỗi đau. Tôi nhận thấy đó là phương pháp được sử dụng bởi tất cả các tùy chọn cấu hình mà tôi có thể tìm thấy. Có lẽ nền tảng của tôi trong các ngôn ngữ năng động làm cho tôi mù quáng về lợi ích của tính dài dòng như vậy. Tôi đã tạo một định dạng tệp cấu hình đơn giản mới và một lib-ish động hơn để đọc nó.

https://github.com/chrisftw/ezconf

Tôi khá mới mẻ với thế giới Go, vì vậy nó có thể không phải là con đường Go. Nhưng nó hoạt động, nó khá nhanh và sử dụng siêu đơn giản.

Ưu

  • Siêu đơn giản
  • Ít mã hơn

Nhược điểm

  • Không có Mảng hoặc loại Bản đồ
  • Định dạng tập tin rất phẳng
  • Các tập tin conf không chuẩn
  • Có một quy ước nhỏ được tích hợp sẵn, mà bây giờ tôi sẽ cau mày nói chung trong cộng đồng Go. (Tìm tập tin cấu hình trong thư mục cấu hình)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.