SpringBoot專案中透過程式修改Nacos配置

strongmore發表於2024-04-10

前言

在專案最好不要透過程式修改 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 中冒號、反斜槓等高階語法了。

相關文章