文档章节

spring cloud config 从0到1

twsghxs
 twsghxs
发布于 2017/11/08 14:08
字数 2164
阅读 1375
收藏 40

一、简介

1.为什么要配置中心?

随着系统微服务的不断增加,首要考虑的是系统的可伸缩、可扩展性好,随之就是一个配置管理的问题。各自管各自的开发时没什么问题,到了线上之后管理就会很头疼,到了要大规模更新就更烦了。 配置中心就是一个比较好的解决方案,下图就是一个配置中心的解决方案:

常见的配置中心的实现方法有:

  1. 硬编码(缺点:需要修改代码,风险大)
  2. 放在xml等配置文件中,和应用一起打包(缺点:需要重新打包和重启)
  3. 文件系统中(缺点:依赖操作系统等)
  4. 环境变量(缺点:有大量的配置需要人工设置到环境变量中,不便于管理,且依赖平台)

2.spring cloud config

特性

  1. 集中式管理分布式环境下的应用配置
  2. 配置存储默认基于 git 仓库,可进行版本管理
  3. 基于spring环境和Spring应用无缝集成
  4. 提供服务端和客户端支持(java)
  5. 基于restful接口获取配置,客户端可实现多语言
  6. 可集群部署,实现高可用
  7. 配合eureke可实现服务发现,配合cloud bus可实现配置推送更新
  8. 支持数据结构丰富,yml, json, properties

演示图

  1. 配置数据存储在Git等版本仓库中
  2. Config Server获取最新的配置数据
  3. Config Client基于restful接口获取配置

二、初级使用

1.构造config server

(1) pom.xml依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.6.RELEASE</version>
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
</dependencies>

(2) 程序入口

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

(3) 配置文件

在bootstrap.yml中添加配置:

spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: http://gitlab.**.cn/java/config-repository.git
          username: ***
          password: ***
          default-label: master
          search-paths: java*,ruby*,go*

server:
  port: 9992

search-paths表示git仓库的存放配置文件的目录

(4) 启动程序

在上面的git仓库中添加一个config-client-test.yml配置文件,配置内容如下:

env: alpha

访问http://localhost:9992/config-client/test/master 可以查看配置信息

表示config server启动成功

{
  "name": "config-client",
  "profiles": Array[1][
    "test"
  ],
  "label": "master",
  "version": "1b28f79de8c4097dc68a4be986d13232662eb036",
  "state": null,
  "propertySources": Array[1][
    {
      "name": "http://gitlab.**.cn/java/config-repository.git/java/config-client-test.yml",
      "source": {
        "env": "alpha"
      }
    }
  ]
}

URL与配置文件的映射关系如下:

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties

http://localhost:9992/master/config-client-test.yml

env: alpha

http://localhost:9992/master/config-client-test.json

{
  "env": "alpha"
}

http://localhost:9992/master/config-client-test.properties

env: alpha

2.构造config client

(1) pom.xml依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.6.RELEASE</version>
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
</dependencies>

(2) 在bootstrap.yml文件中添加相关配置

spring:
  application:
    name: config-client
  profiles:
    active: test
  cloud:
    config:
      label: master
      uri: http://localhost:9992/

(3) 使用配置

启动类

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

Controller类

@RestController
public class EnvController {

  @Value("${env}")
  private String env;

  @RequestMapping(value = "/hi")
  public String hi() {
    return env;
  }
}

(4) 启动观察日志

2017-11-08 17:56:49.730  INFO 3680 --- [  restartedMain] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at: http://localhost:9992/
2017-11-08 17:56:50.313  INFO 3680 --- [  restartedMain] c.c.c.ConfigServicePropertySourceLocator : Located environment: name=config-client, profiles=[test], label=master, version=06ef59ccd30be2089bc8c7d214b57f0497f1f3f5, state=null
2017-11-08 17:56:50.313  INFO 3680 --- [  restartedMain] b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource [name='configService', propertySources=[MapPropertySource [name='configClient'], MapPropertySource [name='http://****/config-repository.git/java/config-client-test.yml']]]

(5) 读取配置

访问http://localhost:8080/hi 直接返回

alpha

三、高级使用

1.使用Spring Security进行安全控制

(1) config-server端配置

pom加入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

bootstrap.yml文件中加入:

security:
  user:
    name: admin
    password: 123

重新启动后进入页面的时候要求输入用户名和密码

(2) config-client端配置

config-client需要在配置文件中添加验证信息:

spring:
  application:
    name: config-client
  profiles:
    active: test
  cloud:
    config:
      label: master
      uri: http://localhost:9992/
      username: admin
      password: 123

2.配置中心微服务化、集群化

原理图如下所示:

负载均衡可以用nginx,这样spring.cloud.uri配置写域名就行

这里只介绍用spring cloud服务发现组件eureka配置

(1) 准备eureka-server

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka-server</artifactId>
    </dependency>
</dependencies>

spring:
  application:
    name: eureka-server
server:
  port: 9991
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9991/eureka/
    fetch-registry: false
    register-with-eureka: false

@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {
    public static void main(String[] args) {
        SpringApplication application = new SpringApplication(EurekaServer.class);
        application.run(args);
    }
}

(2) 改造config-server

修改pom文件,添加maven依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

在程序的入口Application类加上@EnableEurekaClient或者@EnableDiscoveryClient注解

配置文件中添加注册地址,将服务注册到eureka-server中

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9991/eureka/

(3) 改造config-client

修改pom文件,添加maven依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>

在程序的入口Application类加上@EnableEurekaClient或者@EnableDiscoveryClient注解

修改配置文件,将其注册到eureka-server服务

spring:
  application:
    name: config-client
  profiles:
    active: test
  cloud:
    config:
      label: master
      username: admin
      password: 123
      discovery:
        enabled: true
        service-id: config-server
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9991/eureka/

通过service-id查找config-server服务,可部署多台config-server,实现集群化部署达到高可用

(4) 启动

访问eureka-server的服务器,可以看到config-server和config-client同时都注册在上面:

(5) 读取配置

访问http://localhost:8080/hi 直接返回,表示服务部署成功

alpha

3.刷新配置信息

(1) 单台服务配置刷新

config-client项目

修改pom文件,添加maven依赖,可以调用/refresh接口刷新配置

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

boostrap.yml添加配置,暂时去掉安全认证

management:
  security:
    enabled: false

修改配置引用的地方,添加 @RefreshScope

@RestController
@RefreshScope
public class EnvController {

  @Value("${env}")
  private String env;

  @RequestMapping(value = "/hi")
  public String hi() {
    return env;
  }
}

重新启动 访问http://localhost:8080/hi 返回

alpha

修改git仓库里config-client-test.yml文件

env: alpha123

调用refresh接口

curl -X POST http://localhost:8080/refresh

重新访问返回 alpha123表示配置刷新成功

alpha123

(2) 集群配置刷新

往往我们系统都是集群部署,一台一台调用/refresh接口维护成本太高,这里我们介绍如何使用Spring Cloud Bus实现集群配置的自动刷新,它使用轻量级的消息代理(如RabbitMQ、Kafka)连接分布式系统的节点,广播配置的变化或者其他的管理指令。

(2.1) 安装部署rabbitmq服务(这里省略)

(2.2) 改造config-server和config-client

修改pom文件,添加maven依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

修改配置文件,添加rabbitmq配置

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

(2.3) 启动服务

这里我们再启动一个8081端口的config-client服务

访问http://localhost:15672/#/ ,新增了一个Exchange,以及三个queue

因为我们启动了1个config-server、2个config-client 每个服务监听各自的队列消息

(2.4) 刷新配置

先访问http://localhost:8080/hihttp://localhost:8081/hi 返回

alpha123

修改git仓库里config-client-test.yml文件

env: alpha123123

调用config-server的/bus/refresh接口

curl -u admin:123 -X POST http://localhost:9992/bus/refresh

重新访问返回如下,表示集群配置刷新成功

alpha123123

(2.5) 局部刷新配置

调用/bus/refresh,会刷新所有使用spring cloud bus连到rabbitmq的服务,如果需要局部刷新,可通过/bus/refresh的destination参数来定位要刷新的应用程序。

例如 /bus/refresh?destination=config-client:test:8080

这里只刷新8080端口的config-client服务,另一个8081端口的config-client配置没有刷新

我们打断点发现,调用/bus/refresh的服务会向rabbitmq服务发送消息,message的body里有"destinationService":"config-client:test:8080"

因为routing key 是 # ,这样使用spring cloud bus连接rabbitmq的服务都会收到这个消息,

我们发现有个ServiceMatcher类

public class ServiceMatcher implements ApplicationContextAware {
	private ApplicationContext context;
	private PathMatcher matcher;

	@Override
	public void setApplicationContext(ApplicationContext context) throws BeansException {
		this.context = context;
	}

	public void setMatcher(PathMatcher matcher) {
		this.matcher = matcher;
	}

	public boolean isFromSelf(RemoteApplicationEvent event) {
		String originService = event.getOriginService();
		String serviceId = getServiceId();
		return this.matcher.match(originService, serviceId);
	}

	public boolean isForSelf(RemoteApplicationEvent event) {
		String destinationService = event.getDestinationService();
		return (destinationService == null || destinationService.trim().isEmpty() || this.matcher
				.match(destinationService, getServiceId()));
	}

	public String getServiceId() {
		return this.context.getId();
	}

}

重点看isForSelf方法,发现会拿destinationService跟serviceId比较

serviceId实际就是context的id,是由ContextIdApplicationContextInitializer生成的,格式是 name:profiles:index

根据代码可知 name是spring.application.name、index是server.port,如果相匹配就会刷新此服务的配置。

public class ContextIdApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {

	private static final String NAME_PATTERN = "${spring.application.name:${vcap.application.name:${spring.config.name:application}}}";
	
	private static final String INDEX_PATTERN = "${vcap.application.instance_index:${spring.application.index:${server.port:${PORT:null}}}}";

	private final String name;

	private int order = Ordered.LOWEST_PRECEDENCE - 10;

	public ContextIdApplicationContextInitializer() {
		this(NAME_PATTERN);
	}

	public ContextIdApplicationContextInitializer(String name) {
		this.name = name;
	}

	public void setOrder(int order) {
		this.order = order;
	}

	@Override
	public int getOrder() {
		return this.order;
	}

	@Override
	public void initialize(ConfigurableApplicationContext applicationContext) {
		applicationContext.setId(getApplicationId(applicationContext.getEnvironment()));
	}

	private String getApplicationId(ConfigurableEnvironment environment) {
		String name = environment.resolvePlaceholders(this.name);
		String index = environment.resolvePlaceholders(INDEX_PATTERN);
		String profiles = StringUtils
				.arrayToCommaDelimitedString(environment.getActiveProfiles());
		if (StringUtils.hasText(profiles)) {
			name = name + ":" + profiles;
		}
		if (!"null".equals(index)) {
			name = name + ":" + index;
		}
		return name;
	}
}

四、参考链接

http://tech.lede.com/2017/06/12/rd/server/springCloudConfig/

http://www.cnblogs.com/ityouknow/p/6931958.html

© 著作权归作者所有

共有 人打赏支持
twsghxs
粉丝 3
博文 1
码字总数 2164
作品 0
程序员
加载中

评论(2)

twsghxs
twsghxs

引用来自“uemc”的评论

文章
(2.3) 启动服务
这里我们再启动一个8081端口的config-client服务

访问http://localhost:15672/#/ ,新增了一个Exchange,以及三个queue


端口不是 5672 吗
web管理页面端口是15672,client端通信端口是5672
uemc
uemc
文章
(2.3) 启动服务
这里我们再启动一个8081端口的config-client服务

访问http://localhost:15672/#/ ,新增了一个Exchange,以及三个queue


端口不是 5672 吗
白话SpringCloud | 第八章:分布式配置中心的服务化及动态刷新

前言 上一章节,简单介绍了分布式配置中心的使用。同时,我们也遗漏了一些问题,比如如何配置实时生效,当服务端地址变更或者集群部署时,如何指定服务端地址?回想,在服务注册章节,服务提...

oKong
10/11
0
0
白话SpringCloud | 第七章:分布式配置中心的使用

前言 介绍完服务的容错保护处理,接下来我们来了解下关于分布式配置中心的相关知识和使用。众所周知,随着项目的越来越多,日益庞大,每个子项目都会伴随着不同的配置项,于此也就多了很多的...

oKong
10/10
0
0
聊聊spring cloud gateway的GatewayFilter

序 本文主要研究一下spring cloud gateway的GatewayFilter GatewayFilter spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/filter/GatewayFilter.jav......

go4it
06/09
0
0
Spring Cloud(二):Spring Cloud Config

因为涉及到多个子工程,这种情况比较适合gradle担当构建工具。 配置build.gradle setting.gradle 创建子工程 然后在在根项目创建子项目目录cloud 以及类路径目录 mkdir p src/main/{java,res...

神易风
08/08
0
0
聊聊ribbon的超时时间设置

序 本文主要研究一下ribbon的超时时间设置 配置 实例 RibbonClientConfiguration spring-cloud-netflix-ribbon-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/Ribb......

go4it
07/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

《Netkiller Java 手札》· 二进制文件操作大全

本文节选自《Netkiller Java 手札》 Netkiller Java 手札 Mr. Neo Chan, 陈景峯(BG7NYT) 中国广东省深圳市望海路半岛城邦三期 518067 +86 13113668890 <netkiller@msn.com> $Id: book.xml 6......

netkiller-
17分钟前
1
0
Fiddler Debugger post请求

常用的两种: 第一种默认的 对应URL为www 的要用请求头为:Content-Type: application/x-www-form-urlencoded 请求参数为 :param1=1234¶m2=12345 注:有些接口是指定用这种的第二方式并不...

轻量级赤影
25分钟前
2
0
如何搭建母婴亲子类知识社区

近期社交领域融资动作频繁,海尔高管、海尔医疗有限公司总裁管礼庆创办的母婴知识分享社区平台Alwayslove于上月获得700万天使轮融资。 Alwayslove是一个母婴知识分享社区平台,采用UGC模式,...

ThinkSNS账号
27分钟前
1
0
Android 自定义构建类型 BuildType

最近接触到自定义构建类型 BuildType,发现这一块有些地方稍不注意的话会被绕进去浪费点时间,既然我这边已经花费时间了,如果正好你也需要接触到 BuildType,也许接下来分享的 tips 可能会帮...

猴亮屏
28分钟前
1
0
美团点评基于 Flink 的实时数仓建设实践

引言 近些年,企业对数据服务实时化服务的需求日益增多。本文整理了常见实时数据组件的性能特点和适用场景,介绍了美团如何通过 Flink 引擎构建实时数据仓库,从而提供高效、稳健的实时数据服...

美团技术团队
31分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部