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 中冒号、反斜杠等高级语法了。

posted @ 2024-04-10 07:13  strongmore  阅读(777)  评论(0编辑  收藏  举报