摘自:https://www.cnblogs.com/coding-farmer/p/12077297.html
Spring Cloud第十一篇 | 分布式配置中心高可用
本文是Spring Cloud专栏的第十一篇文章,了解前十篇文章内容有助于更好的理解本文:
一、前言
为了保证配置中心服务的高可用,将配置中心服务端做成一个微服务,将其集群化,从而达到高可用,当你部署了多个Config Server实例并预期一个或多个实例不时不可用时,为确保高可用性,你可以指定多个URL(作为spring.cloud.config.uri属性下的逗号分隔列表),也可以让所有实例在Eureka等Service Registry中注册(如果使用发现优先Bootstrap模式)。注意:只有在Config Server未运行时(即应用程序已退出时)或发生连接超时时,才能确保高可用性,例如:如果Config Server返回500(内部服务器错误)响应或Config Client从Config Server收到401(由于凭据错误或其他原因),则Config Client不会尝试从其他URL获取属性,这种错误表示用户问题而不是可用性问题。
上一篇文章《Spring Cloud第十篇 | 分布式配置中心Config》讲述了一个服务如何从配置中心读取文件,上一篇案例只是通过client直接访问server,地址直接写死,这种方式显然不够灵活,当服务实例很多时,都从配置中心读取文件,如果配置中心服务端地址变动的话,那我们所有的服务都启动不起来了,都需要变更配置中心服务端地址,特别麻烦,由于微服务使用了注册中心,以及注册中心的优点等,我们可以结合Eureka注册中心,将配置中心做成微服务,将其集群化部署,达到配置中心的高可用,架构图如下:
二、改造配置中心服务端
1、添加注册中心客户端依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
2、主启动类上添加注解@EnableEurekaClient
3、在application.yml文件中添加Eureka相关配置
eureka: client: service-url: defaultZone: http://localhost:8700/eureka #客户端每隔30秒从Eureka服务上更新一次服务信息 registry-fetch-interval-seconds: 30 #需要将我的服务注册到eureka上 register-with-eureka: true #需要检索服务 fetch-registry: true #心跳检测检测与续约时间 instance: #告诉服务端,如果我10s之内没有给你发心跳,就代表我故障了,将我剔除掉,默认90s #Eureka服务端在收到最后一次心跳之后等待的时间上限,单位为秒,超过则剔除(客户端告诉服务端按照此规则等待自己) lease-expiration-duration-in-seconds: 10 #每隔2s向服务端发送一次心跳,证明自已依然活着,默认30s #Eureka客户端向服务端发送心跳的时间间隔,单位为秒(客户端告诉服务端自己会按照该规则) lease-renewal-interval-in-seconds: 2 # 启用ip配置 这样在注册中心列表中看见的是以ip+端口呈现的 prefer-ip-address: true # 实例名称 最后呈现地址:ip:2002 instance-id: ${spring.cloud.client.ip-address}:${server.port}
4、然后启动config server服务端就行了
三、改造配置中心客户端
1、改造步骤同1-1,1-2,1-3,然后在application.yml文件中将spring.cloud.config配置修改为如下样子
spring: cloud: config: #uri则表示配置中心的地址 #uri: http://localhost:8888 #注:config 客户端在没有 spring.cloud.config.name属性的时候,服务端{application} 获取的是客户端 #spring.application.name的值,否则,获取的是 spring.cloud.config.name的值。 #1)、当没有spring.cloud.config.name时,客户端获取的是spring.application.name 所对应的git库中的文件,并且只能获取一个文件 #2)、当一个项目中有需求要获取多个文件时,就需要用到spring.cloud.config.name这个属性,以逗号分割 name: configclient profile: dev #label对应了label部分 label: master discovery: #表示开启通过服务名来访问config-server enabled: true #则表示config-server的服务名 service-id: tmgsp-config-server
2、查看注册中心上注册的服务
3、然后访问http://localhost:8881/index可以看到相同效果,如果需要配置中心高可用,只需要将配置中心启动多个实例就行了。
四、失败快速响应与重试
1、失败快速响应
Spring Cloud Config的客户端会预先加载很多其他信息,然后再开始连接Config Server进行属性的注入。不作任何额外配置的情况下,失败响应有点迟钝,举个简单的例子,关掉config-server,我们直接启动config-client,此时启动会报错,但是报错时间较晚,当我们构建的应用较为复杂的时候,可能在连接Config Server之前花费较长的启动时间,而在一些特殊场景下,我们又希望可以快速知道当前应用是否能顺利地从Config Server获取到配置信息,这对在初期构建调试环境时,可以减少很多等待启动的时间。要实现客户端优先判断Config Server获取是否正常,并快速响应失败内容,只需在Config客户端的bootstrap.yml中配置参数spring.cloud.config.failFast=true即可。
此时不启动config-server直接启动config-client依然会报错,但是我们看到报错时间较早,系统都没打印几条启动日志。
2、重试
上面我们提过了当Config Server宕机或是客户端配置不正确导致连接不到Config Server而启动失败的情况,快速响应的配置可以发挥比较好的效果。但是,如果网络波动等其他间歇性原因导致的问题,直接启动失败似乎代价有些高。所以Config客户端还提供了自动重试的功能,在开启重试功能前,先确保已经配置了(确保开启失败快速响应)spring.cloud.config.failFast=true在进行下面操作。
2-1、在客户端添加依赖
<!--连接config-server重试机制相关依赖--> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2-2、不需要再做其他任何配置,启动客户端应用,在控制台中可以看到如下内容。客户端在连接Config Server失败之后,会继续尝试,直到第6次失败后,才返回错误信息。通过这样的重试机制,可以避免一些间歇性问题引起的失败导致客户端应用无法启动的情况。查看日志如下,测试的时候切记Config Server需要在注册中心没有剔除掉,要是剔除掉那就没的说了。
INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_SPRINGCLOUD-CONFIG-CLIENT/172.20.10.2:8881 - Re-registering apps/SPRINGCLOUD-CONFIG-CLIENT INFO 9996 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_SPRINGCLOUD-CONFIG-CLIENT/172.20.10.2:8881: registering service... INFO 9996 --- [tbeatExecutor-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_SPRINGCLOUD-CONFIG-CLIENT/172.20.10.2:8881 - registration status: 204 INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://172.20.10.2:8888/ INFO 9996 --- [ main] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.20.10.2:8888/. Will be trying the next url if available ERROR 9996 --- [ main] o.s.boot.SpringApplication : Application run failed
2-3、从日志上可以看出Config client一共尝试了六次去访问Config server,六次都失败了才抛异常和重试机制相关的配置有如下四个:
# 配置重试次数,默认为6 spring.cloud.config.retry.max-attempts=6 # 间隔乘数,默认1.1 spring.cloud.config.retry.multiplier=1.1 # 初始重试间隔时间,默认1000ms spring.cloud.config.retry.initial-interval=1000 # 最大间隔时间,默认2000ms spring.cloud.config.retry.max-interval=2000
五、动态刷新配置
1、有的时候,我动态的更新了Git仓库中的配置文件,那么我如何让我的config-client能够及时感知到呢?方式很简单,首先在config-client中添加如下依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
由于我们父模块(springcloud-learn)依赖中已经引入该依赖,所以可以省略不引入。
该依赖中包含了/refresh端点的实现,我们将利用这个端点来刷新配置信息。
2、在需要刷新的Bean上添加@RefreshScope注解,不然客户端不知道刷新哪里
3、然后需要在application.yml中配置暴露/refresh端点
management: endpoints: web: exposure: include: ["info","health","refresh"]
4、然后启动访问http://localhost:8881/index
5、修改码云上的configclient-dev.yml文件,因为我们客户端加载的是这个配置文件
6、然后用postman工具向http://localhost:8881/actuator/refresh地址发送POST请求(该端点只接受post请求,日志上可以看出),结果如下:
7、重新访问http://localhost:8881/index显示内容如下
详细参考案例源码:https://gitee.com/coding-farmer/spirngcloud-learn