一、ConfigServer从本地读取配置文件
Config Server可以从本地仓库读取配置文件,也可以从远处Git仓库读取。本地仓库是指 将所有的配置文件统一写在ConfigServer工程目录下。ConfigSever暴露HttpAPI接口,ConfigClient通过调用ConfigSever的HttpAPI接口来读取配置文件 。
1.1构建ConfigServer
在主Maven工程下,创建一个Module工程,工程名为config-server,其pom文件继承主Maven工程的pom文件, 并在pom文件中引入ConfigServer的起步依赖sring-cloud-config-server。pom文件代码如下:
<parent>
<groupId>com.hand</groupId>
<artifactId>macro-service</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
</parent>
在程序的启动类 ConfigServerApplication加上@EnableConfigServer 注解,开启ConfigServer的功能,代码如下:
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
在工程的配置文件application.yml中做相关的配置,包括指定程序名为config-server,端号为8678通过spring.profiles.active=native来配置ConfigServer从本地读取配置,读取配置的路径为classpath下的shared目录。 application.yml配置文件的代码如下:
spring:
cloud:
config:
server:
native:
search-locations: classpath:/shared
name: config-client
profiles:
active: dev
application:
name: config-server
server:
port: 8678
在工程的Resources目录下建一个shared文件夹,用于存放本地配置文件。在shared目录下,新建一个config-client-dev.yml文件,用作config-client工程的dev(开发环境)的配置文件。在config-client-dev.yml配置文件中,指定程序的端口号为8673,并定义一个变量foo,变量的值为foo version 1。 代码如下:
server:
port: 8673
foo: foo version 1
1.2 构建ConfigClient
新建Module一个工程,取名为config-client,该工程作为ConfigClient从ConfigServer读取配置文件,该工程的pom文件继承了主Maven工程的porn文件,并在其pom文件引入Config的起步依赖spring-cloud-starter-config和web功能的起步依赖spring-boot-starter-web。代码如下:
<parent>
<groupId>com.hand</groupId>
<artifactId>macro-service</artifactId>
<version>1.0-SNAPSHOT</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
</dependencies>
spring:
application:
name: config-client
cloud:
config:
uri: http://localhost:8678
fail-fast: true
profiles:
active: dev
management:
security:
enabled: false
失败快速响应
不作任何额外配置的情况下,失败响应有点迟钝,举个简单的例子,关掉config-server,我们直接启动config-client,此时启动会报错,但是报错时间较晚,报错的时候系统已经打印了许多启动日志了,如果我们希望在启动失败时能够快速响应,方式很简单,config-client中添加如下配置即可:
spring:
cloud:
config:
fail-fast: true
@RefreshScope
@RestController
@SpringBootApplication
public class ConfigClientApplication {
@Value("${foo}")
String foo;
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
@RequestMapping("/foo")
public String foo(){
return foo;
}
}
可见 eureka-client工程成功地向 eureka-server工程读取了配置文件中 foo变量的值。
二、 ConfigServer从远程git仓库读取配置文件
SpringCloudConfig支持从远程 Git仓库读取配置文件,即ConfigServer可以不从本地的仓库读取,而是从远程Git仓库读取。这样做的好处就是将配置统一管理,并且可以通过SpringCloudBus在不人工启动程序的情况下对ConfigClient的配置进行刷新。这里采用GitHub作为远程Git仓库。
首先,修改ConfigServer的配置文件application.yml,代码如下 :
server:
port: 8678
spring:
cloud:
config:
server:
git:
uri: #配置git仓库的地址
search-paths: config-respo
username: #自己的github用户名
password: #自己的github密码
name: config-client
label: master
application:
name: config-server
Git仓库的配置文件信息:
可见,config-server从远程Git仓库读取了配置文件,config-client从config-server读取了配置文件。
三、 搭建高可用的Config Server
在上一节讲解了 Config Client如何从配置中心 Config Server读取配置文件 ,配置 中心如 何从远程 Git 仓库读取配置文件。当服务实例很多时,所有的服务实例需要同时从配置中心 Config Server读取配置文件,这时可以考虑将配置中心 Config Server做成一个微服务,并且将 其集群化,从而达到高可用。配置中心 Config Server 高可用的架构图如下图所示。 ConfigServer和ConfigClient向EurekaServer注册,且将ConfigServer多实例集群部署。
高可用的 Config Server
3.1 构建EurekaServer
3.2 改造ConfigServer
(1)pom文件引入eureka的依赖,代码如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
(2)在工程的启动类 ConfigServerApplication加上@EnableEurekaClient注解,开启 EurekaClient 的功能,代码如下 :
@SpringBootApplication
@EnableConfigServer
@EnableEurekaClient
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
(3)在工程的配置文件 application.yml 文件中添加服务注册的地址,代码如下 :
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8671/eureka/
3.3 改造Config Client
spring:
application:
name: config-client
cloud:
config:
fail-fast: true
discovery:
enabled: true #开启配置服务发现
serviceId: config-server #配置服务实例名称
profiles:
active: dev
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8671/eureka/
可见, config-server从远程Git仓库读取了配置文件, config-client从config-server读取了 配置文件。
多次启动 config-client 工程,从控制台可以发现它会轮流地从 和
的ConfigServer读取配置文件,并且做了负载均衡。
四、使用SpringCloudBus刷新配置
SpringCloudBus是用轻量的消息代理将分布式的节点连接起来,可以用于广播配置文件 的更改或者服务的监控管理。一个关键的思想就是,消息总线可以为微服务做监控,也可以实现应用程序之间相互通信。 SpringCloudBus可选的消息代理组建包括RabbitMQ、AMQP和Kafka等。这里是用RabbitMQ作为SpringCloud的消息组件去刷新更改微服务的配置文件。
如果有几十个微服务,而每一个服务又是多实例,当更改配置时,需要重新启动多个微服务实例,会非常麻烦。SpringCloudBus的一个功能就是让这个过程变得简单,当远程Git仓库的配置更改后,只需要向某一个微服务实例发送一个Post请求,通过消息组件通知其他微服务实例重新拉取配置文件。 如下所示,当远程Git仓库的配置更改后,通过发送“/bus/refresh”Post请求给某一个微服务实例,通过消息组件,通知其他微服务实例,更新配置文件。
根据此图我们可以看出利用Spring Cloud Bus做配置更新的步骤:
- 1、提交代码触发post给客户端A发送bus/refresh
- 2、客户端A接收到请求从Server端更新配置并且发送给Spring Cloud Bus
- 3、Spring Cloud bus接到消息并通知给其它客户端
- 4、其它客户端接收到通知,请求Server端获取最新配置
- 5、全部客户端均获取到最新的配置
4.1 代码实现
4.11 添加依赖
这里是在上面的代码基础上进行改造的 ,只需要改造config-client工程。首先,需要在pom文件中引入用RabbitMQ实现的SpringCloudBus的起步依赖spring-cloud-starter-bus-amqp。 如 果读者需要自己实践,则需要安装RabbitMQ服务器。 pom文件添加的依赖如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
4.12 配置信息
在工程的配置文件application.yml添加RabbitMQ的相关配置, host为RabbitMQ服务器的IP地址, port为RabbitMQ服务器的端口,usemame和password为RabbitMQ服务器的用户名和密码。通过消息总线更改配置,需要经过安全验证,为了方便讲述 ,先把安全验证屏蔽掉,也就是将management.security.enabled改为false。代码清单如下 :
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
management:
security:
enabled: false
4.13 开启更新机制
需要在更新的配置类上加@RefreshScope注解,只有加上了该注解,才会在不重启服务的情况下更新配置,如本例中更新配置文件foo变量的值。代码清单如下:
@RefreshScope
@RestController
@SpringBootApplication
@EnableEurekaClient
public class ConfigClientApplication {
@Value("${foo}")
String foo;
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
@RequestMapping("/foo")
public String foo(){
return foo;
}
}
更改远程 Git仓库,将 foo的值改为“ foo version 3”。通过Postman或者其他工具发送一个Post请求,请求发送成功,再访问或者,浏览器都会显示“ foo version 3”,如下图: 可见,通过向8672端口的微服务实例发送Post请求 ,请求刷新配置,由于使用了SpringCloudBus,其他服务实例(如案例中的8673端口的服务实例) 会接收到刷新配置的消息,也会刷新配置。
4.2 刷新方式优化
由于每次手动刷新客户端也很麻烦,这里考虑使用自动触发的方式刷新客户端或服务端。由于这里我们使用的是github作为配置中心,故使用github的webhook就可以解决自动刷新的目的。
4.21 webhook
webhook配置
4.22 配置域名
隧道配置信息3、配置好隧道信息,在控制台的ngrok目录执行下面代码
./sunny clientid 你的隧道id
这里就能看到自定义域名映射成功了。如下图:
至此,webhook和ngrok的配置完成了,下面就可以通过push配置信息的修改,来触发刷新客户端配置的功能。
(1)访问结果如下:
(2)修改配置文件内容为:foo version 3 提交并push到Github
(3)在webhook里看到请求push触发刷新请求成功
(4)刷新结果如下:
至此,就实现了使用webhook和ngrok动态刷新配置文件的功能。
4.3 改进版本
在上面的流程中,我们已经到达了利用消息总线触发一个客户端bus/refresh,而刷新所有客户端的配置的目的。但这种方式并不优雅。原因如下:
- 打破了微服务的职责单一性。微服务本身是业务模块,它本不应该承担配置刷新的职责。
- 破坏了微服务各节点的对等性。
这时SpringCloudBus做配置更新步骤如下:
- 1、提交代码触发post请求给bus/refresh
- 2、server端接收到请求并发送给Spring Cloud Bus
- 3、Spring Cloud bus接到消息并通知给其它客户端
- 4、其它客户端接收到通知,请求Server端获取最新配置
- 5、全部客户端均获取到最新的配置
4.2.1 修改config-server
pom添加依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
修改application.yml:
spring:
rabbitmq:
host: 192.168.0.6
port: 5672
username: guest
password: guest
management:
security:
enabled: false
配置文件增加RebbitMq的相关配置,关闭安全验证。这样server端代码就改造完成了。重复刷新config-client端的操作,就会发现服务加载了更新的配置信息。这里就不一一展示了。
4.4 局部刷新
某些场景下(例如灰度发布),我们可能只想刷新部分微服务的配置,此时可通过/bus/refresh端点的destination参数来定位要刷新的应用程序。
例如:/bus/refresh?destination=customers:8000,这样消息总线上的微服务实例就会根据destination参数的值来判断是否需要要刷新。其中,customers:8000指的是各个微服务的ApplicationContext ID。
destination参数也可以用来定位特定的微服务。例如:/bus/refresh?destination=customers:**,这样就可以触发customers微服务所有实例的配置刷新。