Spring Cloud 学习——7. Spring Cloud Config
1. 前言
本文介绍一个 通过 Spring Cloud Config + git 实现 Spring Cloud 项目的配置中心化的简单实践。
在一个分布式系统中,存在着各种微服务,而每一种服务可能都有几十甚至几百个实例在运行。虽然这些实例被分别部署在不同的机器上(或者网络节点中),但是他们需要一致对外提供服务,所以他们必须对所有的配置项都具有相同的配置值。而如果将这些配置项都保存在各个实例的本地上,那么一份配置就会存在几十上百个副本,这种情况下,一旦需要修改某一个配置值,这种运维上的难度可想而知。所以,在一个分布式系统中,单独建立一个配置中心是很有必要的。Spring Cloud 当然也考虑到了这一点,Spring Cloud Config提供了 解决方案。
Spring Cloud Config 分为 config server 和 config client,config server 即是配置中心,它负责管理所有的配置;其它需要使用配置的服务模块就是 config client。所有的 config client 都从 config server 拉去配置,这样就保证了同一种微服务的所有实例都具有相同的配置值,并且只需要维护一份配置值。
Spring Cloud Config 支持将配置保存在本地或者 vcs服务器上(如:git、svn)。一般实际项目中都建议将配置存放到 git 上,好处有:
1.1. git 本身就是一个版本控制工具,用它来管理配置,对于项目的迭代升级以及运营的支持都非常方便。升级出了问题也方便回退,还可以查看历史版本;
1.2. git 本身支持多分支,而实际项目可能也存在多个分支,不同分支的配置提供给对应分支的项目,不用为同一个项目的多个分支分别维护配置仓库;
1.3. git 的云厂商非常成熟,如:github、gitee 等,而且免费;
所以本文也是以将配置保存在 git 上为例。
2. 搭建 config server
2.1. 新建一个 spring boot 模块
2.2. 添加依赖,版本自选
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> <version>2.2.1.RELEASE</version> </dependency>
2.3. 启用 config server 服务器
启动类添加注解 @EnableConfigServer
2.4. 新建git仓库,放入配置文件,示例:
在本例中:我的配置文件仓库名为 onezai-cloud-config ;使用 master 分支;我将所有配置文件放到 config 目录下(避免与 .idea 和 readme 这些文件混合在一起);我的配置文件名为 sys-dev.properties;我的配置文件中有两个配置项 username 和 nickname。
2.5. 指定存放配置文件的 git 仓库信息,在 application 文件中添加配置
spring.cloud.config.server.git.uri=https://gitee.com/xxx/xxx spring.cloud.config.server.git.search-paths=config spring.cloud.config.server.git.username=xxx spring.cloud.config.server.git.password=xxx
其中:
2.5.1. git.uri 指定仓库地址目录,一般由 git 服务商域名+个人空间地址+仓库名组成;
2.5.2. search-paths 指定查找目录,它是相对于仓库根目录的。即只在指定仓库的这个目录下查找配置文件;
2.5.3. git.username 和 git.password 分别指定访问仓库的用户名和密码,一般配置文件都是比较机密的,所以建议放在私有仓库通过账号密码去访问;
2.6. 启动 config server模块,验证是否能从 git 上拉取配置文件
在浏览器访问 http://localhost:7300/master/sys-dev.properties
其中:
7300 是 config server 模块的服务地址;
master 是仓库分支名(因为 master 是默认分支,可以省略,所以也可以这样访问:http://localhost:7300/sys-dev.properties);
虽然两个配置项的顺序跟仓库源文件不一致,虽然
2.7. 从日志看工作过程
这是上述访问过程中 config server 的日志:
所以,在访问配置的时候,config server 会将访问的配置文件拉取到本地的临时目录存放。既然在本地放了副本,那我猜想在访问配置的时候,如果此时 config server 访问不到 git 仓库,也能返回结果,接下来我们验证一下:将仓库中 username 的值从 coding_one_v1 改为 coding_one_v2,然后把本机网络断了,再访问一下上述地址
结果:
说明我们的猜测是正确的。这样在本地放一个副本的策略,在一定程度上提高了可用性,毕竟配置这种东西不是经常改动的,而网路故障时有可能发生的,但是刚好在系统修改了配置需要更新的时候同时又发生了网络故障这种概率就非常小了。
到这里,我们还只是完成了 config server 的搭建和验证,接下来我们搭建 config server。
3. 搭建 config server
3.1. 新建一个 springboot 模块,该模块通常是业务服务模块。
在本例中,我沿用了该系列前面文章的 demo 代码,所以无需新建模块,直接使用已经存在的 sys 模块。前面文章的链接在文章结尾统一目录。
3.2. 添加依赖,版本自选
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-config --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> <version>2.2.1.RELEASE</version> </dependency>
3.3. 添加配置,指定 config server 信息
spring.cloud.config.uri=http://localhost:7300/ spring.cloud.config.profile=@ spring.cloud.config.label=master
其中:
3.3.1. config.uri 指定 config server 的服务地址。这种是直接配置的方式,还有方法是将 config server 也注册到 eureka server ,然后通过服务发现去找到 config server(这里不做示例);
3.3.2. config.profile 指定需要获取哪种环境的配置文件,一般有 dev/test/pro等,@ 是取当前实例运行的环境,环境的配置可以通过 maven 的 <profiles> 节点去完成,然后在打包时指定或者运行 java -jar 命令时使用 --spring.profiles.active=xxx 参数指定;
3.3.3. config.label 指定需要从 git 仓库的哪个分支获取配置;
3.3.4. 一个 git 仓库中可能会存放很多 config client 的配置文件,而 config server 会根据 config client 的服务名( spring.application.name )去区分配置文件。config 支持的配置文件名和请求参数对应规则有:
- / { 应用名 } / { 环境名 } [ / { 分支名 } ]
- / { 应用名 } - { 环境名 }.yml
- / { 应用名 } - { 环境名 }.properties
- / { 分支名 } / { 应用名 } - { 环境名 }.yml
- / { 分支名 } / { 应用名 } - { 环境名 }.properties
不指定分支名的情况下会查找仓库的默认分支。
另外,在我的例子中,上述配置需要在 bootstrap 和 application 文件中都写上,暂时还不明白为什么,可能跟 spring boot 的加载顺序有关系,后面再去解决吧!
3.4. 新建配置类
@Component @RefreshScope public class CommonConfig { @Value("${username}") private String username; @Value("${nickname}") private String nickname; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } }
其中, @Component 注解将配置类实例化,这样就可以在项目中使用 @Autowired 注入到其它类对象中直接使用了;通过在配置类属性添加 @Value 注解来将指定的配置项值注入到对应属性中; @RefreshScope 注解则是动态刷新配置需要用到的,动态刷新配置后面会讲。
3.5. 使用配置
@RestController @RequestMapping("/") public class HomeController { @Autowired EurekaClient eurekaClient; @Autowired RestTemplate restTemplate; @Autowired UserClient userClient; @Autowired CommonConfig commonConfig; @GetMapping("/getUser/{id}") public User getUser(@PathVariable("id")String id){ User user = userClient.getUserById(id); System.out.println("user:\n" + JSONObject.fromObject(user).toString()); System.out.println("从 config server 获取的配置项:username=" + commonConfig.getUsername()); return user; }
访问 /getUser/{id}服务,打印结果如下:
获取配置成功。
4. 至此,通过 Spring Cloud Config + git 实现分布式系统的配置中心化已经实现了。
本例只是一个最简单实践,在实际项目中,还要考虑 config server 的高可用性。
后面文章会介绍怎么实现 spring cloud config 配置的动态刷新。