Spring云源生应用-中心化配置 原

2019-04-18 16:21:28 浏览数 (1)

一切从配置开始

在微服务架构概念索引一文中介绍了整个云源生应用的搭建体系,后续的内容将会从Spring Cloud从技术架构,到基础设置再到团队协作方式一点一滴的记录搭建整个云服务的过程。现在从最基本的中心化配置开始介绍。

Spring基金会项目繁多、种类各异,但是他们都脱离不了一个基本的要求——基于Spring Ioc的配置。Spring的基础在于IoC容器,各种各样的项目都在IoC容器的基础之上扩展而来。在设计模式与IoC中已经介绍了IoCs的目的就是解决数据与Bean的关系、以及Bean与Bean之间的关系。

Spring Cloud 中心化配置

在单Jvm的Spring应用中各种配置文件都是通过Profile结合PropertySource进行管理,而到了Spring Boot则提供了大量的默认配置简化了这个过程。而在Spring Cloud中需要管理大量的节点,中心化配置的需求随之而产生。

Spring Cloud的中心化配置并没有什么特别神奇的地方,实际上就是把本该放到本地的配置文件(例如application.yml)统一放置到一个仓库中。然后用一个Web服务来管理仓库,其他微服务节点从这个Web服务获取配置文件。所以就算没有Spring Cloud Config提供这个功能,我们也可以自己编码在ApplicationContext的启动装载IoC之前先处理好配置。

在假设有别的Spring Cloud知识之前,本文介绍不使用Netflix和Eureka注册服务的来管理中心化配置的方法。

上图是Spring Cloud Config的基本结构。左侧是一系列的微服务节点,右侧是他们对应的配置文件。例如Node-1服务对应的是node-1-config.yml文件,在单一的应用中本来node-1-config.yml文件应该是放置在Node-1工程的classpath下的,现在的区别是将他们分开,将配置文件统一放置到一个仓库中,然后用Config-Service来管理。

中心化配置就是这么简单,除了把配置文件拆走其他的使用方式完全一样,可以通过PropertySources或@Value注解来获取配置的参数。

至于中心化配置有什么好处就不用一一细说了,除了在负载均衡中减少配置之外,也便于环境管理等等。

启用中心化配置

清楚原理之后做事就简单了,案例代码一共包含三个工程(github源码),分别是configuration-serverconfiguration-node-1configuration-node-2。configuration-server就是图中的Config-Service,他为所有的微服务节点提供中心化配置服务,所有的配置都在独立的git仓库中,在某个微服务节点请求配置数据的时候,configuration-server会去仓库中获取对应的配置并传输给微服务节点。

Config Service

Spring Cloud的中心化配置服务通过Http请求提供配置管理服务,所以他自身也是一个Web。要启用的配置服务就2步:1.写一个配置文件指定服务端口和配置文件仓库,2.启动Spring Web服务。

下面application.yml指定了中心化服务的端口(8888),然后指定了配置文件的Git仓库。配置文件的仓库可以是本地文件夹、可以是git仓库或者其他任何形式,这里以Git作为例子。

代码语言:javascript复制
# application.yml
server:
  port: 8888

spring:
  cloud:
    config:
      server:
        git:
          uri:  https://github.com/chkui/spring-cloud-repo

配置好之后启动Web服务:

代码语言:javascript复制
// chkui.spring.cloud.config.ConfigServiceApplication

@EnableConfigServer //表示这是一个Configuration 服务
@SpringBootApplication
public class ConfigServiceApplication {
	public static void main(String[] args) {
		SpringApplication.run(ConfigServiceApplication.class, args);
	}
}

启动web并没有任何特别之处,像个普通的Spring Boot工程启动即可。

Client Node

有了中心化服务之后自然是要使用它。也仅仅需要2步:1.配置微服务,2.启动微服务。配置内容如下:

代码语言:javascript复制
# bootstrap.yml
spring:
  application:
    name: node-config-1
  cloud:
    config:
      uri: http://localhost:8888

访问中心化配置的参数主要就2个:1.指定配置文件名称、2.指定中心化服务器的地址与端口。

切记使用中心化配置时,像上面这个与服务器相关的配置要写到bootstrap.yml中,这样才能在访问远程配置之前先获取远程服务器的参数。写到application.yml里会导致永远都使用默认参数

配置文件的名称要与配置仓库中的文件名对应。下面是configuration-node-1对应的配置文件的内容:

代码语言:javascript复制
# https://github.com/chkui/spring-cloud-repo/blob/master/node-config-1.yml
server:
  port: 9081 #配置node-1端口
message: Response Node Client1 With WebFlux(Netty)! #配置message参数。

下面是configuration-node-1中的主要代码:

代码语言:javascript复制
package chkui.spring.cloud.config;
//import 省略

@SpringBootApplication
public class ConfigClientApplication1 {
    public static void main(String[] args) {
        SpringApplication.run(ConfigClientApplication1.class, args);
    }
}

@RestController
class MessageRestController {

    //通过@Value注解获取中心化配置的参数
    @Value("${message:Configuration Server Error(Node-1)}")
    private String message;

    @RequestMapping("/message")
    Mono<String> getMessage() {
        return Mono.just(this.message);
    }
}

configuration-node-1用spring-boot启动了一个WebFlux,这个时候访问 http://localhost:9081/message 会返回配置文件中的信息:Response Node Client1 With WebFlux(Netty)!

动态刷新

可以在不重启微服务节点的情况下更新配置参数,这在某些场景下非常意义。刷新配置参数主要是使用了ConfigurableApplicationContextrefresh接口,不过Spring Cloud整合了Actuator功能直接外部调用接口即可。

configuration-node-2中的代码演示了这个过程:

首先需要引入spring-boot-starter-actuator:

代码语言:javascript复制
dependencies {
	compile('org.springframework.cloud:spring-cloud-starter-config')
	compile('org.springframework.boot:spring-boot-starter-actuator') //引入actuator
	compile('org.springframework.boot:spring-boot-starter-web')
}

然后在Controller层增加*@RefreshScope*注解:

代码语言:javascript复制
@SpringBootApplication
public class ConfigClientApplication2 {
	public static void main(String[] args) {
		SpringApplication.run(ConfigClientApplication2.class, args);
    }
}

@RefreshScope
@RestController
class MessageRestController {
    @Value("${message:Configuration Server Error(Node-2)}")
    private String message;

    @RequestMapping("/message")
    String getMessage() {
        return this.message;
    }
}

最后在本地配置参数中暴露Actuator的接口:

代码语言:javascript复制
#
management:
  endpoints:
    web:
      exposure:
        include: '*'

现在就可以通过Actuator暴露的接口来更新配置参数——Post访问 localhost:9082/actuator/refresh

代码语言:javascript复制
curl localhost:9082/actuator/refresh -d {} -H "Content-Type: application/json"

到了Spring Boot 2.×之后Actuator默认关闭所有Web服务接口。这里仅仅是说明有这个功能,通常不会使用Web暴露接口的方式来操作。如下图,Spring Boot和Spring Cloud已经暴露了大量的MBean,通常会使用JMX来管理和监控服务状态。

所以如果要使用 配置刷新 等功能建议使用Spirng Boot Admin或者其他基于JMX的管理工具来操作。

配置说明

服务端

启用必要配置:

  1. 引入spring-cloud-config-server
  2. 设置服务端口:server.port
  3. 设置仓库地址:spring.cloud.config.server.git.uri,仓库类型可以有很多种。

更多配置:

  1. spring.cloud.config.server.git.timeout可以设置访问仓库的超时时间(秒)。
  2. spring.cloud.config.server.git.uri可以使用{application}{profile}等占位符。用于复杂的仓库管理。
  3. 占位符可以配合使用通配符和正则表达式来解决更复杂的配置问题:详细。
  4. 通常git仓库都涉及到权限认证,可以用HTTPS或者SSH-KEY的方式解决。
  5. force-pull参数可以强制更新本地仓库中的配置文件。
  6. spring.cloud.config.server.git.repos参数可以用于多项配置,用子目录来区分配置。
  7. spring.cloud.config.server.git.refreshRate可以控制服务器到仓库的更新频率。默认为0,表示只要有微服务请求了数据就会去远程仓库获取文件。
  8. spring.cloud.config.server.git.basedir用于配置远程文件下载本地的临时文件夹,默认使用操作系统的/tmp
  9. 除了使用本文介绍的使用git作为配置文件仓库,还支持本地文件系统、Vault、数据库、Redis、CredHub等方式管理配置仓库。
  10. [可以同时使用git、svn等仓库](Composite Environment Repositories "可以同时使用git、svn等仓库")。
  11. spring.cloud.config.server.overrides用于配置通用属性,通过这个配置可以让所有的节点都获取这个属性。
  12. 安全管理用于在一些非独立的环境做中心化配置,比如配置服务器直接放置在外网。
  13. 通常情况下会将Config Server作为一个独立的Web服务,但是也可以使用spring.cloud.config.server.bootstrap和``嵌入别的系统中去。

客户端

  1. 引入spring-cloud-starter-config
  2. bootstrap.yml设置中心化配置服务地址:spring.cloud.config.uri
  3. bootstrap.yml设置文件名:spring.application.name
  4. 可以在bootstrap.yml设置profiles:spring.profiles.active。非必要

配置仓库

  1. 中心化配置文件同样支持profile特性,所以在仓库中具备以下命名规则:
代码语言:javascript复制
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

(adsbygoogle = window.adsbygoogle || []).push({});

0 人点赞