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