introduce
As a complete example, in the GoGF/GF framework, configuration files are differentiated by environment. That is, how to read different configuration files in test, online, etc.
We will use RK-boot to start the GoGF/GF microservice.
Please visit the following address for the full tutorial:
- rkdocs.netlify.app/cn
The installation
go get github.com/rookie-ninja/rk-boot/gf
Copy the code
Quick start
Yaml config/ Shanghai. Yaml config/default.yaml config/default.yaml config/ Beijing. yaml config/ Shanghai.
Rk-boot uses REALM, REGION, AZ, and DOMAIN environment variables to distinguish between different environments. This is also our recommended cloud native environment resolution method. For example, REALM=” your business “, REGION=” Beijing “, AZ=” Beijing 1 “, DOMAIN=” test environment “.
Rk-boot integrates viper to process configuration files.
1. Create a configuration file
- config/beijing.yaml
---
my-region: beijing
Copy the code
- config/shanghai.yaml
---
my-region: shanghai
Copy the code
- config/default.yaml
---
my-region: default
Copy the code
2. Create the boot. Yaml
The boot.yaml file tells RK-boot how to start the Gogf/GF service.
We use config as the entry point for configuration files in boot.yaml, and multiple config file paths can be provided.
Locale stands for the environment of Config, and we use locale to distinguish between different Configs.
Why is config.name used with the same name?
We want to use the same set of code, but read different files, and we want the names of the files to be different. So you use locale to differentiate between files. We’ll talk more about the logic of locales later.
config:
# the 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
Copy the code
3. Create a main. Go
Set the environment variable: REGION=” Beijing “, 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(a) {
// 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())
}
Copy the code
4. Folder structure
$tree. ├ ─ ─ the boot. Yaml ├ ─ ─ the config │ ├ ─ ─ Beijing. The yaml │ ├ ─ ─ the default. The yaml │ └ ─ ─ Shanghai. The yaml ├ ─ ─. Mod ├ ─ ─. Sum └ ─ ─ main.goCopy the code
5. Verify
$ go run main.go
Copy the code
We get the following output:
beijing
Copy the code
6. No matching environment variable is found
If REGION=”not-matched”, that is, no matching environment variable is found, the default configuration file (config/default.yaml) will be read. The locale attribute of config/default.yaml is *::*::*: *
// Application entrance.
func main(a) {
// 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"))... }Copy the code
We get the following output:
$ go run main.go
default
Copy the code
7. Environment variables are not configured
If we do not configure the REGION environment variable, the config/default.yaml file is read.
// Application entrance.
func main(a){...// Load config which is config/beijing.yaml
fmt.Println(boot.GetConfigEntry("my-config").GetViper().GetString("my-region"))... }Copy the code
We get the following output:
$ go run main.go
default
Copy the code
concept
Rk-boot uses four environment variables to distinguish configuration files: REALM, REGION, AZ, and DOMAIN.
These four environment variables can be arbitrary values.
Best practices
For example, we have a cloud photo album business. If the IP address of MySQL used by this service is different in different environments, you can perform this configuration.
architecture
Suppose that our business has servers in [Beijing] and [Shanghai], and in order to improve service availability, we have opened two districts in [Beijing] and [Shanghai] respectively.
In this case, you can configure the following environment variables on the machine, which can be set in batches using tools like Ansible.
The environment | Corresponding environment variables |
---|---|
Beijing, District 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, Sector two, 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, District 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, Sector two, test | REALM=”cloud-album”, REGION=”sh”, AZ=”sh-2″, DOMAIN=”test” |
Shanghai, Area 2, online | REALM=”cloud-album”, REGION=”sh”, AZ=”sh-2″, DOMAIN=”prod” |
In the meantime, if we do not use services like ETCD, Consul to remotely pull the configuration file, we can directly add the following files to the machine. Each file has a different MySQL IP address.
Folder structure
. ├ ─ ─ the boot. Yaml ├ ─ ─ the 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 │ └ ─ ─ the default. The yaml ├ ─ ─. Mod ├ ─ ─. Sum └ ─ ─ main. GoCopy the code
boot.yaml
Next, we add the following config entry to boot.yaml.
*::*::*::* ::*" path: config/default.yaml # Beijing, 一区, test environment-name: my-config locale: "*::*::*::*" "Cloud-album ::bj::bj-1::test" path: config/bj-1-test.yaml # Beijing, 一区, on-line environment-name: my-config locale: "cloud-album::bj::bj-1::test" path: config/bj-1-test.yaml # Beijing, 一区, on-line environment-name: my-config locale: "Cloud-album ::bj:: BJ-1 ::prod" path: config/ BJ-1-prod. yaml # Beijing, 2 区, test environment-name: my-config locale: "Cloud-album ::bj:: BJ-2 ::test" path: config/ BJ-2-test. yaml # 北 方, 二区, 中 国 地 理 - name: my-config locale: "Cloud-album ::bj::bj-2::prod" path: config/bj-2-prod.yaml # Shanghai, 一区, test environment-name: my-config locale: "Cloud-album ::sh::sh-1::test" path: config/sh-1-test.yaml # Shanghai, 一区, online environment-name: my-config locale: cloud-album::sh::sh-1::test" path: config/sh-1-test.yaml # Shanghai, 一区, online environment-name: my-config locale: "Cloud-album ::sh::sh-1::prod" path: config/sh-1-prod.yaml # Shanghai, 2 区, test environment-name: my-config locale: "Cloud-album ::sh::sh-2::test" path: config/sh-2-test.yaml # Shanghai, 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: trueCopy the code
Go to read the configuration file.
Because all Config is named my-config, we can use my-config to get ConfigEntry when reading from main.go.
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()) }Copy the code
Override using environment variables
Rk-boot integrates with Viper to process configuration files, so it naturally integrates all of viper’s own functionality.
This includes overwriting existing configuration values with environment variables. Let’s look at an example.
1.config/default.yaml
In config/default.yaml, add a K/V.
---
endpoint: 8.88.8.
Copy the code
2.main.go
Under normal circumstances, the following code would get [8.8.8.8], but we overwrite the value of [endpoint] through the environment variable. Note that environment variables are overridden, and the Key of the environment variable needs to be in uppercase English.
// 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(a) {
// 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())
}
Copy the code
3. Verify
$ go run main.go
localhost
Copy the code
4. Use environment variable prefixes
In the real environment, there may be the problem of environment variable conflict. At this point, we can configure an environment variable prefix in Viper to mark our Config.
As an example, suppose the system has initialized HOSTNAME as an environment variable on every machine. If we force this value to change, we will encounter unpredictable errors. At this point, we can add a prefix.
Example:
- config/default.yaml
---
hostname: my-hostname
Copy the code
- 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
Copy the code
- main.go
In this case, we prefix [RK_] when overriding HOSTNAME with the environment variable.
Refer to viper’s official documentation
// 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(a) {
// 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())
}
Copy the code
- validation
$ go run main.go
override-hostname
Copy the code