03.SpringCloud Config_Server

一、分布式Config

1.分布式配置架构

传统架构

 

可以看到,在传统架构中,每个配置硬编码到应用中,写在properties或者xml里,跟随这项目一起保存到git或者svn仓库中。当配置越来越多的时候,比如MQ,数据库,Redis,ES等等的配置,只能一个文件一个文件的累加,并且每当修改时都需要重新打包部署,浪费时间且冗余。

缺点:

  • 硬编码

  • 写在properties,集群环境下需要替换和重启

  • 写在xml中,和应用一起打包,替换需要重新打包部署

SpringCloud Config

 

由于常用的项目+配置的方式有很大缺点,SpringCloud提供了一个配置中心来统一管理配置,它分为服务端和客户端。解决了配置中心化,版本控制,硬编码等问题

服务端即分布式配置中心,为一个单独的微服务应用,用于连接客户端与仓库,为客户端提供相应的配置信息等。服务器默认采用git来存储配置信息,这样有助于对环境配置进行版本管理,并且可以通过git客户端工具来方便的管理和访问配置内容。

客户端是指通过指定的配置中心来管理应用资源,以及与业务相关的配置内容,并在启动的时候从配置中心获取和加载配置信息。

2.EnvironmentRepository仓储

这里我们先介绍一下SpringCloudConfig的仓储,对于配置服务器管理多个配置,客户端获取时需要一定的规则来获取,SpringCloudConfigServer提供了EnvironmentRepository接口供客户端获取,其具体规则为:

  • {application}:配置客户端应用名称

  • {profile}:客户端使用的配置文件环境,比如开发环境或线上环境

  • {label}:版本信息,比如git的分支master or dev

服务端配置映射主要有五种:

  • /{application}/{profile}[/{label}]

  • /{application}-{profile}.yml

  • /{label}/{application}-{profile}.yml

  • /{application}-{profile}.properties

  • /{label}/{application}-{profile}.properties

3.搭建Spring Cloud Conifg Server

关于SpringCloud Config Server的搭建非常简单,最主要是用到@EnableConfigServer这个注解

首先需要基于SpringBoot创建一个SpringCloud应用,pom文件额外需要添加:

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-config-server</artifactId>
</dependency>

基于本地Git仓库

1.需要在启动器类上加入@EnableConfigServer注解

2.在application.properties中添加配置信息,如下:主要是配置本地git路径

## 配置服务器应用名称
spring.application.name = spring-cloud-config-server
​
## 配置服务器端口
server.port = 9090
​
## 关闭管理端actuator 的安全
## /env /health 端口完全开放
management.security.enabled = false
​
# 配置本地git路径
spring.cloud.config.server.git.uri=${user.dir}/src/main/resources/configs

Tips:${user.dir}为此项目在电脑上的绝对路径,比如windows系统下(D:\ideaProject\SpringCloud...),如果在linux系统下即(home/...),使用这个做路径可以很好的适配各个平台的文件路径

3.对应本地git路径创建文件夹及文件

 

这里我们创建三个配置文件

  • enbuys:默认的配置

    • name=enbuys

  • enbuys-dev:开发环境时的配置

    • name=enbuys-dev

  • enbuys-prod:线上生产环境配置

    • name=enbuys-prod

4.在configs目录下进行git初始化

> git init
> git add .
> git commit -m "first commit"

5.启动测试

访问http://localhost:9090/enbuys/test

 

可以发现,如果输入一个没有的配置文件,其会自动使用默认的配置文件

再访问下http://localhost:9090/enbuys/dev

 

可以发现会把enbuys-dev与默认配置文件都输出出来

我们这里使用的映射即为/{label}/{application}-{profile}.properties

基于远程Git仓库

即把我们的配置放到github上面,方法也很简单,和本地仓库类似

1.添加注解@EnableConfigServer

2.在github上创建一个Repository

 

3.根据github提示,找个文件夹绑定远程仓库

 

4.将三个配置文件放到init的那个文件夹,上传到远程git仓库

> git add .
> git commit -m 'add config server'
> git push -u origin master

5.修改项目中的git.url路径

# 远程git仓库
spring.cloud.config.server.git.uri=https://github.com/PAcee1/temp

6.测试

 

可以看到正确输出远程仓库上的信息

Tips:spring.cloud.config.server.git.force-pull,因为git拉取时有时会走本地缓存,如果配置这个可以保证强制拉取远程仓库上的配置

不同方式访问

还记得我们在上面介绍EnvironmentRepository仓储时,对资源的映射方式有好几种,我们来测试一下:

/{application}/{profile}[/{label}]

这种就是我们上面使用的,对于请求比较模糊时使用,会输出很多信息

/{application}-{profile}.properties

 

使用这种方式,可以具体的显示出配置文件中的信息

/{label}/{application}-{profile}.properties

 

 

也是一样输出详细信息

4.搭建Spring Cloud Config Client

客户端加入的pom依赖和server不同,为:

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

搭建客户端获取服务器中的配置也非常简单

1.配置application.properties

## 配置客户端应用名称
spring.application.name = spring-cloud-config-client
​
## 配置客户端应用服务端口
server.port = 8080
​
## 关闭管理端actuator 的安全
## /env /health 端口完全开放
management.security.enabled = false

还是基本的一些配置信息

2.配置bootstrap.properties

## 配置客户端应用关联的应用
## spring.cloud.config.name 是可选的
## 如果没有配置,采用 ${spring.application.name}
spring.cloud.config.name = enbuys
## 关联 profile
spring.cloud.config.profile = prod
## 关联 label
spring.cloud.config.label = master
## 配置配置服务器URI
spring.cloud.config.uri = http://127.0.0.1:9090/

Tips:这里的cloud的config配置也可以写在application.properties,这里写在bootstrap的配置文件中主要还是一个规范问题,因为SpringCloud使用Bootstrap做上下文,对于关于SpringCloud的配置也应该写在bootstrap.properties

3.启动测试

 

首先可以在控制台看到SpringCloud加载时去请求服务器获取配置信息

然后在在浏览器上查看

 

可以看到也正确加载到Environment中了

5.动态配置Bean

动态配置Bean在之前学springBoot的时候已经做过,无非是在Bean上加一个@ConfigurationProperties注解

这里我们要说的是,从SpringCloud Config Server拉取Bean的信息进行注入,

客户端配置文件注入

Bean:

@ConfigurationProperties(prefix = "enbuy.user")
public class User {
​
    private Integer id;
​
    private String name;
​
    public Integer getId() {
        return id;
    }
​
    public void setId(Integer id) {
        this.id = id;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

application.properties:

#配置user信息
enbuy.user.id=1
enbuy.user.name=pacee

UserController:

@RestController
@EnableConfigurationProperties(User.class)
public class UserController {
​
    //通过构造器注入
    private final User user;
​
    @Autowired
    public UserController(User user) {
        this.user = user;
    }
​
    @GetMapping("/user")
    public User user() {
        return user;
    }
}

访问http://localhost:8080/user

 

正确显示出来

服务器配置文件注入

其次,我们将配置放到github上的配置文件上,然后再进行拉取远程配置注入

 

再次访问http://localhost:8080/user

 

可以发现还是原来的信息,这是为什么呢?

因为这里使用的还是原来的远程配置,没有动态的刷新配置,这里我们先对客户端环境进行refresh

 

可以看到,刷新了三个信息,版本以及我们修改的Bean信息。

再次请求

 

可以发现正确修改成远程配置服务器上的信息了。这里有两个问题需要注意的:!

==1.拉取远程服务器上的配置,比客户端中application或bootstrap的配置优先级高==

==2.SpringCloud当配置服务器修改时,不会自动刷新客户端拉取新的配置==

自动拉取服务器配置

这里我们就应该对第二个问题展开探究,这里实现的方式有很多种

  • SpringCloud提供的SpringCloud Bus

  • 使用定时器配合ContextRefresh进行刷新

这里我们先使用ContextRefresh,因为这种方式适配各种情景,比如我们不使用http请求即Git当配置服务器,而是zookeeper实现配置服务器,这种方式就可以完全适应,不用修改任何东西。

Java代码实现:

@SpringBootApplication
public class SpringCloudLesson3ConfigClientApplication {
​
    @Autowired
    private ContextRefresher contextRefresher;
​
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudLesson3ConfigClientApplication.class, args);
    }
​
    @Scheduled(fixedRate = 1000L)
    public void update() {
​
        Set<String> keys = contextRefresher.refresh();
​
        if (!keys.isEmpty()) {
            System.out.println("本次更新的配置项: " + keys);
        }
​
    }
}

可以看到,这里我们使用1秒刷新一次的策略,代码也是比较简单。

 

可以看到,正确刷新了我们服务器上修改的配置

 

请求user,输出信息也是我们期望的。

这样我们就简单实现了自动拉取配置服务器上修改的内容了

 

二、Health EndPoint

之前介绍过几个传动器端点,前面一直在使用env这个端点,现在说下health端点。

这个端点可以查看服务的健康程度,就包含我们刚刚配置的Config Server

因为我们上面已经配了全局的端点敏感性为false,所以可以直接查看

 

可以看到默认包含四个PropertySource

  • status:总体健康程度

  • diskSpace:磁盘健康

  • refreshScope:刷新的健康

  • configServer:配置服务器的健康程度

自定义健康组件

我们先观察下Health的源码:

@ConfigurationProperties(prefix = "endpoints.health")
public class HealthEndpoint extends AbstractEndpoint<Health> {
​
   private final HealthIndicator healthIndicator;
​
   /**
    * Time to live for cached result, in milliseconds.
    */
   private long timeToLive = 1000;
​
   /**
    * Create a new {@link HealthEndpoint} instance.
    * @param healthAggregator the health aggregator
    * @param healthIndicators the health indicators
    */
   public HealthEndpoint(HealthAggregator healthAggregator,
         Map<String, HealthIndicator> healthIndicators) {
      super("health", false);
      Assert.notNull(healthAggregator, "HealthAggregator must not be null");
      Assert.notNull(healthIndicators, "HealthIndicators must not be null");
      CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(
            healthAggregator);
      for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
         healthIndicator.addHealthIndicator(getKey(entry.getKey()), entry.getValue());
      }
      this.healthIndicator = healthIndicator;
   }

可以发现,在构造函数中,HealthEndpoint循环HealthIndicator加入到容器中,看到这个就想起在SpringBoot学习时,那些自动配置类也是这样循环加入容器的,我们便可以仿照SpringBoot添加组件的方式,创建一个类实现HealthIndicator,然后添加到容器中。

1.创建一个类实现`HealthIndicator`

public class HealthCustomer implements HealthIndicator {
    
    @Override
    public Health health() {
        return null;
    }
}

可以看到这里需要返回一个Health对象,再进到Health里查看

public final class Health {
​
   private final Status status;
​
   private final Map<String, Object> details;
​
   /**
    * Create a new {@link Health} instance with the specified status and details.
    * @param builder the Builder to use
    */
   private Health(Builder builder) {
      Assert.notNull(builder, "Builder must not be null");
      this.status = builder.status;
      this.details = Collections.unmodifiableMap(builder.details);
   }

可以看到Health是由Builder创建的,那意思是我们也需要创建一个Builder来初始化Health,再看下Builder

public static class Builder {
​
   private Status status;
​
   private Map<String, Object> details;
​
   /**
    * Create new Builder instance.
    */
   public Builder() {
      this.status = Status.UNKNOWN;
      this.details = new LinkedHashMap<String, Object>();
   }

Builder的创建就很简单了,传递status状态已经具体的detailsMap信息即可

2.在我们的类中创建Builder并初始化Health返回

@Component
public class HealthCustomer implements HealthIndicator {
​
    @Override
    public Health health() {
        Health.Builder builder = new Health.Builder();
        builder.status(Status.UP);
        builder.withDetail("name","MyHealthCustomer");
        builder.withDetail("time",System.currentTimeMillis());
        Health health = builder.build();
        return health;
    }
}

这样就创建完毕我们自定义的Health组件了,记得要加上@Component注解

3.浏览器查看

 

正确加载进HealthEndpoint

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值