Introduction
Recently with Go write back end write very happy, write a more useful blog summary under How to manage the Go program configuration through Spring Cloud Config Server. The implementation is not complex, so it can be easily extended to programs in other languages.
First of all, why do you want to do centralized configuration management? In the era of single application configuration management is not a big problem, the general configuration file and the source code in the repository together, to view or modify directly to the conf
directory to find the finished. But to the micro-service era, the number of services more than in the past dozens of times times, and then to the vast number of code warehouse to find configuration can not be so simple . So we need a unified view of the configuration, the configuration can be version control of the place, this is the configuration center.
Searching for "Configuration Center" on Google can find a lot of good open source software, but most of them are heavier and need to be introduced to specific clients. This is too much for small and medium-sized teams who are not so large. So instead of a lightweight configuration center like Spring Cloud Config Server, it's a good fit to run in minutes, and it's rich enough to be relevant to the configuration itself.
So our architecture is like this:
- Git: Store specific configuration files and be responsible for configuring version management
- Spring Cloud Config Server: Provides a configured query interface
- Go App: Load configuration from configuration center and use
OK, let's do it officially.
A simple search service
As a demonstration we use Go to write a very simple search service. As long as GET /search?q=<keyword>
the access to the service will be found in search engine results. With Go implementation as long as a file Oh ~
Main.go
package mainimport ...func main() { http.HandleFunc("/search", func(w http.ResponseWriter, req *http.Request) { q := req.URL.Query().Get("q") fmt.Fprintf(w, `<iframe width="100%%" height="98%%" scrolling="auto" frameborder="0" src="https://cn.bing.com/search?q=%v">`, q) }) log.Fatal(http.ListenAndServe(":8081", nil))}
Then run the service up:
go run main.go
accessing in the browserhttp://localhost:8081/search?q=golang
It's simple, isn't it? But the problem here is that we put the https://cn.bing.com
write dead in the code, if you want to switch the search engine will have to recompile the program, it is really time-consuming and laborious. This is where we need to decouple the configuration from the configuration file.
Configuration file
Let's start with a local configuration file.go-app.yml
app: search_url: https://cn.bing.com/search?q=%v
This configuration is then loaded via Viper, the more popular configuration library
Conf/conf.go
package confimport ...func init() { viper.SetConfigName("go-app") viper.AddConfigPath(os.ExpandEnv(`$GOPATH\src\github.com\GotaX\config-server-demo`)) viper.SetConfigType("yaml") if err := viper.ReadInConfig(); err != nil { log.Fatal("Fail to load config", err) }}
Now we're going to decouple the search engine's address into the config file.
Main.go
package mainimport ...func main() { http.HandleFunc("/search", func(w http.ResponseWriter, req *http.Request) { q := req.URL.Query().Get("q") src := fmt.Sprintf(viper.GetString("app.search_url"), q) fmt.Fprintf(w, `<iframe width="100%%" height="98%%" scrolling="auto" frameborder="0" src="%v">`, src) }) log.Fatal(http.ListenAndServe(":8081", nil))}
Transfer configuration to the cloud
Next we move the config file from local to Git, and it's convenient for me to put it directly in the Config branch of the current repository.
Address: Https://github.com/GotaX/config-server-demo/tree/config
Start the Configuration Center
After the configuration file has been uploaded, we will open a new config-server empty branch to build the configuration center.
First, create a new Java + Gradle Spring Boot project to the https://start.spring.io/page, and the dependency selection Config Server
Click "Generate Project" to download the compressed package and unzip it.
Modify Application.java
package com.example.demo;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.config.server.EnableConfigServer;@EnableConfigServer // 添加这行@SpringBootApplicationpublic class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }}
Modify APPLICATION.YML, fill in the warehouse address where we store the configuration file
spring.cloud.config.server.git.uri: https://github.com/GotaX/config-server-demo.git
Starting config server in the project root directory
gradle bootrun
Access http://localhost:8080/config/go-app-default.yml
View Configuration
app: search_url: https://cn.bing.com/search?q=%v
So our configuration center is up and ready.
Read the configuration in the Go app
Finally, the configuration in Spring Cloud Config Server is used in the application. If you are a Spring Boot-based app, you can use the spring-cloud-config-client
load configuration directly. In go you need to write a little bit of code, but not much.
Let's first add a function to the Config.go loadRemote()
to read the configuration from the configuration center
Conf/conf.go
// ...const ( kAppName = "APP_NAME" kConfigServer = "CONFIG_SERVER" kConfigLabel = "CONFIG_LABEL" kConfigProfile = "CONFIG_PROFILE" kConfigType = "CONFIG_TYPE")func loadRemoteConfig() (err error) { // 组装配置文件地址: http://localhost:8080/config/go-app-default.yaml confAddr := fmt.Sprintf("%v/%v/%v-%v.yml", viper.Get(kConfigServer), viper.Get(kConfigLabel), viper.Get(kAppName), viper.Get(kConfigProfile)) resp, err := http.Get(confAddr) if err != nil { return } defer resp.Body.Close() // 设置配置文件格式: yaml viper.SetConfigType(viper.GetString(kConfigType)) // 载入配置文件 if err = viper.ReadConfig(resp.Body); err != nil { return } log.Println("Load config from: ", confAddr) return}
Of course, we need to know the portal of the configuration center, so we also need a initDefault()
function to initialize these configurations
Conf/conf.go
func initDefault() { viper.SetDefault(kAppName, "go-app") viper.SetDefault(kConfigServer, "http://localhost:8080") viper.SetDefault(kConfigLabel, "config") viper.SetDefault(kConfigProfile, "default") viper.SetDefault(kConfigType, "yaml")}
And now our init()
function becomes this.
Conf/conf.go
func init() { viper.AutomaticEnv() initDefault() if err := loadRemoteConfig(); err != nil { log.Fatal("Fail to load config", err) }}
This allows viper.AutomaticEnv()
us to modify any configuration with environment variables, so initDefault()
the configuration is not written dead in the code. The more common use is CONFIG_PROFILE=prod
to switch profile by environment variable
Finally we want Viper to appear only in the Conf package, while hiding the concrete implementation of our load configuration. So we read the configuration into the struct and provide it externally.
Conf/conf.go
var App AppConfigtype AppConfig struct { SearchUrl string `mapstructure:"search_url"`}func init() { // ... if err := sub("app", &App); err != nil { log.Fatal("Fail to parse config", err) }}func sub(key string, value interface{}) error { sub := viper.Sub(key) sub.AutomaticEnv() sub.SetEnvPrefix(key) return sub.Unmarshal(value)}
Then we can remove the call from the Main.go viper.Get()
.
Main.go
import ...func main() { http.HandleFunc("/search", func(w http.ResponseWriter, req *http.Request) { q := req.URL.Query().Get("q") src := fmt.Sprintf(conf.App.SearchUrl, q) fmt.Fprintf(w, `<iframe width="100%%" height="98%%" scrolling="auto" frameborder="0" src="%v">`, src) }) log.Fatal(http.ListenAndServe(":8081", nil))}
Summarize
We implemented configuration center-based configuration management and usage with Git + Spring Could Config Server + Viper + small Go code
We can even use Spring Boot-like profile management in Go, in contrast:
- Http://localhost:8080/config/go-app-default.yml
- Http://localhost:8080/config/go-app-prod.yml
The complete code can refer to the 3 branches under Https://github.com/GotaX/config-server-demo:
- Config: Configuration file
- Config-server: Configuration Center
- App:go applications
Of course, the use of this method is relatively simple, there are many improvements can be, such as:
- Real-time push with Spring Cloud Bus for configuration
- High availability with Spring Cloud Eureka for configuration servers
- Monitor SIGINT and SIGTERM for Go app graceful exit
Write next time if you have a chance, or you can refer to the official documentation of Spring Cloud Config Server directly
If you have questions or better ideas, practice, welcome message Discussion