目录
在前面有提到,微服务系统是一种典型的分布式系统,我们会将每个功能都尽可能地拆分一个可独立部署、运行的服务,服务部署完成后,每一次请求的完成,都可能涉及到多个服务的协调作业,面对越来越多的微服务,我们需要有一个东西可以方便地管理配置文件、最好可以在一个地方管理所有微服务的配置,这个就是我们接下来要说的分布式配置组件了。在接下来的内容中,简称这个分布式配置组件为配置中心。
4.1 初识分布式配置
在开始介绍Spring Cloud的配置中心实现之前,我们可以先回忆下以前我们的系统配置是怎么做的。
最开始学习的时候,很多东西都是没有配置的,基本上都是硬编码,简单、快速,系统很快构建起来了,可是随着系统的功能越来越复杂,代码量越来越大,很多地方进行硬编码已经不能满足我们的需求,我们需要在不同的环境、不同的场景下通过修改配置项来达到不一样的效果,配置项放在代码里面难找,而且修改起来会很麻烦。
于是,我们将可能频繁变动的项目提出来放在了xml、properties文件中,方便修改,这个时候修改配置的时候已经不需要再去碰业务代码,只要找到对应的配置文件,修改、打包、重新部署就可以了。这样子看起来已经不错了,但是随着分布式架构逐渐流行,一个一个的集群被搭建起来,这个时候再给每个集群实例搭配一个配置文件,维护的工作量就变得很大了。
这种情况运维人员一般都有一个config list,记录了每个实例的配置文件放在哪里、叫什么名字、现在的配置项是什么等等。这个清单如果维护出错,对整个系统来说都会是一个灾难。基于这种情况,分布式系统需要一个中心化的配置中心来替代这个config list,并且可以方便地从不同维度来进行配置,最好支持配置项的随时,并且最好能够实时生效。
4.2 Spring Cloud Config
Spring Cloud作为一个完善的微服务框架,提供了Spring Cloud Config作为推荐的分布式配置组件。
Spring Cloud Config提供了服务端和客户端,可以很好地支持分布式系统的配置需求。使用服务端可以快速构建一个独立于业务服务之外的配置中心,配置存储默认使用git,因此它拥有git所拥有的一切优点。同时,它还原生支持使用Spring的Environment和PropertySource来获取配置项。接下来,我们将使用Spring Cloud Config构建一个配置中心。
4.2.1 Config Server
新建Spring Boot工程,引入Spring Cloud Config的服务端maven依赖,为了方便测试,我们同时引入了web和actuator的相关依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
pom文件编写完成后,在application.properties中添加配置信息如下:
spring.application.name=config-server
server.port=8888
# 配置git仓库地址
# 这个地址是本书代码所在git仓库地址,这里用到了仓库中的book-config-repo目录
spring.cloud.config.server.git.uri=https://github.com/hanbin/book
# 配置仓库路径
spring.cloud.config.server.git.search-paths=book-config-repo
# 去掉git权限校验
spring.cloud.config.server.git.skip-ssl-validation=true
# 制定远程仓库中配置文件的customer name
spring.config.name=customer
最后,在启动类中添加@EnableConfigServer注解开启Spring Cloud Config的服务端功能,然后启动即可。
我们在book-config-repo目录下新建了配置文件:customer-dev.properties文件。我们可以通过customer-server服务提供的接口来访问这个配置文件。
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
举例来说,customer-dev.properties中的就是{application}-{profile}.properties的形式,customer即是应用名,profile通常用来标示不同的环境,label在使用git存储时代表的是git的分支,不写则默认为master。
上面这些URI都可以获取到远程配置,可以看到使用上面格式的地址访问可以看到如下内容:
http://localhost:8888/customer/dev
http://localhost:8888/customer/dev/master
通过上面两个链接可以得到下面的结果:
{
"name": "customer",
"profiles": [
"dev"
],
"label": "master",
"version": "97ffd82376b9ba87c5a1ffcf60b67eb4866f2415",
"state": null,
"propertySources": [
{
"name": "https://github.com/hanbin/book/book-config-repo/customer-dev.properties",
"source": {
"name": "icer123",
"age": "1001"
}
}
]
}
通过其他格式接口可以直接获取到的结果都是:
age: 1001
name: icer123
age和name就是我们需要的配置项。
构建完成配置中心后,接下来我们看看如何将微服务构建成为“配置中心客户端”,来获取配置中心的配置项。
4.2.2 Config Client
前面我们创建了customer服务,这节我们会将customer服务作为config-server的client,通过远程获取的方式拿到customer-dev.properties中的配置项。
在开始之前,这里要提到一个新的endpoint:env。env端点主要用来显示服务的配置信息。在Spring
Boot2中,env端点默认是关闭的。通过添加下面的代码到application.properties中,可以让env端点能够被访问。
management.endpoints.web.exposure.include=health,info,env
在浏览器中可以看到如图4.1所示的页面。

为customer工程添加Spring Cloud Config的支持,需要在pom.xml文件中添加下面的代码。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
启动项目,可以在启动日志中看到如图4.2所示的内容。

可以看到,添加了Spring Cloud Config的依赖后,项目在启动过程中会默认向http://localhost:8888请求远程配置,附带的参数name为项目的spring.application.name,profiles为default,label为null。
这里的默认配置不能拿到任何信息,因为profiles传的不对,在customer工程的application.properties配置文件中添加。
spring.cloud.config.uri=http://localhost:8888
spring.cloud.config.name=customer
spring.cloud.config.profile=dev
重新启动程序,可以看到如图4.3所示的启动日志。

通过图4.4可以看到现在customer在启动时读取到了之前我们在config server中的配置文件customer-dev.properties。通过本节最开始打开的env端点可以查看,如图4.4所示。

这里有两个配置文件需要注意下:spring.application.name 和 spring.cloud.config.name。
spring.application.name一般配置的是微服务的实例名,这个名字是微服务之间调用时候的唯一名称。spring.cloud.config.name配置的是Spring Cloud Config中涉及的application名称,在本节的例子中,对应的就是customer。如果去掉spring.cloud.config.name这个配置项,Spring Cloud Config会使用spring.application.name的配置去配置中心取值。
注意:充分使用label,profile可以完美达到一套代码部署在多个环境,仅通过在启动参数中修改label和profile就将各个环境进行区分的目的。
4.2.3 配置刷新
前面讲的都是在系统启动之前添加配置,那么在系统运行过程中,能否“热刷新”,让配置项在运行期可以被直接改动。
Spring Cloud Config是支持“热刷新”的,这里需要用到refresh端点。
还是以customer工程为例。为了方便测试,我们将application.properties中的:
management.endpoints.web.exposure.include=health,info,env
换成:
management.endpoints.web.exposure.include=*
这样,customer的所有端口就已经全部被放开了。
我们新建UserController.java文件,文件内容如下:
package cn.com.hanbinit.customer.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RefreshScope
@RestController
public class UserController {
@Value("${name}")
private String name;
@Value("${age}")
private Long age;
@GetMapping("/showinfo")
public String showNameAndAge(){
return "name: " + name + ", age: " + age;
}
}
上面代码中@RefreshScope注解是实现变量热加载的必需配置。@Value可以获取到配置文件以及环境变量中的配置。上面代码中的name和age就分别对应了配置文件中的name和age。showinfo接口返回了@Value获取到的属性值。
启动项目,访问http://localhost:8001/showinfo接口,可以看到如图4.5的返回。

接下来,我们将customer-dev.properties
中的icer改为icer123,100改为1001,push到远程仓库。通过postman工具调用refresh端点,如图4.6所示。

这个时候,刷新showinfo的调用页面,可以看到如图4.7的结果。

这里,就说明我们的热加载已经成功。
注意:这种刷新方式仅适用于非初始化对象的情况下,如果配置项需要在系统启动的时候加载并且使用。那么在这里使用refresh端点是不会生效的。配置项即使刷新过去,但是确没有能真正生效。
截止到这里,我们已经可以通过Spring Cloud Config搭建远程的配置中心,并且可以让微服务获取到配置中心的配置项,且可以通过refresh端点达到热加载的目的。那么能不能实现自动刷新呢?手动调用refresh端点还是比较麻烦的。自动刷新当然是可以的,自动刷新后面有机会再写。
4.3 小结
本章我们使用Spring Cloud Config创建了配置中心,并对之前的customer微服务进行了改造,让customer服务可以远程获取到配置中心的配置。