catalogue
Viper
Viper is a third-party library of Golang, Github: https://github.com/spf13/viper . It is used to process the configuration information of Golang program. It can process configuration files in various formats.
Viper supports the following features:
- Set default values for configuration items.
- Load and parse configuration files in JSON, TOML, YAML, HCL or Java properties format.
- You can read the configuration in the command line parameters and specify the overwritten configuration value, which is often used with Cobra.
- Configuration data can be read from environment variables.
- Data can be read from the remote configuration system and monitored (e.g. ETCD, consult).
- The configuration can be read from the Buffer.
- You can easily distinguish the difference between the command line parameters or configuration files provided by the user and the default values.
- You can monitor the changes of the configuration file and re read the configuration file.
- An alias system is provided, which can rename parameters without destroying existing code.
- Call function to set configuration information
The priority order of Viper reading configuration information, from high to low:
- Explicitly call the Set function.
- Command line arguments.
- Environment variables.
- Configuration file.
- Remote key/value storage system.
- Default value
In short, Viper allows developers to focus on and implement innovative business logic without worrying about the format of configuration files.
Note: the key of Viper configuration item is not case sensitive.
Use of Viper
Set default values
The default value is not required, but it is recommended. If Set function, command line parameters, environment variables, configuration files and remote configuration system are not specified, the default value will take effect.
Example:
viper.SetDefault("name", "xiaoming") viper.SetDefault("age", "12") viper.SetDefault("notifyList", []string{"xiaohong", "xiaoli", "xiaowang"})
Explicitly set key values
If a key passes viper If the set method sets a value, this value has the highest priority.
viper.Set("redis.port", 5381)
Read configuration from command line parameters
If a key does not pass viper If the set method explicitly sets the value, it will try to read from the command line parameters when getting. Viper uses the pflag library to parse command line options.
- Command line
go run test.go --ip=192.168.7.3 --port=3306
- code
package main import ( "fmt" "github.com/spf13/pflag" "github.com/spf13/viper" ) func main() { // Define command line options pflag.String("ip", "127.0.0.1", "Server running address") pflag.Int64("port", 8080, "Server running port") // Parse command line options. pflag.Parse() // Bind options to viper. viper.BindPFlags(pflag.CommandLine) fmt.Printf("ip :%s , port:%s", viper.GetString("ip"), viper.GetString("port")) }
Read configuration from environment variable
If the key value is not obtained in the previous methods, it will try to read from the environment variable.
In Golang, the os package is usually used to obtain environment variables, such as:
getenv := os.Getenv("JAVA_HOME") fmt.Print(getenv)
Viper provides a unique way:
// Bind all environment variables. viper.AutomaticEnv() // Verify whether the binding is successful. fmt.Println("GOPATH: ", viper.Get("GOPATH")) // Read possible environment variables. if env := viper.Get("JAVA_HOME"); env == nil { println("error!") } else { fmt.Printf("%#v\n", env) }
You can also bind the specified environment variables separately:
func init() { // Bind specific environment variables viper.BindEnv("redis.port") viper.BindEnv("go.path", "GOPATH") } func main() { fmt.Println("go path: ", viper.Get("go.path")) }
BindEnv method. If only one parameter is passed in, this parameter represents both the key name and the environment variable name; If two parameters are passed in, the first parameter represents the key name and the second parameter represents the environment variable name.
You can also use viper The setenvprefix method sets the prefix of the environment variable. In this way, the environment variables bound by the AutomaticEnv method and BindEnv method,
When using the Get method, Viper will automatically add this prefix and look it up from the environment variable. If the corresponding environment variable does not exist, Viper will automatically convert all key names to uppercase and look it up again.
Read configuration from configuration file
Reading configuration from configuration file belongs to io Reader read configuration. From Io The form of reading configuration in reader is very flexible. The source can be a file, a string generated in the program, or even a byte stream that can be read from a network connection (remote configuration system).
Viper can specify to search configuration files from multiple paths and supports JSON, TOML, YAML, HCL and Java properties format files. However, at present, a single Viper instance only supports a single configuration file. By default, Viper does not search for any paths. Therefore, the path is not required, but it is recommended to provide at least one path if a configuration file is used.
Example:
viper.SetConfigName("dbConfig") // Set the configuration file name without suffix. viper.AddConfigPath("/workspace/path1/") // First search path. viper.AddConfigPath("/workspace/path2/") // Multiple paths can be added and will be found in order. viper.AddConfigPath(".") // . indicates the current directory. err := viper.ReadInConfig() // Search the path defined above and read the configuration data. if err != nil { panic(fmt.Errorf("Fatal error config file: %s \n", err)) }
After reading the contents of the configuration file, Viper will automatically resolve according to the type of the configuration file, and then call viper The get method gets the key value of the configuration. Example: read configuration from YAML file.
- YAML file
userName: "xiaoming" address: "Guangzhou City XXX" sex: 1 company: name: "xxx" employeeId: 1000 department: - "Technology Department"
- code
package main import ( "fmt" "github.com/spf13/viper" ) type UserInfo struct { UserName string Address string Sex byte Company Company } type Company struct { Name string EmployeeId int Department []interface{} } func main() { // Building Viper instances v := viper.New() // Set profile name v.SetConfigName("userInfo") // Set profile path v.AddConfigPath("/root/go/src/webDemo/") // Set profile type v.SetConfigType("yaml") if err := v.ReadInConfig();err != nil { fmt.Printf("err:%s\n",err) } fmt.Printf("userName:%s sex:%s company.name:%s \n", v.Get("userName"), v.Get("sex"), v.Get("company.name")) // Deserialize to Struct type variable var userInfo UserInfo if err := v.Unmarshal(&userInfo) ; err != nil{ fmt.Printf("err:%s",err) } fmt.Println(userInfo) }
Note: generally, the information of the configuration file can be stored in two forms in the code:
- Directly resolve to a key/value Map type variable.
- Explicitly deserialize variables of type Struct.
Monitor the configuration file and reread the configuration data
Viper supports dynamic updating of configuration files, so that the application has the ability to read the configuration files at runtime. Therefore, the configuration can take effect without restarting the server. The WatchConfig() function of viper instance needs to be called. Viper uses fsnotify library to realize the function of monitoring file modification.
Example:
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")) }
Just call viper Watchconfig, Viper will automatically listen for configuration changes. If there are modifications, reload the configuration.
In the above example, we first print redis Port, and then Sleep 10s. During this period, redis.com is modified in the configuration Port, and print again after Sleep. It is found that the modified value is printed out:
redis port before sleep: 7381 redis port after sleep: 73810
In addition, you can also specify a callback function for the action of configuration modification to obtain the change notification:
viper.OnConfigChange(func(e fsnotify.Event) { fmt.Printf("Config file:%s Op:%s\n", e.Name, e.Op) })
This callback will be executed when the file is modified.
Read Key
After configuring various key sources, you can read the values of various key sources.
It is worth noting that the Get method provided by Viper returns a value of interface {} Type, which will be inconvenient to use. Therefore, Viper also provides GetType series methods to return the value of the specified Type. If the specified key does not exist or the Type is incorrect, the GetType method returns a zero value of the corresponding Type. The Type can be:
- Bool
- Float64
- Int
- String
- Time
- Duration
- IntSlice
- StringSlice
If you want to determine whether a key exists, you can use the IsSet method. In addition, GetStringMap and GetStringMapString methods can directly return all key value pairs under a key in Map. The former returns map[string]interface{}, and the latter returns map[string]string. The AllSettings method returns all settings with map[string]interface {}.
Examples
- config.toml file
[server] protocols = ["http", "https", "port"] ports = [10000, 10001, 10002] timeout = 3s
- code
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()) }
- result
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]]]
If redis. In the configuration file If port is commented out, redis.com is output port is not set.
Save configuration
When we want to save the configuration generated in the program or the changes made, we can use the interface provided by Viper:
- WriteConfig: writes the current Viper configuration to a predefined path. If there is no predefined path, an error is returned. Otherwise, the current configuration will be overwritten.
- SafeWriteConfig: the same function as above, but if the configuration file exists, it will not be overwritten;
- WriteConfigAs: save the configuration to the specified path. If the file exists, overwrite it;
- SafeWriteConfig: the same function as above, but if the configuration file exists, it will not be overwritten.
Example:
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) } }
The saved configuration file is as follows:
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
Reference documents
https://www.cnblogs.com/rickiyang/p/11074161.html