Spring Boot profile
在Spring Boot应用中,我们可以将配置内容写入application.yml,设置多个profile,也可以用多个application-{profile}.properties文件配置,并在启动时指定–spring.profiles.active={profile}来加载不同环境下的配置。
在Spring Cloud微服务架构中,这种方式未必适用,微服务架构对配置管理有着更高的要求,如:
- 集中管理:成百上千(可能没这么多)个微服务需要集中管理配置,否则维护困难、容易出错;
- 运行期动态调整:某些参数需要在应用运行时动态调整(如连接池大小、熔断阈值等),并且调整时不停止服务;
- 自动更新配置:微服务能够在配置发生变化是自动更新配置。
以上这些要求,传统方式是无法实现的,所以有必要借助一个通用的配置管理机制,通常使用配置服务器来管理配置。
Spring Cloud Config
Spring Cloud Config 为分布式系统的外部配置提供了服务端和客户端的支持方案。在配置的服务端您可以在所有环境中为应用程序管理外部属性的中心位置。客户端和服务端概念上的Spring Environment 和 PropertySource 抽象保持同步, 它们非常适合Spring应用程序,但是可以与任何语言中运行的应用程序一起使用。当应用程序在部署管道中从一个开发到测试直至进入生产时,您可以管理这些环境之间的配置,并确保应用程序在迁移时具有它们需要运行的所有内容。服务器存储后端的默认实现使用git,因此它很容易支持标记版本的配置环境,并且能够被管理内容的各种工具访问。很容易添加替代的实现,并用Spring配置将它们插入。
Spring Cloud Config 包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。Spring cloud使用git或svn存放配置文件,默认情况下使用git,我们先以git为例做一套示例。
Spring Cloud Config同样分为server端和client端,一般将配置都放在Git上,这样版本管理比较方便。
Spring Cloud Config Server
新建项目config-server
pom.xml文件内容如下:
<?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>cn.ac.iie</groupId>
<artifactId>config</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.4.RELEASE</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.1.4.RELEASE</version>
<configuration>
<mainClass>cn.ac.iie.App</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>
添加App.java,内容如下:
@SpringBootApplication
@EnableEurekaClient
@EnableConfigServer
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
修改application.properties
,内容如下:
spring.application.name=config
eureka.client.service-url.defaultZone=http://192.168.171.45:8090/eureka/
spring.cloud.config.server.git.uri=http://swarm-worker1/duandingyang/config-repo.git
spring.cloud.config.server.git.username=duandingyang
spring.cloud.config.server.git.password=12345678
server.port=8085
在gitlab上新建一个项目config-repo
,然后在新建一个文件elasticsearch.properties
,文件内容跟之前的elasticsearch项目中的配置文件内容一样,如下所示:
management.endpoints.web.exposure.include=health,info,env,metrics
spring.elasticsearch.rest.uris=swarm-manager:9200
management.endpoint.health.show-details=always
logging.level.root=INFO
logging.file=user.log
es.ips[0]=swarm-manager
es.port=9300
es.clusterName=my-application
eureka.client.service-url.defaultZone=http://192.168.171.45:8090/eureka/
spring.application.name=es-service
server.port=8082
启动项目,在Eureka中可以看到Config服务已经成功启动
然后我们访问http://localhost:8085/elasticsearch-a.properties
:
如果访问:http://localhost:8085/elasticsearch-b.properties
也可以访问:
可以看到正确输出了我们在gitlab上的配置文件。
为什么要在文件名后加一个a
?,如果不写就会报错
如果在浏览器中输入:http://localhost:8085/elasticsearch-a.yml
:
自动会对文件格式进行转换。此外还可以转换json:
浏览器地址栏中的命名格式写法
- /{name}-{profiles}.yml:
- /{label}/{name}-{profiles}.yml
name表示文件名(或者服务名),profiles表示环境,label表示分支(如果不写,默认是master分支)
/{name}-{profiles}.yml方式
在gitlab上面新建一个文件elasticsearch-test.properties
,内容如下:
management.endpoints.web.exposure.include=health,info,env,metrics
spring.elasticsearch.rest.uris=swarm-manager:9200
management.endpoint.health.show-details=always
logging.level.root=INFO
logging.file=user.log
es.ips[0]=swarm-manager
es.port=9300
es.clusterName=my-application
eureka.client.service-url.defaultZone=http://192.168.171.45:8090/eureka/
spring.application.name=es-service
env=test
跟之前的内容是一样的,加了一行env=test
,然后在浏览器中输入:http://localhost:8085/elasticsearch-test.json
,同样可以访问配置文件:
/{label}/{name}-{profiles}.yml
新建分支release
release分支会从master中拷贝一份,修改elasticsearch-test.json,添加内容:label=release
然后通过访问http://localhost:8085/release/elasticsearch-test.json
,可以得到release分支下的配置文件
可以看到多了一个label=release
spring-cloud config会从远端的git拉取到本地的git
在控制台可以查看本地的git路径:
可以修改配置:spring.cloud.config.server.git.basedir=E:/git/neimeng/config/basedir
来修改git的路径.
Spring-config client
上面介绍了config server的使用,现在介绍client的使用。首先修改elasticsearch服务的依赖,添加spring cloud config依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
替换application.properties文件:
server.port=8082
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=config
spring.cloud.config.profile=test
spring.application.name=elasticsearch
如果仅仅是这样启动的话,会报错。理由是不知道这个配置文件中的配饰项的先后顺序。因此需要先加载这个配置文件中的内容。
因此需要修改application.properties为bootstrap.properties,此外如果eureka的端口不是默认的8761,程序启动时默认从localhost:8761获取eureka中的service-id,当修改端口后,获取不到service-id,因此会从默认的localhost:8888获取,便会报错找不到service-id。因此需要将git上面的配置eureka.client.service-url.defaultZone=http://192.168.171.45:8090/eureka/
拿出来,写到bootstrap.properties中。这样就会首先读取eureka,然后在eureka中找服务。因为我们的配置文件中有方括号,程序无法识别,需要将这个配置放在bootstrap.properties中。
bootstrap.properties内容如下:
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.service-id=config
spring.cloud.config.profile=test
eureka.client.service-url.defaultZone=http://192.168.171.45:8090/eureka/
es.ips[0]=swarm-manager
application.properties内容如下:
server.port=8082
spring.application.name=elasticsearch
远端git的elasticsearch-test.properties配置文件内容如下:
management.endpoints.web.exposure.include=health,info,env,metrics
spring.elasticsearch.rest.uris=swarm-manager:9200
management.endpoint.health.show-details=always
logging.level.root=INFO
logging.file=user.log
es.port=9300
es.clusterName=my-application
eureka.client.service-url.defaultZone=http://192.168.171.45:8090/eureka/
spring.application.name=es-service
server.port=8082
这样就配置成功了。
动态刷新配置SpringCloud Config Bus
当我们修改git上的配置时,我们的项目中的配置并没有修改。
我们需要在config server端添加SpringCloud Config Bus依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
然后在application.properties中添加:
spring.rabbitmq.host=swarm-manager
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
在RabbitMQ中就会出现下面的队列:
在config client中引入config bus依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
然后在application.properties中添加:
spring.rabbitmq.host=swarm-manager
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
启动client,查看RabbitMQ管理页面:
可以看到多了一个队列,说明注册成功。
然后需要在config server中暴露api,修改application.properties:
management.endpoints.web.exposure.include=*
启动服务,然后发送请求:
查看RabbitMQ:
应用
在git上面elasticsearch-test.properties中添加内容:
girl.name=Jenny
girl.age=18
在elasticsearch项目中新建GirlConfig.java,内容如下:
package cn.ac.iie.bean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("girl")
@RefreshScope
public class GirlConfig {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
然后在新建GirlController.java,内容如下:
@RestController
public class GirlController {
@Autowired
private GirlConfig girlConfig;
@GetMapping("/girl/print")
public String print() {
return "" + girlConfig.getName() + "," + girlConfig.getAge();
}
}
这样访问http://localhost:8082/girl/print
当我们修改配置文件之后:
girl.name=Jenny
girl.age=30
首先发送请求:
再次发送请求:
就可以看到更新后的配置了。
每次我们修改配置之后,都需要手动刷新http://localhost:8085/actuator/bus-refresh
,如何避免这种方式?
webhooks
使用webhooks可以实现此功能。