introduce
Through a complete example, in gogf/gf In the framework, the configuration files are distinguished according to the environment. That is, how to read different configuration files in [test], [Online] and other environments.
We will use rk-boot To start gogf/gf Microservices.
Please visit the following address for a complete tutorial:
install
go get github.com/rookie-ninja/rk-boot/gf
Quick start
We will create config / Beijing yaml, config/shanghai. yaml, config/default. Yaml three configuration files, and then read different files according to different environment variables.
Rk boot uses REALM, REGION, AZ and DOMAIN environment variables to distinguish different environments. This is also the cloud native environment resolution method we recommend.
For example, REALM = "your business", REGION = "Beijing", AZ = "Beijing first district", DOMAIN = "test environment".
Rk boot integrates viper To process the configuration file.
1. Create a profile
- config/beijing.yaml
--- my-region: beijing
- config/shanghai.yaml
--- my-region: shanghai
- config/default.yaml
--- my-region: default
2. Create boot yaml
boot.yaml file tells rk boot how to start gogf/gf Service.
We use config as boot The entry of configuration file in yaml, which can provide multiple config file paths.
Locale represents the environment of Config. We use locale to distinguish different configs.
Why config Name uses the same name?
We want to use the same set of code, but read different files, and we want the file names to be different. Therefore, different files are distinguished by locale. We will introduce the logic of locale in detail later.
config: # default - name: my-config locale: "*::*::*::*" path: config/default.yaml # If the environment variable REGION=beijing, read this file - name: my-config locale: "*::beijing::*::*" path: config/beijing.yaml # If the environment variable REGION=shanghai, read this file - name: my-config locale: "*::shanghai::*::*" path: config/shanghai.yaml gf: - name: greeter port: 8080 enabled: true
3. Create main go
Set the environment variable: REGION="beijing", and then read the configuration file, config / Beijing Yaml will be read.
// Copyright (c) 2021 rookie-ninja // // Use of this source code is governed by an Apache-style // license that can be found in the LICENSE file. package main import ( "context" "fmt" "github.com/rookie-ninja/rk-boot" _ "github.com/rookie-ninja/rk-boot/gf" "os" ) // Application entrance. func main() { // Set REGION=beijing os.Setenv("REGION", "beijing") // Create a new boot instance. boot := rkboot.NewBoot() // Load config which is config/beijing.yaml fmt.Println(boot.GetConfigEntry("my-config").GetViper().GetString("my-region")) // Bootstrap boot.Bootstrap(context.Background()) // Wait for shutdown sig boot.WaitForShutdownSig(context.Background()) }
4. Folder structure
$ tree . ├── boot.yaml ├── config │ ├── beijing.yaml │ ├── default.yaml │ └── shanghai.yaml ├── go.mod ├── go.sum └── main.go
5. Verification
$ go run main.go
We will get the following output:
beijing
6. No matching environment variables found
If Region = "not matched", i.e. no matching environment variable is found, the default configuration file (config/default.yaml) is read. Because config / default The locale attribute of yaml is *:: *:: *::*
// Application entrance. func main() { // Set REGION=not-matched os.Setenv("REGION", "not-matched") ... // Load config which is config/default.yaml fmt.Println(boot.GetConfigEntry("my-config").GetViper().GetString("my-region")) ... }
We will get the following output:
$ go run main.go default
7. The environment variable is not configured
If we do not configure the REGION environment variable, we will read config / default Yaml file.
// Application entrance. func main() { ... // Load config which is config/beijing.yaml fmt.Println(boot.GetConfigEntry("my-config").GetViper().GetString("my-region")) ... }
We will get the following output:
$ go run main.go default
concept
Rk boot uses four environment variables: real, REGION, AZ and DOMAIN to distinguish configuration files.
These four environment variables can be arbitrary values.
Best practices
For example, we have a cloud album business. If the IP address of MySQL used by this service in different environments is different, it can be configured in this way.
framework
It is assumed that our business has servers in [Beijing] and [Shanghai]. At the same time, in order to improve service availability, we have opened two districts in [Beijing] and [Shanghai].
At this time, we can configure the following environment variables on the machine, which can be set in batch through Ansible and other tools.
environment | Corresponding environment variable |
---|---|
Beijing, zone 1, test | REALM="cloud-album",REGION="bj",AZ="bj-1",DOMAIN="test" |
Beijing, District 1, online | REALM="cloud-album",REGION="bj",AZ="bj-1",DOMAIN="prod" |
Beijing, zone 2, test | REALM="cloud-album",REGION="bj",AZ="bj-2",DOMAIN="test" |
Beijing, District 2, online | REALM="cloud-album",REGION="bj",AZ="bj-2",DOMAIN="prod" |
Shanghai, zone 1, test | REALM="cloud-album",REGION="sh",AZ="sh-1",DOMAIN="test" |
Shanghai, District 1, online | REALM="cloud-album",REGION="sh",AZ="sh-1",DOMAIN="prod" |
Shanghai, zone 2, test | REALM="cloud-album",REGION="sh",AZ="sh-2",DOMAIN="test" |
District 2, Shanghai Online | REALM="cloud-album",REGION="sh",AZ="sh-2",DOMAIN="prod" |
At the same time, if we do not use services such as ETCD and consult to pull configuration files remotely, we can directly add the following files to the machine. Each file has a different MySQL IP address.
Folder structure
. ├── boot.yaml ├── config │ ├── bj-1-test.yaml │ ├── bj-1-prod.yaml │ ├── bj-2-test.yaml │ ├── bj-2-prod.yaml │ ├── sh-1-test.yaml │ ├── sh-1-prod.yaml │ ├── sh-2-test.yaml │ ├── sh-2-prod.yaml │ └── default.yaml ├── go.mod ├── go.sum └── main.go
boot.yaml
Next, we are in boot Add the following config entry to yaml.
config: # Default entry - name: my-config locale: "*::*::*::*" path: config/default.yaml # Beijing, zone 1, test environment - name: my-config locale: "cloud-album::bj::bj-1::test" path: config/bj-1-test.yaml # Beijing, first district, online environment - name: my-config locale: "cloud-album::bj::bj-1::prod" path: config/bj-1-prod.yaml # Beijing, zone 2, test environment - name: my-config locale: "cloud-album::bj::bj-2::test" path: config/bj-2-test.yaml # Beijing, zone 2, online environment - name: my-config locale: "cloud-album::bj::bj-2::prod" path: config/bj-2-prod.yaml # Shanghai, zone 1, test environment - name: my-config locale: "cloud-album::sh::sh-1::test" path: config/sh-1-test.yaml # Shanghai, first district, online environment - name: my-config locale: "cloud-album::sh::sh-1::prod" path: config/sh-1-prod.yaml # Shanghai, zone 2, test environment - name: my-config locale: "cloud-album::sh::sh-2::test" path: config/sh-2-test.yaml # Shanghai, zone 2, online environment - name: my-config locale: "cloud-album::sh::sh-2::prod" path: config/sh-2-prod.yaml gf: - name: greeter port: 8080 enabled: true
main.go to read the configuration file.
Because all configs are named my Config in main When reading in go, we can use my Config to get configeentry.
package main import ( "context" "fmt" "github.com/rookie-ninja/rk-boot" "os" ) // Application entrance. func main() { // Create a new boot instance. boot := rkboot.NewBoot() // Get viper instance based on environment variable boot.GetConfigEntry("my-config").GetViper() // Bootstrap boot.Bootstrap(context.Background()) // Wait for shutdown sig boot.WaitForShutdownSig(context.Background()) }
Override with environment variables
Rk boot integrates viper To process the configuration file, so it is naturally integrated viper All the functions that come with it.
This includes overwriting the existing configuration [value] through [environment variable]. Let's take an example.
1.config/default.yaml
In config / default Yaml file, add a K/V.
--- endpoint: 8.8.8.8
2.main.go
Under normal circumstances, the following code will get [8.8.8.8], but we override the value of [endpoint] through the environment variable. Note that the Key of the environment variable needs to be in uppercase English when overridden by the environment variable.
// Copyright (c) 2021 rookie-ninja // // Use of this source code is governed by an Apache-style // license that can be found in the LICENSE file. package main import ( "context" "fmt" "github.com/rookie-ninja/rk-boot" _ "github.com/rookie-ninja/rk-boot/gf" "os" ) // Application entrance. func main() { // Set ENDPOINT=localhost os.Setenv("ENDPOINT", "localhost") // Create a new boot instance. boot := rkboot.NewBoot() // Load config which is config/default.yaml fmt.Println(boot.GetConfigEntry("my-config").GetViper().GetString("endpoint")) // Bootstrap boot.Bootstrap(context.Background()) // Wait for shutdown sig boot.WaitForShutdownSig(context.Background()) }
3. Verification
$ go run main.go localhost
4. Use environment variable prefix
In the actual environment, there may be the problem of environment variable conflict. At this time, we can configure an [environment variable prefix] in Viper to mark our Config.
For example, suppose that the system has initialized HOSTNAME into each machine as an environment variable. If we forcibly modify this value, we will encounter unpredictable errors. At this time, we can add a prefix.
example:
- config/default.yaml
--- hostname: my-hostname
- boot.yaml In the config option, add our ENV prefix.
config: - name: my-config locale: "*::*::*::*" path: config/default.yaml envPrefix: rk gf: - name: greeter port: 8080 enabled: true
- main.go At this time, when overriding the HOSTNAME through the environment variable, we add RK_ As a prefix.
Refer to viper official documents
// Copyright (c) 2021 rookie-ninja // // Use of this source code is governed by an Apache-style // license that can be found in the LICENSE file. package main import ( "context" "fmt" "github.com/rookie-ninja/rk-boot" _ "github.com/rookie-ninja/rk-boot/gf" "os" ) // Application entrance. func main() { // Set RK_HOSTNAME=override-hostname os.Setenv("RK_HOSTNAME", "override-hostname") // Create a new boot instance. boot := rkboot.NewBoot() // Load config which is config/default.yaml fmt.Println(boot.GetConfigEntry("my-config").GetViper().GetString("hostname")) // Bootstrap boot.Bootstrap(context.Background()) // Wait for shutdown sig boot.WaitForShutdownSig(context.Background()) }
- verification
$ go run main.go override-hostname