微服务配置中心de小实验
本文是个人在学习书籍《Go语言高并发与微服务实战》第8章内容过程中,而动手做的小实验,涉及spring cloud config和yaml相关知识,仅供参考。
准备spring boot环境
对java熟悉的童鞋可以直接使用IDEA,安装插件即可直接使用。
本人使用Mac机器,在这里尝试使用CLI模式搭建环境(PS:因对java不专,最后发现此步骤非必须,但还是保留搭建记录吧)。
# brew tap spring-io/tap
# brew install spring-boot
试一下helloworld运行,新建并编辑文件app.groovy:
@RestController
class ThisWillActuallyRun {
@RequestMapping("/")
String home() {
"Hello World!"
}
}
运行效果:
# spring run app.groovy
Resolving dependencies..............................................................................
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.2)
2021-07-19 14:58:03.533 INFO 40088 --- [ runner-0] o.s.boot.SpringApplication : Starting application using Java 1.8.0_281 on lijingzhaodeMacBook-Pro.local with PID 40088 (started by lijingzhao in /Users/lijingzhao/LocalProject/spring/config-demo)
2021-07-19 14:58:03.548 INFO 40088 --- [ runner-0] o.s.boot.SpringApplication : No active profile set, falling back to default profiles: default
2021-07-19 14:58:05.353 INFO 40088 --- [ runner-0] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2021-07-19 14:58:05.375 INFO 40088 --- [ runner-0] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2021-07-19 14:58:05.375 INFO 40088 --- [ runner-0] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.48]
2021-07-19 14:58:05.435 INFO 40088 --- [ runner-0] org.apache.catalina.loader.WebappLoader : Unknown class loader [org.springframework.boot.cli.compiler.ExtendedGroovyClassLoader$DefaultScopeParentClassLoader@63cb21ef] of class [class org.springframework.boot.cli.compiler.ExtendedGroovyClassLoader$DefaultScopeParentClassLoader]
2021-07-19 14:58:05.478 INFO 40088 --- [ runner-0] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-07-19 14:58:05.478 INFO 40088 --- [ runner-0] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1599 ms
2021-07-19 14:58:06.727 INFO 40088 --- [ runner-0] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2021-07-19 14:58:06.748 INFO 40088 --- [ runner-0] o.s.boot.SpringApplication : Started application in 4.243 seconds (JVM running for 132.146)
2021-07-19 14:59:21.243 INFO 40088 --- [nio-8080-exec-3] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2021-07-19 14:59:21.244 INFO 40088 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2021-07-19 14:59:21.249 INFO 40088 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Completed initialization in 4 ms
打开 http://localhost:8080/ 即可看到效果
部署配置中心server端
使用IDEA Initializr可生成依赖Config Server的项目。
也可以使用网页工具 https://start.spring.io/ 帮你生成项目代码,本文使用项目名为config-server。
生成代码后,修改 src/main/java/com/example/configserver/ConfigServerApplication.java
文件,增加 @EnableConfigServer ,整体代码如下:
package com.example.configserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
新增配置文件 src/main/resources/application.yml
内容如下:
server:
port: 8888
spring:
application:
name: config-server
profiles:
active: git #设置使用本地配置(默认是git,可以设置:subversion(SVN),native(本地))
cloud:
config:
server:
#如下是GIT配置
git:
uri: https://github.com/longjoy/config-repo.git
search-paths: ${APP_ENV:dev}
username:
password:
default-label: master
使用marven工具编译打包成jar,java指令运行:
# ./mvnw clean package
# java -jar target/config-server-0.0.1-SNAPSHOT.jar
运行成功后,可使用curl指令
curl http://localhost:8888/master/client-demo-dev.yaml
查看结果
编写client端代码
resume.go
package main
import (
"fmt"
"github.com/longjoy/micro-go-book/ch8-config/conf"
"github.com/spf13/viper"
"log"
"net/http"
)
func main() {
http.HandleFunc("/resumes", func(w http.ResponseWriter, req *http.Request) {
//q := events.goreq.URL.Query().Get("q")
_, _ = fmt.Fprintf(w, "个人信息:\n")
_, _ = fmt.Fprintf(w, "姓名:%s,\n性别:%s,\n年龄 %d!", viper.GetString("resume.name"), conf.Resume.Sex, conf.Resume.Age) //这个写入到w的是输出到客户端的
})
log.Fatal(http.ListenAndServe(":8081", nil))
}
conf/config.go
package conf
import (
"fmt"
"log"
"net/http"
"github.com/spf13/viper"
)
const (
kAppName = "APP_NAME"
kConfigServer = "CONFIG_SERVER"
kConfigLabel = "CONFIG_LABEL"
kConfigProfile = "CONFIG_PROFILE"
kConfigType = "CONFIG_TYPE"
)
var (
Resume ResumeConfig
)
type ResumeConfig struct {
Name string
Age int
Sex string
}
func init() {
viper.AutomaticEnv()
initDefault()
if err := loadRemoteConfig(); err != nil {
log.Fatal("Fail to load config", err)
}
if err := sub("resume", &Resume); err != nil {
log.Fatal("Fail to parse config", err)
}
}
func initDefault() {
viper.SetDefault(kAppName, "client-demo")
viper.SetDefault(kConfigServer, "http://localhost:8888")
viper.SetDefault(kConfigLabel, "master")
viper.SetDefault(kConfigProfile, "dev")
viper.SetDefault(kConfigType, "yaml")
}
func loadRemoteConfig() (err error) {
confAddr := fmt.Sprintf("%v/%v/%v-%v.%v",
viper.Get(kConfigServer), viper.Get(kConfigLabel),
viper.Get(kAppName), viper.Get(kConfigProfile),
viper.Get(kConfigType))
resp, err := http.Get(confAddr)
if err != nil {
return
}
defer resp.Body.Close()
viper.SetConfigType(viper.GetString(kConfigType))
if err = viper.ReadConfig(resp.Body); err != nil {
return
}
log.Println("Load config from: ", confAddr)
return
}
func sub(key string, value interface{}) error {
log.Printf("配置文件的前缀为:%v", key)
sub := viper.Sub(key)
sub.AutomaticEnv()
sub.SetEnvPrefix(key)
return sub.Unmarshal(value)
}
编译运行
# go run resume.go
2021/07/19 18:10:38 Load config from: http://localhost:8888/master/client-demo-dev.yaml
2021/07/19 18:10:38 配置文件的前缀为:resume
浏览器访问 http://127.0.0.1:8081/resumes ,或使用curl指令
curl http://127.0.0.1:8081/resumes
可以查看client程序获取到的配置内容。
总结
- spring cloud config是一个比较容易上手的配置中心,通过它我们可以快速了解配置中心的功能、原理及其使用。
- yaml是其中一种配置文件格式,而viper是一个比较方便的配置解析工具,支持的格式也比较多,可以灵活使用。
- 关于热更新问题,书中提到的方案是amqp,其实用的是消息队列通知,本文则不做演示;因为配置改动频率不高,且引入mq感觉不太划算,而且还有别的方式可以实现。
关于我
name: yison.li
blog: http://yyeer.com
github: https://github.com/yisonli