ежедневная библиотека Viper of Go

Go

Введение

Предыдущая статьяВведениеcobraупоминается, когдаviper, Сегодня мы познакомим вас с этой библиотекой. viper — это конфигурационное решение с богатыми возможностями:

  • Поддержка файлов конфигурации в различных форматах, таких как свойства JSON/TOML/YAML/HCL/envfile/Java;
  • Вы можете настроить монитор для изменения файла конфигурации, автоматически загружать новую измененную конфигурацию;
  • Из переменных среды и параметров командной строкиio.Readerпрочитать конфигурацию;
  • Чтение и мониторинг изменений из систем удаленной настройки, таких как etcd/Consul;
  • Установленное значение ключа отображается в логике кода.

быстрый в использовании

Установить:

$ go get github.com/spf13/viper

использовать:

package main

import (
  "fmt"
  "log"

  "github.com/spf13/viper"
)

func main() {
  viper.SetConfigName("config")
  viper.SetConfigType("toml")
  viper.AddConfigPath(".")
  viper.SetDefault("redis.port", 6381)
  err := viper.ReadInConfig()
  if err != nil {
    log.Fatal("read config failed: %v", err)
  }

  fmt.Println(viper.Get("app_name"))
  fmt.Println(viper.Get("log_level"))

  fmt.Println("mysql ip: ", viper.Get("mysql.ip"))
  fmt.Println("mysql port: ", viper.Get("mysql.port"))
  fmt.Println("mysql user: ", viper.Get("mysql.user"))
  fmt.Println("mysql password: ", viper.Get("mysql.password"))
  fmt.Println("mysql database: ", viper.Get("mysql.database"))

  fmt.Println("redis ip: ", viper.Get("redis.ip"))
  fmt.Println("redis port: ", viper.Get("redis.port"))
}

прежде чем мы используемGo ежедневная библиотека go-iniКонфигурация, использованная в этой статье, изменена на формат toml. Синтаксис toml очень прост, см. краткое руководство.learn X in Y minutes.

app_name = "awesome web"

# possible values: DEBUG, INFO, WARNING, ERROR, FATAL
log_level = "DEBUG"

[mysql]
ip = "127.0.0.1"
port = 3306
user = "dj"
password = 123456
database = "awesome"

[redis]
ip = "127.0.0.1"
port = 7381

Использование Viper очень просто и требует минимальной настройки. установить имя файла (SetConfigName), тип конфигурации (SetConfigType) и путь поиска (AddConfigPath), то звонитеReadInConfig. viper автоматически прочитает конфигурацию в соответствии с типом. вызывается при использованииviper.Getметод получения значения ключа.

Скомпилируйте и запустите программу:

awesome web
DEBUG
mysql ip:  127.0.0.1
mysql port:  3306
mysql user:  dj
mysql password:  123456
mysql database:  awesome
redis ip:  127.0.0.1
redis port:  7381

Несколько замечаний:

  • Не используйте суффикс при установке имени файла;
  • Можно задать несколько путей поиска, и viper будет искать в соответствии с установленным порядком;
  • VIPER используется при получении значенияsection.key, то есть передать имя вложенного ключа;
  • Значение по умолчанию можно назватьviper.SetDefaultнастраивать.

прочитать ключ

viper предоставляет несколько форм методов чтения. В приведенном выше примере мы видимGetиспользование метода.Getметод возвращаетinterface{}стоимость, неудобно пользоваться.

GetTypeМетоды серии могут возвращать значение указанного типа. где Тип может бытьBool/Float64/Int/String/Time/Duration/IntSlice/StringSlice. Но обратите внимание, чтоЕсли указанный ключ не существует или имеет неправильный тип,GetTypeМетод возвращает нулевое значение соответствующего типа.

Если вы хотите определить, существует ли ключ, используйтеIsSetметод. Кроме того,GetStringMapиGetStringMapStringНепосредственно используйте карту, чтобы вернуть все пары ключ-значение под ключом, первый возвращаетmap[string]interface{}, последний возвращаетсяmap[string]string.AllSettingsотmap[string]interface{}Возвращает все настройки.

// 省略包名和 import 部分

func main() {
  viper.SetConfigName("config")
  viper.SetConfigType("toml")
  viper.AddConfigPath(".")
  err := viper.ReadInConfig()
  if err != nil {
    log.Fatal("read config failed: %v", err)
  }

  fmt.Println("protocols: ", viper.GetStringSlice("server.protocols"))
  fmt.Println("ports: ", viper.GetIntSlice("server.ports"))
  fmt.Println("timeout: ", viper.GetDuration("server.timeout"))

  fmt.Println("mysql ip: ", viper.GetString("mysql.ip"))
  fmt.Println("mysql port: ", viper.GetInt("mysql.port"))

  if viper.IsSet("redis.port") {
    fmt.Println("redis.port is set")
  } else {
    fmt.Println("redis.port is not set")
  }

  fmt.Println("mysql settings: ", viper.GetStringMap("mysql"))
  fmt.Println("redis settings: ", viper.GetStringMap("redis"))
  fmt.Println("all settings: ", viper.AllSettings())
}

Мы добавили в файл конфигурации config.tomlprotocolsиportsКонфигурация:

[server]
protocols = ["http", "https", "port"]
ports = [10000, 10001, 10002]
timeout = 3s

Скомпилируйте и запустите программу, вывод:

protocols:  [http https port]
ports:  [10000 10001 10002]
timeout:  3s
mysql ip:  127.0.0.1
mysql port:  3306
redis.port is set
mysql settings:  map[database:awesome ip:127.0.0.1 password:123456 port:3306 user:dj]
redis settings:  map[ip:127.0.0.1 port:7381]
all settings:  map[app_name:awesome web log_level:DEBUG mysql:map[database:awesome ip:127.0.0.1 password:123456 port:3306 user:dj] redis:map[ip:127.0.0.1 port:7381] server:map[ports:[10000 10001 10002] protocols:[http https port]]]

Если вы настроитеredis.portзакомментировано, будет выведеноredis.port is not set.

Как использовать его в примере вышеtime.DurationТип, пока этоtime.ParseDurationПринятые форматы в порядке, например.3s,2min,1min30sЖдать.

установить значение ключа

viper поддерживает настройки в нескольких местах, читаемые последовательно в следующем порядке:

  • перечислитьSetНастройки отображения;
  • параметры командной строки;
  • переменные окружения;
  • конфигурационный файл;
  • По умолчанию.

viper.Set

Если ключ проходитviper.SetУстановите значение, тогда это значение имеет наивысший приоритет.

viper.Set("redis.port", 5381)

Если вы поместите приведенную выше строку кода в программу и запустите программу, выводredis.portбудет 5381.

параметры командной строки

Если ключ не проходитviper.SetОтобразите значение параметра, затем fetch попытается прочитать параметры командной строки. Если доступно, используйте его в первую очередь. viper использует библиотеку pflag для разбора опций. мы первыеinitОпределите параметры в методе и вызовитеviper.BindPFlagsПривяжите параметры к конфигурации:

func init() {
  pflag.Int("redis.port", 8381, "Redis port to connect")

  // 绑定命令行
  viper.BindPFlags(pflag.CommandLine)
}

Затем вmainвызов в начале методаpflag.ParseВарианты разбора.

Скомпилируйте и запустите программу:

$ ./main.exe --redis.port 9381
awesome web
DEBUG
mysql ip:  127.0.0.1
mysql port:  3306
mysql user:  dj
mysql password:  123456
mysql database:  awesome
redis ip:  127.0.0.1
redis port:  9381

Как не пройти мимо вариантов:

$ ./main.exe
awesome web
DEBUG
mysql ip:  127.0.0.1
mysql port:  3306
mysql user:  dj
mysql password:  123456
mysql database:  awesome
redis ip:  127.0.0.1
redis port:  7381

Обратите внимание, что параметры здесь не используются.redis.portзначение по умолчанию для .

Однако, если значение ключа недоступно с помощью приведенных ниже методов, верните значение параметра по умолчанию (если есть). Попробуйте закомментировать файл конфигурацииredis.portПроверьте эффект.

переменная среды

Если значение ключа не получено ранее, он попытается прочитать его из переменной окружения. Мы можем связать их по одному или автоматически связать их все.

существуетinitвызов методаAutomaticEnvМетод связывает все переменные окружения:

func init() {
  // 绑定环境变量
  viper.AutomaticEnv()
}

Чтобы проверить успешность привязки, мыmainПеременная окружения GOPATH выводится в методе:

func main() {
  // 省略部分代码

  fmt.Println("GOPATH: ", viper.Get("GOPATH"))
}

Через System -> Advanced Settings -> New Создайте новый файл с именемredis.portпеременная окружения со значением 10381. запустить программу, вывестиredis.portЗначение равно 10381, и в выходных данных есть информация GOPATH.

Вы также можете привязать переменные среды по отдельности:

func init() {
  // 绑定环境变量
  viper.BindEnv("redis.port")
  viper.BindEnv("go.path", "GOPATH")
}

func main() {
  // 省略部分代码
  fmt.Println("go path: ", viper.Get("go.path"))
}

перечислитьBindEnvметод, если передается только один параметр, этот параметр представляет как имя ключа, так и имя переменной среды. Если передаются два параметра, первый параметр представляет имя ключа, а второй параметр представляет имя переменной среды.

также черезviper.SetEnvPrefixметод для установки префикса переменной среды, чтобы черезAutomaticEnvи параметрBindEnvсвязанные переменные окружения, в настоящее время используетGet, viper автоматически добавит этот префикс и найдет его в переменных окружения.

Если соответствующая переменная среды не существует, viper автоматически преобразует все имена ключей в верхний регистр и повторит поиск. Итак, используйте ключевое имяgopathПеременные среды также могут быть прочитаныGOPATHзначение .

конфигурационный файл

Если ключ не может быть найден по предыдущим путям, viper попытается найти его в файле конфигурации. Во избежание влияния переменных окружения нужно удалитьredis.portэта переменная окружения.

Смотретьбыстрый в использованииВ примерах.

По умолчанию

вышебыстрый в использованииВ предыдущем разделе мы видели, как устанавливать значения по умолчанию, поэтому я не буду повторять их здесь.

чтение конфигурации

отio.Readerчитать в

вайпер поддерживает отio.ReaderПрочитайте конфигурацию в. Эта форма является гибкой, и источником может быть файл, строка, сгенерированная программой, или даже поток байтов, прочитанный из сетевого подключения.

package main

import (
  "bytes"
  "fmt"
  "log"

  "github.com/spf13/viper"
)

func main() {
  viper.SetConfigType("toml")
  tomlConfig := []byte(`
app_name = "awesome web"

# possible values: DEBUG, INFO, WARNING, ERROR, FATAL
log_level = "DEBUG"

[mysql]
ip = "127.0.0.1"
port = 3306
user = "dj"
password = 123456
database = "awesome"

[redis]
ip = "127.0.0.1"
port = 7381
`)
  err := viper.ReadConfig(bytes.NewBuffer(tomlConfig))
  if err != nil {
    log.Fatal("read config failed: %v", err)
  }

  fmt.Println("redis port: ", viper.GetInt("redis.port"))
}

Unmarshal

поддержка вайпер настроитUnmarshalв структуру и присваивать значения соответствующим полям в структуре.

package main

import (
  "fmt"
  "log"

  "github.com/spf13/viper"
)

type Config struct {
  AppName  string
  LogLevel string

  MySQL    MySQLConfig
  Redis    RedisConfig
}

type MySQLConfig struct {
  IP       string
  Port     int
  User     string
  Password string
  Database string
}

type RedisConfig struct {
  IP   string
  Port int
}

func main() {
  viper.SetConfigName("config")
  viper.SetConfigType("toml")
  viper.AddConfigPath(".")
  err := viper.ReadInConfig()
  if err != nil {
    log.Fatal("read config failed: %v", err)
  }

  var c Config
  viper.Unmarshal(&c)

  fmt.Println(c.MySQL)
}

Компилируем, запускаем программу, выводим:

{127.0.0.1 3306 dj 123456 awesome}

сохранить конфигурацию

Иногда мы хотим сохранить конфигурацию, сгенерированную в программе, или внесенные изменения. viper предоставляет интерфейс!

  • WriteConfig: Напишите текущую конфигурацию Viper до заранее определенного пути или вернуть ошибку, если нет предопределенного пути. перезаписать текущую конфигурацию;
  • SafeWriteConfig: Та же функция, что и выше, но если файл конфигурации существует, он не будет перезаписан;
  • WriteConfigAs: Сохраните конфигурацию по указанному пути, если файл существует, он будет перезаписан;
  • SafeWriteConfigAs: та же функция, что и выше, но если файл конфигурации общего доступа существует, он не будет перезаписан.

Далее мы программно генерируемconfig.tomlКонфигурация:

package main

import (
  "log"

  "github.com/spf13/viper"
)

func main() {
  viper.SetConfigName("config")
  viper.SetConfigType("toml")
  viper.AddConfigPath(".")

  viper.Set("app_name", "awesome web")
  viper.Set("log_level", "DEBUG")
  viper.Set("mysql.ip", "127.0.0.1")
  viper.Set("mysql.port", 3306)
  viper.Set("mysql.user", "root")
  viper.Set("mysql.password", "123456")
  viper.Set("mysql.database", "awesome")

  viper.Set("redis.ip", "127.0.0.1")
  viper.Set("redis.port", 6381)

  err := viper.SafeWriteConfig()
  if err != nil {
    log.Fatal("write config failed: ", err)
  }
}

Скомпилируйте и запустите программу, сгенерированные файлы выглядят следующим образом:

app_name = "awesome web"
log_level = "DEBUG"

[mysql]
  database = "awesome"
  ip = "127.0.0.1"
  password = "123456"
  port = 3306
  user = "root"

[redis]
  ip = "127.0.0.1"
  port = 6381

Отслеживание изменений файлов

VIPER может контролировать модификации файлов и конфигурации горячей нагрузки. Таким образом, нет необходимости перезапустить сервер для конфигурации, чтобы вступить в силу.

package main

import (
  "fmt"
  "log"
  "time"

  "github.com/spf13/viper"
)

func main() {
  viper.SetConfigName("config")
  viper.SetConfigType("toml")
  viper.AddConfigPath(".")
  err := viper.ReadInConfig()
  if err != nil {
    log.Fatal("read config failed: %v", err)
  }

  viper.WatchConfig()

  fmt.Println("redis port before sleep: ", viper.Get("redis.port"))
  time.Sleep(time.Second * 10)
  fmt.Println("redis port after sleep: ", viper.Get("redis.port"))
}

просто позвониviper.WatchConfig, viper будет автоматически отслеживать изменения конфигурации. Если есть изменения, перезагрузите конфигурацию.

В приведенной выше программе мы сначала печатаемredis.portзначение, тоSleep10 с. За это время изменить конфигурациюredis.portзначение ,SleepРаспечатайте снова, когда закончите. Discovery выводит измененное значение:

redis port before sleep:  7381
redis port after sleep:  73810

Кроме того, вы можете добавить обратный вызов для изменения конфигурации:

viper.OnConfigChange(func(e fsnotify.Event) {
  fmt.Printf("Config file:%s Op:%s\n", e.Name, e.Op)
})

Этот обратный вызов будет выполняться при изменении файла.

использование гадюкиfsnotifyВ этой библиотеке реализована функция мониторинга модификаций файлов.

Полный пример кода см.GitHub.

Ссылаться на

  1. viperРепозиторий GitHub

я

мой блог

Добро пожаловать, чтобы обратить внимание на мою общедоступную учетную запись WeChat [GoUpUp], учитесь вместе и добивайтесь прогресса вместе ~

Эта статья опубликована в блогеOpenWriteвыпуск!