本文记录Nacos做为配置中心,服务器端的设置与客户端几种的访问方式。 实验用的环境如下: Nacos Server集群: 192.168.20.30:8848 192.168.20.31:8848 192.168.20.32:8848
Nacos地址服务器: 192.168.20.33:8080
客户端: 192.168.20.100
为演示方便,我们假设Nacos Client上将使用如下的配置项 user-dev.properties: user.type=user.dev user.name=user_dev user.birthday=2011-12-12 user.age=10
user-test.properties: user.type=user.test user.name=user_test user.birthday=2011-12-13 user.age=11
manager-dev.properties: manager.type=manager.dev user.name=manager_dev user.birthday=2011-12-14 user.age=12
manager-test.properties: manager.type=manager.test user.name=manager_test user.birthday=2011-12-15 user.age=13
一、在Nacos Service上创建配置 1、创建namespace namespace按"用户id:groupid:版本"设置为:"01:com.hhao.erp:01",如图:
2、分别创建以下Data Id:
DataId:user-dev.properties Group:erp-user 配置内容: user.type=user.dev user.name=user_dev user.birthday=2011-12-12 user.age=10
DataId:user-test.properties Group:erp-user 配置内容: user.type=user.test user.name=user_test user.birthday=2011-12-13 user.age=11
DataId:manager-dev.properties Group:erp-manager 配置内容: manager.type=manager.dev user.name=manager_dev user.birthday=2011-12-14 user.age=12
DataId:manager-test.properties Group:erp-manager 配置内容: manager.type=manager.test user.name=manager_test user.birthday=2011-12-15 user.age=13
二、创建Nacos Client nacos client的Pom文件如下: <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.0.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.hhao.nacos</groupId> <artifactId>nacos-client</artifactId> <version>0.0.1-SNAPSHOT</version> <name>nacos-client</name> <description>Demo project for Spring Boot</description>
<properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.RC1</spring-cloud.version> </properties>
<dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.0.BUILD-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies>
</dependencyManagement>
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
<repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> </project> 不得不说的是:在项目中,Spring Cloud用到了Hoxton.RC1版本,但是Spring官网上最新的spring-cloud-alibaba-dependencies不支持该版本; 以下是此时Spring官网上最新的spring-cloud-alibaba-dependencies版本 <dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> 所以从阿里spring-cloud-alibaba下载了最新的spring-cloud-alibaba-dependencies https://github.com/alibaba/spring-cloud-alibaba 版本如下: <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2.2.0.BUILD-SNAPSHOT</version> <type>pom</type> <scope>import</scope> </dependency> 可以看到,还只是SNAPSHOT,实际上它也无法支持Spring Cloud Hoxton.RC1版本,所以,没有办法,下载了源码,修改了Maven依赖项,重新编绎生成2.2.0.BUILD-SNAPSHOT,让其支持到Spring Cloud Hoxton.RC1版本。所以,如果按上述POM设置运行不起来,可以降低Spring Cloud版本,但是具体除到哪个版本,没有试过。 真希望阿里的更新能够更加及时些。
三、客户端访问、监听配置中心的user.properties 客户端application.properties设置如下: server.port=8080 #启用的配置文件 spring.profiles.active=dev #激活所有的端点的web方式请求 management.endpoints.web.exposure.include=*
客户端添加bootstrap.properties,并设置如下: #接入点,地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址 spring.cloud.nacos.config.endpoint=192.168.20.33:8080 #配置中心的命名空间 spring.cloud.nacos.config.namespace=390a21f0-1826-4109-bfa6-c963fb170d59 #Group spring.cloud.nacos.config.group=erp-user #DataId前缀,默认等于spring.application.name spring.cloud.nacos.config.prefix=user #dataID后缀及内容文件格式,dataId的后缀,同时也是配置内容的文件格式,支持 properties,yml\yaml spring.cloud.nacos.config.file-extension=properties #配置内容的编码方式 spring.cloud.nacos.config.encode=UTF-8 #获取配置的超时时间,单位为ms spring.cloud.nacos.config.timeout=3000
为什么配置要放在bootstrap.properties或bootstrap.yml里呢?因为在Spring Boot中,bootstrap.properties(或bootstrap.yml)会优先于application.properties(或application.yml)加载,因此,在后续的配置中可以使用到它
创建UserEndpoint类:
/** * @author Wan * @date 2019/10/31 9:59 */ @RefreshScope @RestController public class UserEndpoint { @Value("${user.type}") String type;
@Value("${user.name}") String name;
@Value("${user.age}") Integer age;
@Value("${user.birthday}") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate birthday;
@Autowired private NacosConfigManager nacosConfigManager;
@GetMapping("/info") public String info(){ return type + name + age + birthday + nacosConfigManager.getConfigService().getServerStatus(); } }
创建监听类NacosUserListener: @Component public class NacosUserListener implements ApplicationRunner { @Value("${user.type}") String type;
@Value("${user.name}") String name;
@Value("${user.age}") Integer age;
@Value("${user.birthday}") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate birthday;
@Autowired private NacosConfigManager nacosConfigManager;
@Override public void run(ApplicationArguments args) throws Exception { System.out.println(String.format("Initial type=%s,name=%s,age=%d,birthday=%s",type, name, age,birthday.format(DateTimeFormatter.BASIC_ISO_DATE)));
//注意,直接用dataID的都不支持自动配置文件项的匹配 nacosConfigManager.getConfigService().addListener( "user-dev.properties", "erp-user", new Listener() {
/** * Callback with latest config data. * * For example, config data in Nacos is: * * user.name=Nacos user.age=25 * @param configInfo latest config data for specific dataId in Nacos * server */ @Override public void receiveConfigInfo(String configInfo) { Properties properties = new Properties(); try { properties.load(new StringReader(configInfo)); } catch (IOException e) { e.printStackTrace(); } System.out.println("config changed: " + properties); }
@Override public Executor getExecutor() { return null; } }); } }
启动类NacosClientApplication: @SpringBootApplication public class NacosClientApplication { public static void main(String[] args) { SpringApplication.run(NacosClientApplication.class, args); } } 启动NacosClientApplication。 访问http://192.168.20.100:8080/info看输出,也可以看Console中看输出。 可以在Nacos Server端变更属性值,在客户端可以看到值的变更响应; 可以在Nacos 客户端的application.propertis中修改源活的配置文件为spring.profiles.active=test,查看加载属性的不同;
四、客户端同时访问user.properties和manager.properties 修改bootstrap.properties,设置如下:
#接入点,地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址 spring.cloud.nacos.config.endpoint=192.168.20.33:8080 #配置中心的命名空间 spring.cloud.nacos.config.namespace=390a21f0-1826-4109-bfa6-c963fb170d59 #user.properties加载,注意,此种方式不支持配置文件组合成data-id,须要自己处理成user-dev.properties或user-test.properties,nacos的不足 spring.cloud.nacos.config.ext-config[0].data-id=user-dev.properties spring.cloud.nacos.config.ext-config[0].group=erp-user spring.cloud.nacos.config.ext-config[0].refresh=true #manager.properties加载,注意,此种方式不支持配置文件组合成data-id,须要自己处理成manager-dev.properties或manager-test.properties,nacos的不足 spring.cloud.nacos.config.ext-config[1].data-id=manager-dev.properties spring.cloud.nacos.config.ext-config[1].group=erp-manager spring.cloud.nacos.config.ext-config[1].refresh=true
#配置内容的编码方式 spring.cloud.nacos.config.encode=UTF-8 #获取配置的超时时间,单位为ms spring.cloud.nacos.config.timeout=3000
修改NacosUserListener类,查看是否两个配置都加载了,以及加载后如果属性重复,则nacos的处理方法。 @Component public class NacosUserListener implements ApplicationRunner { @Value("${manager.type}") String managerType;
@Value("${user.type}") String type;
@Value("${user.name}") String name;
@Value("${user.age}") Integer age;
@Value("${user.birthday}") @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate birthday;
@Autowired private NacosConfigManager nacosConfigManager;
@Override public void run(ApplicationArguments args) throws Exception { System.out.println(String.format("Initial type=%s %s,name=%s,age=%d,birthday=%s",type,managerType ,name, age,birthday.format(DateTimeFormatter.BASIC_ISO_DATE)));
nacosConfigManager.getConfigService().addListener( "user.properties", "erp-user", new Listener() {
/** * Callback with latest config data. * * For example, config data in Nacos is: * * user.name=Nacos user.age=25 * @param configInfo latest config data for specific dataId in Nacos * server */ @Override public void receiveConfigInfo(String configInfo) { Properties properties = new Properties(); try { properties.load(new StringReader(configInfo)); } catch (IOException e) { e.printStackTrace(); } System.out.println("config changed: " + properties); }
@Override public Executor getExecutor() { return null; } }); } } 运行NacosClientApplication,输出:
可以看到,两个项都加载,但合并后相同的属性,后面的会覆盖前面的。
五、客户端同时访问user.properties和manager.properties另一种方式 修改bootstrap.properties,设置如下: #接入点,地域的某个服务的入口域名,通过此域名可以动态地拿到服务端地址 spring.cloud.nacos.config.endpoint=192.168.20.33:8080
spring.cloud.nacos.config.shared-data-ids=user-dev.properties,manager-dev.properties spring.cloud.nacos.config.refreshable-dataids=user-dev.properties,manager-dev.properties
#配置内容的编码方式 spring.cloud.nacos.config.encode=UTF-8 #获取配置的超时时间,单位为ms spring.cloud.nacos.config.timeout=3000
用shared-data-ids和refreshable-dataids配置共享的多个配置项(refreshable-dataids控制刷新),不过,这种方式只支持默认的public命名空间、DEFAULT_GROUP的Group,所以Nacos Server端要在public命名空间、DEFAULT_GROUP组下创建user-dev.properties,manager-dev.properties配置项。 这种局限还挺大的,nacos代码这部份没有完善。
终上所述,目前比较靠谱的加载方法即第二种: spring.cloud.nacos.config.ext-config[1].data-id=manager-dev.properties spring.cloud.nacos.config.ext-config[1].group=erp-manager spring.cloud.nacos.config.ext-config[1].refresh=true 不过,这种也有缺限,不像第一种那样,能够自动识别配置文件。不过,一般情况下,配置环境的分隔可以话到Nacos服务器地址那去做,这里也就无所谓配置文件了。 所以,尽量不要在data id中用到配置文件的标识。
六、关于@RefreshScope 在这个范围内的所有bean 仅在首次访问时初始化,所以这个范围强制懒加载。这个注解会给范围内的每个bean创建个代理对象. 如果刷新bean,则下次访问bean时(即执行方法)将创建一个新实例。所有生命周期方法都应用于bean实例,因此在刷新时会调用在bean工厂的销毁回调方法,然后在创建新实例时正常调用初始化回调。从原始bean定义创建新的bean实例,因此在创建时会重新评估任何外化内容(属性占位符或字符串文字中的表达式)
所以这段话的重点就是: 所有@RefreshScope的Bean都是延迟加载的,只有在第一次访问时才会初始化 刷新Bean也是同理,下次访问时会创建一个新的对象
七、@ConfigurationProperties应用 package com.hhao.erp.user.endpoint;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
/** * @author Wan * @date 2019/11/4 10:15 */ @RestController public class UserEndpoint2 { @Autowired UserConfigProperties properties;
@RequestMapping("/info2") public UserConfigProperties getProperties(){ return properties; } }
@Component @ConfigurationProperties(prefix = "user") class UserConfigProperties { String type; String name; Integer age;
public String getType() { return type; }
public void setType(String type) { this.type = type; }
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; }
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate birthday;
public LocalDate getBirthday() { return birthday; }
public void setBirthday(LocalDate birthday) { this.birthday = birthday; } } 在Nacos下,这会自动更新。 八、本地application.properties引用配置中心的参数 例如,在application.properties中添加如下: hhao.test.user.name=${user.name} 因为配置中心属性的加载顺序先于application.properteis,所以可以访问的到。