SpringBoot项目中通过程序修改Nacos配置
前言
在项目最好不要通过程序修改 nacos 配置,这样比较危险,如果代码有问题或者将其他的配置给覆盖了,可能会造成生产事故。需要频繁修改的配置信息最好存储到数据库。
修改 yaml 类型的配置
bootstrap.yaml 配置
spring:
application:
name: cnblogs
cloud:
nacos:
config:
server-addr: http://ip:8848
namespace: d8b0df04-aa58-4a5b-b582-7d133b9e8b2c #命名空间ID
file-extension: yaml
username: xxx
password: xxx
extension-configs:
- data-id: server.yaml
group: DEFAULT_GROUP
refresh: true
- data-id: testUpdateNacos.yaml
group: DEFAULT_GROUP
refresh: true
server.yaml
server:
port: 8090
servlet:
context-path: /cnblogs
dynamic:
nacos:
config:
server-addr: http://ip:8848
namespace: d8b0df04-aa58-4a5b-b582-7d133b9e8b2c
username: xxx
password: xxx
timeout: 5000
data-id: testUpdateNacos.yaml
group-id: DEFAULT_GROUP
key: myuser.name
testUpdateNacos.yaml,随便配置一些数据
order:
timeout: 1000
myuser:
address:
province: 上海
name: lisi
pwd: test123
product:
quantity: 2000
我们通过程序来修改 testUpdateNacos.yaml 配置文件中 myuser.name 的值
注意:testUpdateNacos.yaml 此文件必须提前创建好,且 myuser.name 的值也必须提前配置好(有一个默认值)
代码如下
@RestController
@RefreshScope
public class TestNacosController {
@Autowired
private DynamicNacosConfig dynamicNacosConfig;
@Value("${myuser.name}")
private String myUserName;
@PostMapping("testUpdateNacos")
public String testUpdateNacos(String updateValue) throws NacosException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, dynamicNacosConfig.getServerAddr());
properties.put(PropertyKeyConst.NAMESPACE, dynamicNacosConfig.getNamespace());
properties.put(PropertyKeyConst.USERNAME, dynamicNacosConfig.getUsername());
properties.put(PropertyKeyConst.PASSWORD, dynamicNacosConfig.getPassword());
ConfigService configService = NacosFactory.createConfigService(properties);
//获取当前配置
String config = configService.getConfig(dynamicNacosConfig.getDataId(), dynamicNacosConfig.getGroupId(), dynamicNacosConfig.getTimeout());
System.out.println(config);
DumperOptions dumperOptions = new DumperOptions();
//一定要设置
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(dumperOptions);
//如果value为对象,那么类型为LinkedHashMap
Map<String, Object> newMap = yaml.load(config);
String updateKey = dynamicNacosConfig.getKey();
String[] words = updateKey.split("[.]");
Map<String, Object> currentMap = newMap;
for (int i = 0; i < words.length; i++) {
String currentWord = words[i];
if (!currentMap.containsKey(currentWord)) {
break;
}
if (i != words.length - 1) {
currentMap = (Map<String, Object>) newMap.get(currentWord);
} else {
currentMap.put(currentWord, updateValue);
}
}
String newContent = yaml.dump(newMap);
System.out.println(newContent);
boolean success = configService.publishConfig(
dynamicNacosConfig.getDataId(),
dynamicNacosConfig.getGroupId(),
newContent,
ConfigType.YAML.getType());
return success ? "success" : "fail";
}
@GetMapping("/testQueryNacos")
public String testQueryNacos() {
return myUserName;
}
@ConfigurationProperties(prefix = "dynamic.nacos.config")
@Data
@Component
public static class DynamicNacosConfig {
//连接nacos的配置
private String serverAddr;
private String namespace;
private String username;
private String password;
private Integer timeout;
//要修改的配置
private String dataId;
private String groupId;
private String key;
}
}
通过 publishConfig() 方法修改配置和我们通过 nacos 管理后台页面修改是同样的效果,最终也会走 SpringCloud 的 RefreshScope 流程,达到自动刷新配置的效果。
以上面的程序为例,我们调用了 testUpdateNacos 请求之后,再调用 testQueryNacos,就会得到更新后的值。
注意:如果 testUpdateNacos.yaml 配置文件中,我们配置了注释,那么会被覆盖掉(yaml.dump()方法),但是 key 的顺序不会变,因为 yaml.load() 方法返回的是 LinkedHashMap。
修改 properties 类型的配置
bootstrap.properties
spring.application.name=cnblogs
spring.cloud.nacos.config.server-addr=http://ip:8848
spring.cloud.nacos.config.namespace=d8b0df04-aa58-4a5b-b582-7d133b9e8b2c
spring.cloud.nacos.config.file-extension=properties
spring.cloud.nacos.config.username=xxx
spring.cloud.nacos.config.password=xxx
spring.cloud.nacos.config.extension-configs[0].data-id=server.properties
spring.cloud.nacos.config.extension-configs[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[0].refresh=true
spring.cloud.nacos.config.extension-configs[1].data-id=testUpdateNacos.properties
spring.cloud.nacos.config.extension-configs[1].group=DEFAULT_GROUP
spring.cloud.nacos.config.extension-configs[1].refresh=true
server.properties
server.port=8090
server.servlet.context-path=/cnblogs
dynamic.nacos.config.server-addr=http://ip:8848
dynamic.nacos.config.namespace=d8b0df04-aa58-4a5b-b582-7d133b9e8b2c
dynamic.nacos.config.username=xxx
dynamic.nacos.config.password=xxx
dynamic.nacos.config.timeout=5000
dynamic.nacos.config.data-id=testUpdateNacos.properties
dynamic.nacos.config.group-id=DEFAULT_GROUP
dynamic.nacos.config.key=myuser.name
testUpdateNacos.properties
order.timeout=1000
myuser.address.province=上海
myuser.name=lisi
myuser.pwd=test123
product.quantity=2000
代码如下
@RestController
@RefreshScope
public class TestNacosController2 {
@Autowired
private DynamicNacosConfig dynamicNacosConfig;
@Value("${myuser.name}")
private String myUserName;
@PostMapping("testUpdateNacos2")
public String testUpdateNacos(String updateValue) throws NacosException, IOException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, dynamicNacosConfig.getServerAddr());
properties.put(PropertyKeyConst.NAMESPACE, dynamicNacosConfig.getNamespace());
properties.put(PropertyKeyConst.USERNAME, dynamicNacosConfig.getUsername());
properties.put(PropertyKeyConst.PASSWORD, dynamicNacosConfig.getPassword());
ConfigService configService = NacosFactory.createConfigService(properties);
//获取当前配置
String config = configService.getConfig(dynamicNacosConfig.getDataId(), dynamicNacosConfig.getGroupId(), dynamicNacosConfig.getTimeout());
System.out.println(config);
Properties configProperties = new Properties();
configProperties.load(new StringReader(config));
//更新配置,只做修改,不做新增
String updateKey = dynamicNacosConfig.getKey();
if (configProperties.containsKey(updateKey)) {
configProperties.put(updateKey,updateValue);
}
StringWriter stringWriter = new StringWriter();
configProperties.store(stringWriter,null);
String newContent = stringWriter.toString();
boolean success = configService.publishConfig(
dynamicNacosConfig.getDataId(),
dynamicNacosConfig.getGroupId(),
newContent,
ConfigType.PROPERTIES.getType());
return success ? "success" : "fail";
}
@GetMapping("/testQueryNacos2")
public String testQueryNacos() {
return myUserName;
}
@ConfigurationProperties(prefix = "dynamic.nacos.config")
@Data
@Component
public static class DynamicNacosConfig {
//连接nacos的配置
private String serverAddr;
private String namespace;
private String username;
private String password;
private Integer timeout;
//要修改的配置
private String dataId;
private String groupId;
private String key;
}
}
通过 Properties.store() 方法得到的字符串,key 的顺序是乱的,而且其中的注释也会被覆盖掉。我们可以通过手动拼接字符串的方式来避免这个问题。
@PostMapping("testUpdateNacos3")
public String testUpdateNacos3(String updateValue) throws NacosException, IOException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, dynamicNacosConfig.getServerAddr());
properties.put(PropertyKeyConst.NAMESPACE, dynamicNacosConfig.getNamespace());
properties.put(PropertyKeyConst.USERNAME, dynamicNacosConfig.getUsername());
properties.put(PropertyKeyConst.PASSWORD, dynamicNacosConfig.getPassword());
ConfigService configService = NacosFactory.createConfigService(properties);
//获取当前配置
String config = configService.getConfig(dynamicNacosConfig.getDataId(), dynamicNacosConfig.getGroupId(), dynamicNacosConfig.getTimeout());
System.out.println(config);
//更新配置,只做修改,不做新增
String updateKey = dynamicNacosConfig.getKey();
StringBuilder newContent = new StringBuilder();
String[] lines = config.split("\n");
for (String line : lines) {
String[] arr = line.split("=");
String key = arr[0].trim();
// 排除注释
if (!line.startsWith("#") && Objects.equals(key,updateKey)) {
newContent.append(key).append("=").append(updateValue);
} else {
//原来的配置
newContent.append(line);
}
newContent.append("\n");
}
boolean success = configService.publishConfig(
dynamicNacosConfig.getDataId(),
dynamicNacosConfig.getGroupId(),
newContent.toString(),
ConfigType.PROPERTIES.getType());
return success ? "success" : "fail";
}
当然我们自己拼接,肯定是不支持 properties 中冒号、反斜杠等高级语法了。