spring cloud config server使用svn来作为外部配置中心

来源:互联网 发布:织梦源码安装 编辑:程序博客网 时间:2024/04/26 04:44

默认情况下config server是使用git来作为配置中心的,由于还是有许多公司还是用svn来作为版本管理工具,幸好config server服务也支持svn配置,下面就来试验一下

服务端配置:

新建一个config-server项目,pom文件:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>config-server</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>config-server</name><description>Demo project for Spring Boot</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.7.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><spring-cloud.version>Dalston.SR3</spring-cloud.version></properties><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-config-server</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka</artifactId></dependency><!-- https://mvnrepository.com/artifact/org.tmatesoft.svnkit/svnkit --><dependency><groupId>org.tmatesoft.svnkit</groupId><artifactId>svnkit</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

这里要注意由于使用的是svn来保存配置,所以还要引入svnkit依赖,这里还引入了eureka依赖,因为我们将config-server配置成了高可用,即config-server也会作为一个服务注册到注册中心去,到时候客户端可以通过服务Id来自动发现一组config-server服务负载均衡的访问。

启动类:

package com.example.configserver;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;import org.springframework.cloud.config.server.EnableConfigServer;@SpringBootApplication@EnableConfigServer@EnableDiscoveryClient //整合eureka,实现config server高可用,将config-server作为服务注册public class ConfigServerApplication {public static void main(String[] args) {SpringApplication.run(ConfigServerApplication.class, args);}}


加了两个注解,一个是@EnableConfigServer,将该服务作为一个config server服务启动,另一个@EnableDiscoveryClient作为一个服务注册到eureka


application.properties配置文件:

spring.application.name=config-serverserver.port=7001eureka.client.service-url.defaultZone=http://peer1:8001/eureka/,http://peer2:8002/eureka/#关闭配置仓库的健康检查#spring.cloud.config.server.health.enabled=false#配置属性覆盖,定义了两个属性,name和from#spring.cloud.config.server.overrides.name=xiaojun#spring.cloud.config.server.overrides.from=shanghai#spring.cloud.config.server.git.uri=https://github.com/hupuxiaojun/spring-cloud/#spring.cloud.config.server.git.uri=https://github.com/hupuxiaojun/{application}-config/ #通过占位符动态管理配置仓库,将获取客户端的spring.application.name作为{application}值去获取配置#spring.cloud.config.server.git.uri=file:///D:/config-repo/resources/ #使用本地git clone目录,需要先从git上拉到本地来,windows下面用file:///作为schema# 配置仓库路径下的相对搜索位置,可以配置多个#spring.cloud.config.server.git.search-paths=resources#spring.cloud.config.server.git.username=#spring.cloud.config.server.git.password=#spring.cloud.config.server.git.basedir=/data  #默认在系统临时目录下面,需要调整一下避免临时文件被系统自动清理#使用svn作为配置仓库,必须显示声明profiles.active=subversion,不然还是用的gitspring.profiles.active=subversionspring.cloud.config.server.svn.uri=http://192.168.78.239/usvn/svn/datadev/docs/config/spring.cloud.config.server.svn.username=xiaojspring.cloud.config.server.svn.password=xxxxxxspring.cloud.config.server.svn.search-paths={application} #使用{application}占位符spring.cloud.config.server.svn.default-label=trunkspring.cloud.config.server.svn.basedir=/data #默认在系统临时目录下面,需要调整一下避免临时文件被系统自动清理management.security.enabled=false

这里要注意的是要使用svn作为配置中心,必须显示声明spring.profiles.active=subversion,不然还是用的git,svn默认的lable是trunk,git默认的是master,客户端在使用的时候注意指定label,这里search-paths使用了{application}占位符,用来根据客户端的spring.application.name来动态搜索配置目录,即按照每个应用一个配置目录的形式来组织配置。

这个demo的svn目录结构如下:


每个文件里面都定义了一个from属性,例如mapp-dev.properties中定义的是from=svn-dev-1.0,其他类似。

启动服务端,访问http://localhost:7001/myapp/dev,能够得到如下结果:

{    "name": "myapp",    "profiles": [        "dev"    ],    "label": null,    "version": "606",    "state": null,    "propertySources": [        {            "name": "http://192.168.78.239/usvn/svn/datadev/docs/config/trunk/myapp/myapp-dev.properties",            "source": {                "from": "svn-dev-1.0"            }        },        {            "name": "http://192.168.78.239/usvn/svn/datadev/docs/config/trunk/myapp/myapp.properties",            "source": {                "from": "svn-default-1.0"            }        }    ]}


客户端配置:

接下来新建一个config-client客户端,尝试获取一下config-server上的配置,pom文件:


<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>config-client</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>config-client</name><description>Demo project for Spring Boot</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.7.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><spring-cloud.version>Dalston.SR3</spring-cloud.version></properties><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-eureka</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><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

客户端也引入了eureka依赖,由于客户端直接从config-server上获取配置而不是从svn上获取,所以不需要引入svnkit依赖


客户端启动类:

package com.example.configclient;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.discovery.EnableDiscoveryClient;@SpringBootApplication@EnableDiscoveryClientpublic class ConfigClientApplication {public static void main(String[] args) {SpringApplication.run(ConfigClientApplication.class, args);}}

添加了@EnableDiscoveryClient注解,用来注册和发现服务

客户端为了能动态加载外部配置,需要将配置信息卸载bootstrap.properties中而不是application.properties中,在resources目录下面新建一个bootstrap.properties文件:

spring.application.name=myappeureka.client.service-url.defaultZone=http://peer1:8001/eureka/,http://peer2:8002/eureka/#高可用配置中心通过服务Id去自动发现config-server服务组spring.cloud.config.discovery.enabled=truespring.cloud.config.discovery.service-id=config-serverspring.cloud.config.profile=dev#spring.cloud.config.label=master#使用svn配置时lable是trunkspring.cloud.config.label=trunk#高可用模式下配置了service-id就不用指定uri了#spring.cloud.config.uri=http://localhost:7001/server.port=7002management.security.enabled=false

高可用配置中心是通过服务id去自动发现config-server服务地址的,所以不用配置spring.cloud.config.uri指向具体的地址了,直接通过spring.cloud.config.discovery.service-id来指定config server的服务名,这里是config-server,同时需要将spring.cloud.config.discovery.enable=true(默认为false,使用uri方式),声明通过服务发现的方式来获取config server地址。


最后我们在config-client服务上写一个controller类:

package com.example.configclient.controller;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.cloud.context.config.annotation.RefreshScope;import org.springframework.core.env.Environment;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RefreshScope //允许动态刷新配置@RestControllerpublic class MyController {    @Value("${from}")    private String from;    @Autowired    private Environment env;    @RequestMapping("/from")    public String from() {        return this.from;    }    @RequestMapping("/from-env")    public String fromEnv() {        //也可以通过env来获取        return env.getProperty("from", "undefined");    }}

为了能动态刷新配置,需要在允许动态刷新的类上注解@RefreshScope,这里我们定义了一个变量from,值是通过@Value("${from}")注解从配置中获取的值自动注入的,也可以从Environment中获取

接下来我们启动客户端,然后访问http://localhost:7002/from,将显示svn-dev-1.0。说明正确从config server上获取了配置信息

接下来我们修改一下svn上的myapp-dev.properties,将from属性值改成svn-dev-2.0,然后commit提交svn。访问config server的http://localhost:7001/myapp/dev,属性已经变成了svn-dev-2.0,但是访问http://localhost:7002/from显示的还是svn-dev-1.0。

我们需要手动刷新一下配置,post调用acturator提供的/refresh端点来刷新配置, post http://localhost:7002/refresh。

然后再请求一下http://localhost:7002/from,将显示最新的svn-dev-2.0。


由于是外部集中式配置中心,所以我们希望将一些敏感的信息加密处理,config server也支持加密解密,首先生成一个keystore文件:

keytool -genkeypair -alias config-server -keyalg RSA -dname "CN=xiaojun, OU=kingnet, O=kdc, L=shanghai, ST=shanghai, C=china" -keypass 222222 -keystore config-server.keystore -storepass 111111

会在当前目录下面生成一个config-server.keystore文件,我们可以将它放到config-server项目的resources目录下面去.

然后在config-server项目中resources目录下面创建bootstrap.yml配置文件:

#encrypt相关的配置必须配置在bootstrap配置文件中,不然没法解密# encrypt.key这个版本有BUG还没修复,导致无法使用加解密 https://github.com/spring-cloud/spring-cloud-config/issues/767#encrypt.key: xiaojun##下面除了location之外的其他三个属性建议设置到部署机的环境变量当中去更安全,对应的是ENCRYPT_KEY_STORE_ALIAS,ENCRYPT_KEY_STORE_PASSWORD,ENCRYPT_KEY_STORE_SECRETencrypt:  key-store:    location: config-server.keystore    password: 111111    secret: 222222    alias: config-server

这里注意一点,encrypt相关的配置必须配置在bootstrap配置文件中,不然没法解密属性,另外password,secret,alias这三个属性最好以环境变量的形式配置到config-server部署机器上,不要直接写到配置文件中,这样做更安全一些。

然后我调整了一下config-server的application.yml配置:

spring:  profiles:    active: subversion  cloud:    config:      server:        svn:          uri: '{cipher}AQAl5kbzrB6ghxjyQ760VzyxsXKiWJ58oXXOvIUw9B6bHf0+0kfxyIMw1XjL0ISqGfmvjW/BN6o1Y0l6NaYaT1sMcplUkzQgewELJYSLkAjBpAfWjsg+F0kQSLHxvyCYciZharikZ7IUHEYdB2iSe/YcL8g28WMG4jFEiqg4nRaMb8retOMa/7YI6mMLwKH8FevEmXlqo7gzoJwfx/uPdBhLWyJL1ABOZMA++1K5dhYVVTelAMDcg49r2s1bDotqcDIP7ngUrVjpJsdIfCKWcYVmVHBBpGeFcgnBLkYh5WAneeSckeZ+4EhPl+6JbnUVlSLJoxI3P7B1bSTDgHfyzxWEwX9Tbq+8ZQff65ad38mVx9wz/6c8XyS9gEaVwM6OAdZpoSoyJNaxaWZ66rzRXiTHefE716m8DGRbzZViy7IMJY2w0C1RIcWjnhbJGjZvTEI='          username: '{cipher}AQBubzgpKhIQ/qSaOKx5PdHUX7K8eSETovb1QaHUNyx5k12RCozVFvkeT3P41V9bYpSvEaWLF2s+1rBoXjPjBXvdoCxXU//S4DpD95SF+ZudsVzH7CktSesE1IX/ViZbBuAQ/NuGBAJvoBN530TL8ny0OS9g6DWX6d9ilZPis2/rTTH3U0EJ7qpFNA8w7LfZbAUhOog8s+CACj7f8eWDmdM7kKojVV5O+Ms3QxCmJWZUQSlv2C/IQpQjoiuFTeYRedyHS1p0UN40E3/ywpIUMWFdz8NY/gOo2nW3b/HfP6ZGDAQIirCghNdOzD+rHncrnoC2IXT3Y7Gj+rpQp2m5hwt8ySCEFltuGuH9Mp3JNAVt4Z9YAy2kRx+SF5MwxCFwlLg='          password: '{cipher}AQAoFnasNP9emLuGNt77NILtksE74KoIfWsSAAOMO8A9l1DjoSQFI5CMzcKNDNBZ+ES5yKrq8IpVkLNsKg3AMpAlfX7zHnk4XKzbYPZetheAlYGhDWJacTy6BKzsj0sIytu6lPfZ/erJCnEHG94CcKRLheRKHmhbwt4nDB/VBpIkDaAmVdjRTyYk9jPZ+ZgO53qrO1gAsdww81JkqPaFNL3yql9wksXdRd/JRh4z+aCXi52VpPN3hPo3Tx2Ikt8p4AbYu9uNLNpDLLSvOihPmMUUB09lZEh3sQFbsVheVS72/xNM2ha0x+nSZKL8V3EQn7XN46HfvQ5csvLVRunov8NmC/5glNVH+l3OLDmyYAutkHGMVoFQVbGkL1U64uTvYzw='          search-paths: '{application}'          default-label: trunk          basedir: /data/config  application:    name: config-serverserver:  port: 7001eureka:  client:    service-url:      defaultZone: http://peer1:8001/eureka/,http://peer2:8002/eureka/management:  security:    enabled: false

将svn地址、用户名、密码都加密处理了,通过加上{cipher}这个hint,在装载配置的时候会自动解密。yml文件中如果属性值用了{},必须用引号括起来,如上的'{application}'和'{cipher}',不然无法识别。

加解密功能同样适用在具体服务的配置上面,比如给上面的myapp-dev.yml中增加一个配置my.password,原始内容是test-dev,外面通过config-server的/encrypt端点生成加密字符串: curl http://localhost:7001/encrypt -d xiaojun,生成的字符串如下写在myapp-dev.yml中:

from: svn-dev-1.0my:  password: '{cipher}AQBkRH5IfW9koXtci/3tGyEG7LJgH4TGCTQ8fLBfdOB3Bq2DRZHEkW5FAtATB/OmCk+PEYgC4q3YuHsc6BP0gG/S5hVmZwCxyg4jpgD7LooVjW8hfJekYlqD/qgJvjx2z11AhLFiw7nfdwTqEXGforf0UPXIgS4bN2WVaAqFjbORqXazVOwAS8SggP32bnWyWc3Ezi1fXuqQp8UK0ej7xhwVM+S1UrcTTfJho6lPiDvPhjq7+6x3vYWUP2lQHD4tY9Q0+v8r62Oo/K2Gp7gu+LO+vC1BfDfUJZW1hhoWJ22OipAnjBgJLPJyWaK60MeovSe9ylnd2c7G39faa1pnXZUYRXdfuhxZvVKaTu1aoeWQQPmLfBp9qyHYdIZ+aQXbmFc='

然后修改一些config-client的controller:

package com.example.configclient.controller;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.cloud.context.config.annotation.RefreshScope;import org.springframework.core.env.Environment;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RefreshScope //允许动态刷新配置@RestControllerpublic class MyController {    @Value("${from}")    private String from;    @Autowired    private Environment env;    @RequestMapping("/from")    public String from() {        return this.from;    }    @RequestMapping("/from-env")    public String fromEnv() {        //也可以通过env来获取        return env.getProperty("my.password", "undefined");    }}

访问config-client的/from-env:http://localhost:7002/from-env,返回的内容是解密后的内容,由config server直接解密返回给客户端了。


有时候由于网络不稳定导致与客户端无法成功获取到配置,可以在客户端bootstrap配置中增加重试机制:

#允许失败快速响应spring.cloud.config.fail-fast=true#允许重试,需要添加spring-retry和spirng-aop依赖,下面配置的都是和默认值一样spring.cloud.config.retry.max-attempts=6spring.cloud.config.retry.initial-interval=1000spring.cloud.config.retry.multiplier=1.1spring.cloud.config.retry.max-interval=2000
添加依赖:
<dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>


我们可以试验一下,将config-server停掉,然后将config-client的logging.level.root=debug,启动client,在控制台将会看到类似重试信息

2017-09-20 18:00:22.644  INFO 15036 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at: http://peer1:7001/2017-09-20 18:00:22.664 DEBUG 15036 --- [           main] o.s.web.client.RestTemplate              : Created GET request for "http://peer1:7001/myapp/dev/trunk"2017-09-20 18:00:22.676 DEBUG 15036 --- [           main] o.s.web.client.RestTemplate              : Setting request Accept header to [application/json, application/*+json]2017-09-20 18:00:23.680 DEBUG 15036 --- [           main] o.s.r.backoff.ExponentialBackOffPolicy   : Sleeping for 10002017-09-20 18:00:24.680 DEBUG 15036 --- [           main] o.s.retry.support.RetryTemplate          : Checking for rethrow: count=12017-09-20 18:00:24.680 DEBUG 15036 --- [           main] o.s.retry.support.RetryTemplate          : Retry: count=1

当server起来之后,自动重试成功,client将成功启动。

阅读全文
0 0
原创粉丝点击