consul作为配置中心
Conusl可以作为注册中心,也可以作为配置中心。
作为配置中心,使用起来也很方便,大多数都是配置。
Consul的安装这里不说了,主要是说明java后端怎么使用,所以本次采用windows版本的consul。
引入和配置
2个文件:
pom配置:
<!-- consul配置中心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-config</artifactId>
</dependency>
<!-- 使用 @EnableConfigurationProperties 开启 @ConfigurationProperties 注解 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
bootstrap.yml
spring:
profiles:
active: dev
cloud:
consul:
host: 127.0.0.1 #注册中心的ip或host。也是集群地址,配置一个即可。注释掉整个consul这段就可以启动,即使没有注册中心也不报错。有这段就必须有一个可用的注册中心,否则启动报错
port: 8500
discovery:
enabled: true #默认true。Consul Discovery Client是否注册到注册中心。和register同时设置成false,就不需要起consul服务。
register: true #是否将服务注册到Consul集群中心.。这个参数和上面的enabled参数同时设置成false,应用才不会注册注册中心,才可以不起consul服务!
deregister: true #默认true,服务停止时注销服务,即从服务列表中删除。设置成false的话,???
#service-name: ${spring.application.name} #注册在consul上面的名字,在consul的调用中,是通过此名字调用的。默认服务名,不要改
instance-id: ${spring.application.name}-${spring.cloud.client.ip-address}:${server.port} #只供显示用,在ID列显示
health-check-interval: 5s #配置 Consul 健康检查频率,也就是心跳频率。
# health-check-critical-timeout: 2s #健康检查失败多长时间后,取消注册。在node上显示红色叉。配了这个参数,如果consul集群的server重启会注销应用!
#健康检查路径。默认是使用actuator的健康检查接口:http://localhost:8901/actuator/health返回{"status":"UP"}。其实只看http状态码是200就认为服务正常。你可以换成自己的一个rest接口替代actuator
#health-check-path: /hello
prefer-ip-address: true #表示注册时使用IP而不是hostname
retry:
initial-interval: 5000 # 服务监测时间间隔
max-attempts: 20
#consul作为配置中心,官网属性https://docs.spring.io/spring-cloud-consul/docs/2.2.4.RELEASE/reference/html/appendix.html
config:
enabled: true # 启用 consul 配置中心.默认是true
format: YAML # 配置转码方式,默认 key-value,其他可选:yaml/files/properties
data-key: data # 配置 key 值,value 对应整个配置文件。例如config/application,dev/data
prefix: config # 基础文件夹,默认值 config.
default-context: one #应用文件夹,默认值 application,consul 会加载 config/<applicationName> 和 config/<defaultContext> 两份配置,设置为相同值,则只加载一份.sets the folder name used by all applications
# profile-separator: '-' #环境分隔符,默认值 ",例如例如config/application,dev/data修改后是config/application-dev/data
watch:
enabled: true # 启用配置自动刷新
delay: 1000 # 刷新频率,单位:毫秒
servlet:
multipart:
enabled: true
max-file-size: 100MB
max-request-size: 300MB
到此为止,consul作为配置中心就可以使用了。
要注意,consul作为配置中心,一些配置项在应用启动的时候就需要加载和初始化了,所以consul的配置必须写在bootstrap.yml
我们可以将welcom.value属性写入配置文件中,但要注意,配置中心的配置优先级高于本地配置文件.
如果本地配置文件不存在,配置中心也没有,启动会报错的.
注意,需要变更配置的类上,要加@RefreshScope注解,否则不会刷新配置。
(网上有人做实验,就是没有加@RefreshScope注解,所以才说@Value不会刷新consul修改后的值,只有@ConfigurationProperties才会。原因就是没有加@RefreshScope注解)
先说结论:consul作为配置中心,既然必须配置在bootstrap.yml,那地址只能写死了。如果有多个环境怎么办?其实一个配置中心也是支持多个环境的,配置中心就是把原来使用的bootstrap.yml,application.yml,application-dev.yml,application-test.yml搬到了consul的内存中而已,并且取值的优先级比这些配置文件高,其他使用并没有区别。
那么多个环境还是跟以前一样,通过
spring:
profiles:
active: dev
指定使用哪套配置就行了,没必要每个环境搭建一套consul。
实际情况,我们确实每个环境搭建了一套consul,毕竟生产环境和开发环境是物理隔离的。
对于bootstrap.yml里我们必须要按环境分开配置的项怎么办?我们可以使用环境变量参数来修改,例如java -jar -Dspring.profiles.active=test -Dspring.cloud.consul.host=192.168.1.18
参考springboot启动时加载参数的顺序优先级:
启动时读取参数属性顺序:
Spring Boot允许您外部化配置,以便您可以在不同的环境中使用相同的应用程序代码。您可以使用属性文件,YAML文件,环境变量和命令行参数来外部化配置。属性值可以通过直接注射到你的bean @Value注释,通过Spring的访问Environment抽象,或者被 绑定到结构化对象通过@ConfigurationProperties。Spring Boot使用一种非常特殊的PropertySource顺序,旨在允许合理地覆盖值。按以下顺序考虑属性:
- Devtools 主目录上的全局设置属性(当devtools处于活动状态时,配置是~/.spring-boot-devtools.properties)。
- @TestPropertySource 你的测试注释。
- properties属性测试。可 用于测试特定应用程序片段[url=https://docs.spring.io/spring-boot/docs/2.1.2.RELEASE/api/org/springframework/boot/test/context/SpringBootTest.html]@SpringBootTest[/url]的 测试注释。
- 命令行参数。
- 来自SPRING_APPLICATION_JSON(嵌入在环境变量或系统属性中的内联JSON)的属性。
- ServletConfig init参数。
- ServletContext init参数。
- JNDI属性来自java:comp/env。
- Java系统属性(System.getProperties())。
- OS环境变量。
- 一RandomValuePropertySource,只有在拥有性能random.*。
- 特定于配置文件的应用程序属性在打包的jar(application-{profile}.properties和YAML变体)之外。
- 打包在jar中的特定于配置文件的应用程序属性(application-{profile}.properties 以及YAML变体)。
- 打包jar之外的应用程序属性(application.properties以及YAML变体)。
- 打包在jar中的应用程序属性(application.properties和YAML变体)。
- @PropertySource 你@Configuration班上的注释。
- 默认属性(由设置指定SpringApplication.setDefaultProperties
既然可以把之前的配置文件内容搬到consul里,那么,我们可以把整个配置文件的内容复制到consul中就可以了,这是一套使用方式。
还有一种使用方式,我使用consul的配置中心是为了动态修改配置,我在consul里初始时不配置任何属性值。这样应用会使用配置文件的值。当我需要修改某个值时,我在consul里去修改这个值,然后应用读取最新的值。
测试
测试配置类
写个配置项
mytest:
myname: lsy
myaddress: bejing
mysalary:
在写个查看该值的controller,用于测试应用中的值是什么。
/one/src/main/java/com/example/one/bean/MyTestBean.java
package com.example.one.bean;
import javax.annotation.PostConstruct;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 在另一个类中使用@EnableConfigurationProperties(MyTestBean.class) 就注释掉@Component
* @author lsy
*
*
*ignoreUnknownFields = false告诉Spring Boot在有属性不能匹配到声明的域的时候抛出异常。
*开发的时候很方便! prefix 用来选择哪个属性的prefix名字来绑定。
*/
//@Component
//@ConfigurationProperties(prefix = "mytest")
@ConfigurationProperties(prefix = "mytest", ignoreInvalidFields = true)
public class MyTestBean {
private String myname;
private String myaddress;
private String mysalary;//测试ignoreInvalidFields,如果没有改属性,是否报错
public String getMyname() {
return myname;
}
public void setMyname(String myname) {
this.myname = myname;
}
public String getMyaddress() {
return myaddress;
}
public void setMyaddress(String myaddress) {
this.myaddress = myaddress;
}
public String getMysalary() {
return mysalary;
}
public void setMysalary(String mysalary) {
this.mysalary = mysalary;
}
/*
* 在对象构建后打印一下.
* 经测试世纪打印:
*
* MyTestBean toPrint: [myname=lsy, myaddress=bejing, mysalary=null]
*
*/
@PostConstruct
public String toPrint() {
String str= " [myname=" + myname + ", myaddress=" + myaddress + ", mysalary=" + mysalary + "]";
System.out.println("MyTestBean toPrint:"+str);
return str;
}
}
/one/src/main/java/com/example/one/config/ConfigCenterConfig.java
package com.example.one.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import com.example.one.bean.MyTestBean;
/**
* 配置中心相关
*
* 这类测试了一个注解的使用
*
* @ConfigurationProperties
* @Component
* @EnableConfigurationProperties
* 这3个注解的关系
*
* @EnableConfigurationProperties注解的作用是:使使用 @ConfigurationProperties 注解的类生效。
* 定义一个配置类的时候,我们可以把这个类直接用@Component声明并注入spring容器,也可以不用@Component,然后再另一个@Configuration的类中
* 显示的使用@Bean注入到spring容器中。
*
* 还有另外一种方式:不使用@Component,也不在@Configuration的类中使用@Bean,
* 而是在@Configuration的类中使用注解@EnableConfigurationProperties,指定那个类。
* @EnableConfigurationProperties就是告诉spring容器需要生成这个bean。
* 请看下面的例子
*
*
*
* @author lsy
*
*/
@Configuration
@EnableConfigurationProperties(MyTestBean.class)
@ConditionalOnClass(MyTestBean.class)
public class ConfigCenterConfig {
}
/one/src/main/java/com/example/one/controller/ConfigCenterController.java
package com.example.one.controller;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.one.bean.MyTestBean;
/**
* 使用consul作为配置中心的测试
*
* 一个方法用于查看这些属性的值,比较用@Value和 @Autowired加@ConfigurationProperties 两种方式的区别
*
* @RefreshScope
* 有人做过实验,说consul作为配置中心,修改值后,@Value的对象值不变,还是之前的,@ConfigurationProperties对象的值就变了,是最新的。
* 其实是没有加@RefreshScope注解导致的。
*
*
* @author lsy
*
*/
@RestController
@RequestMapping("/config")
@RefreshScope
public class ConfigCenterController {
@Value("${mytest.myname}")
private String myname;
@Value("${mytest.myaddress}")
private String myaddress;
/**
* @Value("${mytest.mysalary:100万}")
* 冒号后跟的是默认值,要注意:不是在配置文件中这个属性为空才是用默认值,而是在属性文件中根本配有这个属性采用默认值。
* 如果你在属性文件中配置了这个属性,但是值没写东西,那么这个值也算是配置了,所以默认值不会起作用。
* 另外要注意,如果没有默认值,那么你在属性文件中必须配置这个属性,不然spring在做映射的时候找不到这个属性就报错了。
* 所以,一些对程序不重要的属性,可有可无,就配置上这个默认值。保证程序不会应为配置文件中少了这个属性导致应用无法启动。
*
* @Value这个注解有这个限制,@ConfigurationProperties没有这个限制,@ConfigurationProperties找不到对应的属性值就是null,不会报错。
*
*/
@Value("${mytest.mysalary:100万}")
// @Value("${mytest.mysalary}")
private String mysalary;
@Autowired
private MyTestBean myTestBean;
/**
*
@RefreshScope
* 有人做过实验,说consul作为配置中心,修改值后,@Value的对象值不变,还是之前的,@ConfigurationProperties对象的值就变了,是最新的。
* 其实是没有加@RefreshScope注解导致的。
* @return
*/
@GetMapping(value="/queryValue")
public String queryValue() {
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");//LocalDateTime startTime = LocalDateTime.now();
String timeNow=LocalDateTime.now().format(df);
String message="Value方式返回:myname=["+myname+"],myaddress=["+myaddress+"],mysalary=["+mysalary+"] ";
message+="<br>";
message+="对象方式返回:myTestBean="+myTestBean.toPrint();
System.out.println(message);
return message;
}
}
测试配置中心步骤
访问consul的UI控制台,点击Key/Value菜单
按照我们的配置
config:
enabled: true # 启用 consul 配置中心.默认是true
format: YAML # 配置转码方式,默认 key-value,其他可选:yaml/files/properties
data-key: data # 配置 key 值,value 对应整个配置文件。例如config/application,dev/data
prefix: config # 基础文件夹,默认值 config.
default-context: one #应用文件夹,默认值 application,consul 会加载 config/<applicationName> 和 config/<defaultContext> 两份配置,设置为相同值,则只加载一份.sets the folder name used by all applications
# profile-separator: '-' #环境分隔符,默认值 ",例如例如config/application,dev/data修改后是config/application-dev/data
watch:
enabled: true # 启用配置自动刷新
delay: 1000 # 刷新频率,单位:毫秒
最上层目录必须是config,创建目录的时候加斜杠/就可以了,如果创建文件就不要加。
最下层是文件,我们配置的是data,
中间是各个环境的配置。
比如,要把application.yml的文件内容迁移过来,我们就在config下建立one目录;
如果要把application-dev.yml的文件内容迁移过来,我们就在config下建立one,dev目录(这个目录全名就是“one,dev”)。
(逗号是默认的,你可以通过profile-separator改成其他符号)
步骤:
把config和one建立好后,再建data,这时候注意data是文件,不要加斜杠了。
里面的内容,直接把application.yml的内容全部复制进去就好,格式选择yml
这样,application.yml就迁移到配置中心了。
同理,我们在config下建立目录“one,dev”,再建立data文件,把application-dev.yml的内容全部复制进去。
到此,因为配置中心的优先级比yml文件高,其实本地的yml文件除了bootstrap.yml,其他就可以删掉了。
留着也不会报错,应用优先取配置中心的。
Yml文件中的配置项本身都是一段段的,所以我们也可以在初始的时候,consul里面的data都是空,这样应用先使用yml文件的配置。我们想在不重启应用的情况下去修改某个配置就可以把需要修改的项(也可以把整个yml文件内容写入data),写入consul的data中。这样应用自动就获取了。
访问controller查看配置项的值
修改配置中心的值
再访问:
有人做过实验,说consul作为配置中心,修改值后,@Value的对象值不变,还是之前的,@ConfigurationProperties对象的值就变了,是最新的。
其实是没有加@RefreshScope注解导致的。
总结
搭建开发测试生产环境。
在有api网关的情况下,这样搭建环境。
首先,生产环境只能是独立的一套。
生产环境使用环境变量参数指定环境
java -jar -Dspring.profiles.active=prod -Dspring.cloud.consul.host=192.168.1.18
这样bootstrap.yml中的配置对生产环境无效了。
那么开发和测试环境怎么配置?
开发和测试使用同一个consul:
(这里的开发环境指开发人员的电脑环境)
首先考虑注册中心,如果开发和测试用一个consul,那么api网关转发请求就会把两个环境搞乱。为了不乱,开发环境的注册中心配置就必须关闭。
即:
把discovery的enabled和register改成false。
spring:
profiles:
active: dev
cloud:
consul:
host: 127.0.0.1 #注册中心的ip或host。也是集群地址,配置一个即可。注释掉整个consul这段就可以启动,即使没有注册中心也不报错。有这段就必须有一个可用的注册中心,否则启动报错
port: 8500
discovery:
enabled: false #默认true。Consul Discovery Client是否注册到注册中心。和register同时设置成false,就不需要起consul服务。
register: false #是否将服务注册到Consul集群中心.。这个参数和上面的enabled参数同时设置成false,应用才不会注册注册中心,才可以不起consul服务!
deregister: true #默认true,服务停止时注销服务,即从服务列表中删除。设置成false的话,???
#service-name: ${spring.application.name} #注册在consul上面的名字,在consul的调用中,是通过此名字调用的。默认服务名,不要改
instance-id: ${spring.application.name}-${spring.cloud.client.ip-address}:${server.port} #只供显示用,在ID列显示
health-check-interval: 5s #配置 Consul 健康检查频率,也就是心跳频率。
# health-check-critical-timeout: 2s #健康检查失败多长时间后,取消注册。在node上显示红色叉。配了这个参数,如果consul集群的server重启会注销应用!
#健康检查路径。默认是使用actuator的健康检查接口:http://localhost:8901/actuator/health返回{"status":"UP"}。其实只看http状态码是200就认为服务正常。你可以换成自己的一个rest接口替代actuator
#health-check-path: /hello
prefer-ip-address: true #表示注册时使用IP而不是hostname
retry:
initial-interval: 5000 # 服务监测时间间隔
max-attempts: 20
#consul作为配置中心,官网属性https://docs.spring.io/spring-cloud-consul/docs/2.2.4.RELEASE/reference/html/appendix.html
config:
enabled: true # 启用 consul 配置中心.默认是true
format: YAML # 配置转码方式,默认 key-value,其他可选:yaml/files/properties
data-key: data # 配置 key 值,value 对应整个配置文件。例如config/application,dev/data
prefix: config # 基础文件夹,默认值 config.
default-context: one #应用文件夹,默认值 application,consul 会加载 config/<applicationName> 和 config/<defaultContext> 两份配置,设置为相同值,则只加载一份.sets the folder name used by all applications
# profile-separator: '-' #环境分隔符,默认值 ",例如例如config/application,dev/data修改后是config/application-dev/data
watch:
enabled: true # 启用配置自动刷新
delay: 1000 # 刷新频率,单位:毫秒
servlet:
multipart:
enabled: true
max-file-size: 100MB
max-request-size: 300MB
这时候开发环境就不用注册中心了,因为大家在本地写代码的时候都是单实例启动,不需要注册中心。而配置中心和测试环境是一个consul,不同的spring.profiles.active参数。
bootstrap.yml写的是dev,测试环境用命令行参数覆盖:
java -jar -Dspring.profiles.active=test -Dspring.cloud.consul.host=192.168.1.11 -Dspring.cloud.consul.discovery.enabled=true -Dspring.cloud.consul.discovery.register=true
这样开发环境不使用注册中心,但是使用测试环境的配置中心,使用配置中心的spring.profiles.active是dev。而测试环境既使用注册中心,也使用配置中心spring.profiles.active=test。
开发和测试使用不同consul:
这种情况和生产环境就一样了,每个环境搭建一套consul,互不影响。
在这种情况下,开发环境也可以关闭注册中心。同时配置中心的配置不方便每个开发人员各自改本地的配置,所以建议把配置中心的配置都删除,让开发人员使用本地yml文件更直观。
附录
Springcloud的cosnul相关文档
所有属性值在这个链接里:https://docs.spring.io/spring-cloud-consul/docs/2.2.4.RELEASE/reference/html/appendix.html