• spring cloud项目05:中心化配置-P03-高可用


    Java 8

    spring boot 2.5.2

    spring cloud 2020.0.3

    ---

    本文介绍 高可用配置中心 的搭建。

    目录

    基本架构

    失败快速响应和重试

    配置安全

    加密解密

    参考文档

    思路

    将 配置中心服务、依赖配置中心服务获取配置的应用服务 注册到 服务注册中心,之后,应用服务 通过配置中心 获取各个配置中心服务的信息,再从 任一配置中心获取配置。

    前文

    spring cloud项目01~04

    基本架构

    本文涉及服务

    1、注册中心 service-registration-and-discovery-service

    三个,端口分别为 8771、8772、8773

    2、配置中心 configserver

    二个,端口分别为 10000、10001

    3、应用服务 web3-client

    二个,端口分别为 8083、8093

    注册中心:无需改造

    配置中心改造:

    # 文件 pom.xml
    
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    		</dependency>
    
    # 文件application.properties
    # eureka client
    eureka.instance.lease-renewal-interval-in-seconds=10
    eureka.instance.lease-expiration-duration-in-seconds=20
    eureka.instance.prefer-ip-address=true
    eureka.client.service-url.defaultZone=http://localhost:8771/eureka/,http://localhost:8772/eureka/,http://localhost:8773/eureka/
    

    启动配置中心服务,两台,分别使用端口 10000、10001。来自博客园

    启动后,检查注册中心(任一个)的面板,CONFIGSERVER 有2个。

    应用服务改造:

    示例代码:获取配置中的 message属性值,没有message时,应用服务无法启动

    @RestController
    @RefreshScope
    @RequestMapping(value="hello")
    class HelloController {
    	
    	@Value("${message}")
    	private String message;
        ...
    }
    # 文件 pom.xml
    
    		<dependency>
    			<groupId>org.springframework.cloud</groupId>
    			<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    		</dependency>
    
    # 文件 application.properties
    # 1、直接地址
    #spring.config.import=optional:configserver:http://localhost:10000/
    # 2、注册中心获取
    # 启动失败,,还需要配置 spring.cloud.config.discovery.*
    spring.config.import=optional:configserver:http://CONFIGSERVER
    
    # 关键配置
    spring.cloud.config.discovery.enabled=true
    spring.cloud.config.discovery.service-id=CONFIGSERVER
    
    # eureka client
    eureka.instance.lease-renewal-interval-in-seconds=10
    eureka.instance.lease-expiration-duration-in-seconds=20
    eureka.instance.prefer-ip-address=true
    eureka.client.service-url.defaultZone=http://localhost:8771/eureka/,http://localhost:8772/eureka/,http://localhost:8773/eureka/
    

    启动应用服务web3-client,启动成功,也成功获取了 配置中心 的配置。

    启动后,检查注册中心(任一个)的面板,WEB3-CLIENT 有2个。

    application.properties中的一个变化

    spring.config.import 由 http://localhost:10000/ 改为了 http://CONFIGSERVER

    此时,不再局限于 单个的 配置中心服务 了。来自博客园

    注册中的面板:

    试验1:

    关闭任一configserver,再重启 应用服务。

    结果:

    web3-client 启动成功,成功通过 运行中的唯一configserver 获取到了Git仓库中的配置。

    实现了高可用。

    这就完成了?So easy!应该 还有一些更高级的配置吧?

    失败快速响应和重试

    失败快速响应,英文 fail fast。配置 spring.cloud.config.fail-fast=true 可以实现。来自博客园

    试验了 配置在 bootstrap.properties 中,但未生效,最后,配置到了 application.properties中生效了——不启动CONFIGSERVER时启动客户端服务。

    对比日志

    未配置 或 未正确配置时,启动客户端服务 输出了很多日志:

    在 application.properties 中配置正确(成功)后,启动时错误日志 仅一条:来自博客园

    注,上面两种情况 都是在 CONFIGSERVER 没有启动时测试。

    在生产环境下,客户端服务 和 CONFIGSERVER 是经过网络访问的,在 客户端服务 启动时,网络故障 也会导致 FAIL FAST——客户端服务启动失败。

    假设这个 网络故障持续时间不长,是否可以允许重试来避免 客户端服务 启动失败呢?

    在 客户端服务 添加下面两个依赖包即可:spring-retry 和spring-boot-starter-aop。默认重试机制,每隔1秒+N*01秒 重试一次,总计6次。

    # 文件 pom.xml
    		<!-- 210818 -->
    		<dependency>
    			<groupId>org.springframework.retry</groupId>
    			<artifactId>spring-retry</artifactId>
    		</dependency>
    		<!-- 210818 -->
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-aop</artifactId>
    		</dependency>

    添加后,再次启动 客户端服务:

    此时,出现异常的时间 会更长,大约6秒。

    不过,日志中 没有看到重试。

    修改 spring.cloud.config.retry.* 的一些值,让重试更明显一些。来自博客园

    # 快速失败响应
    spring.cloud.config.fail-fast=true
    # 重试配置
    spring.cloud.config.retry.initial-interval=3000
    # max-interval 必须 大于 initial-interval,因此,也要配置(默认2000)
    spring.cloud.config.retry.max-interval=4000

    配置后,启动失败会等待更长时间了。

    上面 耗时约 18秒,期间重启 CONFIGSERVER & 成功注册到 注册中心,是否能让 客户端服务 失败几次后 成功获取配置 并 启动成功呢?

    CONFIGSERVER 服务启动花了 5秒:

    Started ConfigserverApplication in 5.065 seconds (JVM running for 5.965)

    加上 注册、客户端拉取 注册 的时间,第一次试验 失败了——客户端服务 没有启动成功。

    修改配置:

    # 重试配置
    spring.cloud.config.retry.initial-interval=5000
    spring.cloud.config.retry.max-interval=10000

    改为上面的配置后,CONFIGSERVER 在 客户端服务 启动后几秒内 启动——并在5秒启动成功,此时,客户端服务 web3-client 启动成功

    其启动日志现实了下面的信息:来自博客园

    o.s.b.context.config.ConfigDataLoader    : Fetching config from server at : http://192.168.128.197:10000/
    o.s.b.context.config.ConfigDataLoader    : Located environment: name=web3-client, profiles=[default], label=null, 
    version=71b4d86615b8530bf0926b1cc5d9bc2f47382aa7, state=null

    特别说明

    关闭CONFIGSERVER 后,一段时间内,注册中心 还是会有它的信息,此时启动 客户端服务 没有 快速失败响应,但却有重试的日志输出,当然,客户端服务启动失败。

    不过,上面日志的时间 是不准确的!

    总结:

    fail fast 解决了 因为 配置中心故障等 导致的 客户端服务 启动失败 时 快速响应的问题,

    加上 重试机制,解决了 因为一些 偶发因素(比如,网络波动等) 导致 客户端服务启动失败的情况:来自博客园

    1)CONFIGSERVER正常,网络间歇性不正常;2)启动客户端服务时,CONFIGSERVER没启动, 但在重试期间变好了并且客户端服务可以连接到它们。

    配置安全

    在 CONFIGSERVER 添加 安全保护,此时,客户端服务 访问 它时,就需要账号信息了。

    1、改造 CONFIGSERVER:

    # 文件 pom.xml
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-security</artifactId>
    		</dependency>
    
    # 文件 application.properties
    # 基本的安全保护
    spring.security.user.name=client
    spring.security.user.password=password2021

    启动CONFIGSERVE,输出下面的信息:来自博客园

    CONFIG日志
    o.s.s.web.DefaultSecurityFilterChain     : Will secure any request with [
    org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@42a0501e, 
    org.springframework.security.web.context.SecurityContextPersistenceFilter@2ab26378, 
    org.springframework.security.web.header.HeaderWriterFilter@6056232d, 
    org.springframework.security.web.csrf.CsrfFilter@19a31b9d, 
    org.springframework.security.web.authentication.logout.LogoutFilter@c017175, 
    org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@4462efe1, 
    org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@18371d89, 
    org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter@6aa3bfc, 
    org.springframework.security.web.authentication.www.BasicAuthenticationFilter@2629d5dc, 
    org.springframework.security.web.savedrequest.RequestCacheAwareFilter@404eca05, 
    org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@31e2232f, 
    org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6e4599c0, 
    org.springframework.security.web.session.SessionManagementFilter@107bfcb2, 
    org.springframework.security.web.access.ExceptionTranslationFilter@37d28f02, 
    org.springframework.security.web.access.intercept.FilterSecurityInterceptor@28237492]

    2、改造 客户端服务:

    # 文件 application.properties
    # 用户信息
    spring.cloud.config.username=client
    spring.cloud.config.password=password2021

    启动客户端服务:启动成功,成功获取到 配置中心的 配置信息。来自博客园

    注:

    上面的安全保护只是 最基本的,实际中应该还会有更多需求要实现吧。来自博客园

    spring cloud config还支持更高级别的安全保护错误(比如,基于 OAuth2),本文就不介绍了(暂无能为力)。

    加密解密

    在S.C.config管理的配置中,使用 {cipher}前缀 表名其后的值 是敏感的加密值。

    配置中心服务 会将这样的值 解密,然后,返回给 客户端服务。

    通过这样的机制,线上的加密信息 就可以 放心地给 客户端服务团队了。来自博客园

    注:从这样来看,配置中心 是由 运维 或 底层研发团队管理的,而业务性的微服务 则是由 业务团队管理。

    相关端点(Endpoints):

    /encrypt/status 检查状态,正确配置后,返回 {"status":"OK"}

    /key 无需配置,默认及配置对称秘钥时,返回 {"description":"No public key available","status":"NOT_FOUND"} 

    /encrypt 加密

    /decrypt 解密

    使用这个功能的前提是:

    配置中心的 JCE版本——需要使用 不限长度的(JRE 中默认的是 长度有限的)。来自博客园

    去Oracle官网下载,然后,替换掉本地的。

    注,先关掉上一部的 安全保护机制 再测试,发起请求时,总是得到 未授权错误。来自博客园

    晚点研究了 S.C.security 再尝试。

    改造 CONFIGSERVER :

    # 秘钥:加密
    # 没有配置的话,/encrypt /decrypt 等端点无法使用
    encrypt.key=cfserverevresfc

    加解密试验:

    客户端服务 的配置 加密&获取试验:

    web3-client服务,将 要获取的配置的 message内容加密,然后检查 web3-client服务得到的信息 是否为 未加密内容。

    明文:欢迎来到地球!@RefreshScope 明文

    密文:501ad8af1671a1c9dc28c3cf4c12cf6c0d9f23674997b33550062a497d96471a5eac3e5a2ac13a149afebe5f128b76345b4e28464b46aa649f6a6012b858f6b2

    注:好长的密文!明文越长,密文当然也越长了。

    启动 web3-client服务,检查得到的 message值:获取明文成功

    上面是对称加密,非对称加密就不再试验了。

    加密解密对于保护 重要敏感信息很重要,为了安全,应该用上它。

    >>>THE END<<<

    210818 15:33

    先写到这里,关于中心化配置的更多内容(包括 配置更新后,自动更新、通过 S.C.Bus 更新),以后再试验&介绍吧。

    还得再看看 官文,补充写基础知识才行,虽然用起来了,但也存在不少盲点。

    210819 08:22

    读了一遍S.C.Config的文档,原来,这种 依赖注册中心的方式 存在缺陷的:

    因为依赖注册中心,那么,之前 客户端服务直接 连接 CONFIGSERVER 的 高效方式 被取代了,增加了网络延迟。

    这也是 上面 第一次修改重试时间 后,试验失败的原因——因为延迟更大,因此,这这种方式下,上面的配置还需要进一步调整。

    除了前面介绍的 CONFIGSERVER 作为独立服务外,官文 还介绍了 嵌入CONFIGSERVER 到客户端服务中,这样的确更高效了,但缺点是每个应用服务都要配置。

    除了 Git仓库这种 属性源(Property Source),官文还介绍了 JDBC兼容的数据库、SVN、Hashicorp Vault、Credhub 和 本地文件系统 等,除了一个一个使用外,可以组合使用(官文2.1.11. Composite Environment Repositories)。

    210820 09:10

    在前面的基础上,整合了Spring Cloud Bus,想使用其实现自动更新配置(结合Git仓库的WebHook——一个Web调用配置)。

    添加后, 通过 /actuator 可以看到 暴露了 /actuator/busrefresh(还有一个 /actuator/refresh 是 actuator 本来就有的)。

    执行 /actuator/busrefresh,更新配置失败,错误信息如下:

    o.s.cloud.bus.event.RefreshListener      : Received remote refresh request.
    o.s.b.context.config.ConfigDataLoader    : Fetching config from server at : http://CONFIGSERVER
    o.s.b.context.config.ConfigDataLoader    : Connect Timeout Exception on Url - http://CONFIGSERVER. Will be trying the next url if available
    # 因为配置了 spring-retry,上面的 2、3 还会重复几次

    直接去访问 http://CONFIGSERVER,但是呢,CONFIGSERVER又无法识别。

    定位到了 ConfigServerConfigDataLoader 类里面,Connect Timeout Exception发生在 getRemoteEnvironment 函数中,执行下面的语句超时了——restTemplate:

    response = restTemplate.exchange(uri + path, HttpMethod.GET, entity, Environment.class, args);

    这里的uri就是 http://CONFIGSERVER ,奇怪的是,启动时,这里的 uri是 CONFIGSERVER 的IP地址+端口:

    o.s.b.context.config.ConfigDataLoader    : Fetching config from server at : http://192.168.184.197:10000/

    启动时,配置获取成功。

    可执行 busrefresh 或 refresh 端点时,失败了

    还没弄清楚怎么回事。

    将客户端的 spring.config.import 改为 optional:configserver:http://localhost:10000/ (直接地址),此时执行 两个refresh时,配置更新成功了。

    还需调查原因,或降低版本再尝试。

    还有一个更严重的问题:上面的 refresh 执行失败后,还会导致 DiscoveryClient 被 shutdown

    添加下面的配置 可以避免此问题:

    eureka.client.refresh.enable=false

    打开 debug日志(debug=true),可看到 DiscoveryClient 一直在 更新本地的 uris

    [freshExecutor-0] o.s.web.client.RestTemplate              : HTTP GET http://localhost:8772/eureka/apps/delta
    ...
    [freshExecutor-0] o.s.b.c.c.ConfigDataLocationResolver     : Locating configserver (CONFIGSERVER) via discovery
    [freshExecutor-0] o.s.b.c.c.ConfigDataLocationResolver     : Located configserver (CONFIGSERVER) via discovery. No of instances found: 1
    [freshExecutor-0] o.s.b.c.c.ConfigDataLocationResolver     : Updating config uris to [http://192.168.184.197:10000/]
    

    参考文档

    1、Spring Cloud官方手册

    配置相关

    2、书《Spring Cloud微服务实战》 by 翟永超

    第8章 分布式配置中心:Spring Cloud Config

    3、

  • 相关阅读:
    Today is 5.20,her birthday...
    哭有时,笑有时,悲伤有时,欢乐有时。
    母亲节……
    性格决定命运...
    男人一生只会最爱一个女人?男人的爱一辈子只会付出一次?
    利物浦输了……You'll never walk alone!
    Liverpool,give me power again.Control myself.
    Python编程题27合并两个有序列表
    Python编程题25最长回文串的长度
    Python编程题26爬楼梯
  • 原文地址:https://www.cnblogs.com/luo630/p/15153950.html
Copyright © 2020-2023  润新知