spring cloud config 从0到1
博客专区 > twsghxs 的博客 > 博客详情
spring cloud config 从0到1
twsghxs 发表于2周前
spring cloud config 从0到1
  • 发表于 2周前
  • 阅读 1114
  • 收藏 37
  • 点赞 4
  • 评论 2
摘要: spring cloud 系列

一、简介

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

标签: Spring Cloud
共有 人打赏支持
粉丝 4
博文 1
码字总数 2164
评论 (2)
uemc
文章
(2.3) 启动服务
这里我们再启动一个8081端口的config-client服务

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


端口不是 5672 吗
twsghxs

引用来自“uemc”的评论

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

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


端口不是 5672 吗
web管理页面端口是15672,client端通信端口是5672
×
twsghxs
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: