package com.lememo.nacosconfigadapter; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; /** * @author houyi **/ public abstract class AbstractNacosConfigService implements NacosConfigService { /** * key和unit的映射关系 */ private Map<String, ConfigUnit> unitMap; /** * key和配置对象的映射关系 */ private Map<String, NacosConfig> configMap; /** * Nacos 配置项的 dataId */ private String dataId; /** * Nacos 配置项的 group */ private String group; /** * Nacos 配置服务 api */ private ConfigService configService; public AbstractNacosConfigService(String serverAddress, String dataId, String group) { this.unitMap = new ConcurrentHashMap<>(); this.configMap = new ConcurrentHashMap<>(); // 刷新 unit this.refreshUnit(); this.dataId = dataId; this.group = group; Properties properties = new Properties(); properties.put("serverAddr", serverAddress); try { this.configService = NacosFactory.createConfigService(properties); // 增加监听器 Listener listener = new ConfigListener(); this.configService.addListener(dataId, group, listener); } catch (NacosException e) { e.printStackTrace(); } } /** * 获取 ConfigUnit 的对象列表 * * @return 对象列表 */ public abstract List<ConfigUnit> getConfigUnitList(); /** * 接收到最新的配置信息时,是否对 ConfigUnit 进行刷新 * * @return true:刷新 false:不刷新 */ public abstract boolean refreshUnitOnReceiveConfig(); @Override public NacosConfig getConfig(String key) { return configMap.get(key); } @Override public Object getConfigValue(String key) { NacosConfig config = getConfig(key); return config != null ? config.getValue() : null; } @Override public List<NacosConfig> getConfigList(int grade) { if (configMap.isEmpty()) { return new ArrayList<>(); } List<NacosConfig> configList = new ArrayList<>(configMap.size()); for (Map.Entry<String, NacosConfig> entry : configMap.entrySet()) { NacosConfig config = entry.getValue(); // 如果当前config的等级小于或等于指定的等级 if (!config.higherGrade(grade)) { configList.add(config); } } // 根据 grade 进行升序排序 configList.sort(new Comparator<NacosConfig>() { @Override public int compare(NacosConfig o1, NacosConfig o2) { return o1.getUnit().getGrade() - o2.getUnit().getGrade(); } }); return configList; } @Override public boolean updateConfig(String key, Object value, int grade) { if (key == null || key.trim().length() == 0) { throw new IllegalArgumentException("key can not be null"); } if (value == null) { throw new IllegalArgumentException("value can not be null"); } // 获取配置项 NacosConfig config = getConfig(key); if (config == null) { throw new IllegalArgumentException("config does not exists with key=" + key); } // 如果配置项的 unit 不合法 if (!config.validUnit()) { throw new IllegalArgumentException("config unit invalid with key=" + key); } // 如果当前配置项的等级更高,则不允许修改 if (config.higherGrade(grade)) { throw new IllegalArgumentException("you can't update config with key=" + key + ", cause this config has a higher grade"); } // 如果当前配置项的等级更高,则不允许修改 if (config.readOnly()) { throw new IllegalArgumentException("you can't update config with key=" + key + ", cause this config is read only"); } // 更新配置项前的一些操作,例如: // 1.记录更新操作的日志,更新者,更新时间等等,方便回溯 // 2.甚至可以把操作前的内容记录下来,方便一键回滚到上一次的值 // 更新配置项的值 config.setValue(value); // 检查当前配置项的值类型是否合法 if (!config.validValueType()) { throw new IllegalArgumentException("value type invalid with key=" + key + ", should be:" + config.getUnit().getType()); } configMap.put(key, config); // 将更新后的配置内容推送到 nacos 服务端 return publishConfig(); } /** * 刷新key 和 unit 的对应关系 */ private void refreshUnit() { List<ConfigUnit> factorList = getConfigUnitList(); if (factorList == null || factorList.isEmpty()) { return; } unitMap.clear(); for (ConfigUnit unit : factorList) { unitMap.put(unit.getKey(), unit); } } /** * 重新处理配置信息 * 将配置信息加载到 keyConfig 中去 * * @param content 配置信息 */ private void reloadConfigs(String content) { Properties properties = new Properties(); try { properties.load(new ByteArrayInputStream(content.getBytes())); } catch (IOException e) { e.printStackTrace(); } // 清空内存中原来的值 configMap.clear(); // 遍历 properties for (Object k : properties.keySet()) { String key = k.toString(); // 获取该 key 对应的 unit ConfigUnit unit = unitMap.get(key); if (unit == null) { continue; } // 将配置项的值封装成 config 对象 String val = properties.getProperty(key); Object value = parseValue(val, unit.getType()); NacosConfig config = new NacosConfig(key, value, unit); configMap.put(key, config); } } private Object parseValue(String val, ConfigType type) { Object value; try { switch (type) { case INT: { value = Integer.parseInt(val); } break; case DOUBLE: { value = Double.parseDouble(val); } break; case FLOAT: { value = Float.parseFloat(val); } break; case LONG: { value = Long.parseLong(val); } break; case BOOLEAN: { value = Boolean.parseBoolean(val); } break; case STRING: { value = val; } break; default: { value = val; } break; } } catch (Exception e) { value = val; } return value; } private boolean publishConfig() { StringBuilder sb = new StringBuilder(); int size = configMap.size(); int index = 1; for (Map.Entry<String, NacosConfig> entry : configMap.entrySet()) { sb.append(entry.getKey()) .append("=") .append(entry.getValue().getValue()); if (index++ < size) { sb.append("\n"); } } String newContent = sb.toString(); try { configService.publishConfig(dataId, group, newContent); } catch (NacosException e) { e.printStackTrace(); return false; } return true; } private class ConfigListener implements Listener { @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String content) { // 如果需要每次对 unit 进行刷新 if (refreshUnitOnReceiveConfig()) { refreshUnit(); } reloadConfigs(content); } } }