随着线上环境的复杂多变,以及业务需求动荡,我们有足够的理由需要一个配置中心来处理配置的变更问题!
但对于项目初期,往往只需要能够做到数据支持动态配置,就能够满足需求了。
本文给出一个配置组件的实现方案,希望对有这方面需求的同学有点参考!
(本实例虽然只是从数据库取值,但是其实稍微做下扩展,就可以是一个完整的配置中心了,比如将从数据库更新缓存改为使用ZK的订阅功能进行缓存更新,即可随时接受后台传过来的配置变更了)
核心实现类:
/** * 简单数据库 k->v 字典表配置缓存工具类。 * 作用有二: * 1. 将配置放到数据库,方便变更; * 2. 配置查询可能很频繁, 将db数据缓存放到本地内存, 以减少数据库压力; * */ @Component @Slf4j public class ConfigDictManager { /** * 配置变量映射表 */ private final Map<String, ConfigNidValueStrictMapBean> configMappings = new HashMap<>(); // 缓存失效时间, 可以尽量做成配置可变的 private Long cacheTimeout = 100; // 从数据库取配置值的 mapper @Resource private DictConfigMapper dictConfigMapper; /** * 获取模块配置下的value, 未配置则返回null * * @param module 模块名 * @param configName 配置名 * @return 配置值, 没有配置时返回 null */ public String getConfigValue(String module, String configName) { return getConfigValueOrDefault(module, configName, null); } /** * 获取配置值带默认值的(如果没查到配置) * * @param module 模块名 * @param configName 配置key * @param defaultIfNull 默认值 * @return 配置值或者默认值 */ public String getConfigValueOrDefault(String module, String configName, String defaultIfNull) { if(module == null || configName == null) { throw new RuntimeException("配置变量名不能为空!"); } ConfigNidValueStrictMapBean moduleConfigs = getCachedModuleConfig(module); if(isConfigCacheExpired(moduleConfigs)) { // 首次初始化,必须同步等待,否则将出现前面几次取配置值为空情况 if(!isModuleConfigInitialized(moduleConfigs)) { blockingUpdateConfigNidModuleCache(moduleConfigs); } // 不是首次更新,可以使用旧值,因对配置生效实时性不高业务需求决定 else { noneBlockingUpdateConfigNidModuleCache(moduleConfigs); } } String value = moduleConfigs.getNameValuePairs() .getOrDefault(configName, defaultIfNull); log.debug("【配置中心】获取配置变量: {}->{} 值为: {}, default:{}" , module, configName, value, defaultIfNull); return value; } /** * 阻塞更新模块配置信息,用于初始化配置时使用 * * @param moduleConfigs 配置原始值 */ private void blockingUpdateConfigNidModuleCache(ConfigNidValueStrictMapBean moduleConfigs) { synchronized (moduleConfigs) { if(!isModuleConfigInitialized(moduleConfigs)) { if(!moduleConfigs.getIsUpdating().compareAndSet(false, true)) { log.warn("【配置中心】并发配置更新异常,请确认1!"); } updateConfigNidModuleCacheFromDatabase(moduleConfigs); } } } /** * 非阻塞更新模块配置信息,用于非初始化时的并发操作 * * @param moduleConfigs 配置原始值 */ private void noneBlockingUpdateConfigNidModuleCache(ConfigNidValueStrictMapBean moduleConfigs) { if((moduleConfigs.getIsUpdating().compareAndSet(false, true))) { updateConfigNidModuleCacheFromDatabase(moduleConfigs); } } /** * 判断是否模块数据已初始化 * * @param moduleConfigs 模块外部配置 * @return true|false */ private boolean isModuleConfigInitialized(ConfigNidValueStrictMapBean moduleConfigs) { return moduleConfigs.getNameValuePairs() != null; } /** * 获取模块配置缓存,如果没有值,则先默认初始化一个key * * @param module 模块名 * @return 模块配置 */ private ConfigNidValueStrictMapBean getCachedModuleConfig(String module) { ConfigNidValueStrictMapBean moduleConfig = configMappings.get(getModuleCacheKey(module)); if(moduleConfig == null) { synchronized (configMappings) { if((moduleConfig = configMappings.get(getModuleCacheKey(module))) == null) { String profile = SpringContextsUtil.getActiveProfile(); moduleConfig = new ConfigNidValueStrictMapBean(); moduleConfig.setModuleName(module); moduleConfig.setEnvironmentProfile(profile); moduleConfig.setUpdateTime(0L); // 初始为0,必更新 configMappings.put(getModuleCacheKey(module), moduleConfig); } } } return moduleConfig; } /** * 更新nid对应的模块缓存 * * @param moduleConfigs 原始缓存配置,更新后返回 */ private void updateConfigNidModuleCacheFromDatabase(ConfigNidValueStrictMapBean moduleConfigs) { String profile = SpringContextsUtil.getActiveProfile(); String module = moduleConfigs.getModuleName(); DictConfigEntity cond = new DictConfigEntity(); cond.setEnv(profile); cond.setModule(module); List<DictConfigEntity> resultList= dictConfigMapper.selectByCond(cond); Map<String, String> nidKeyValuePairs = new HashMap<>(); if(resultList != null && resultList.size() > 0) { resultList.forEach(c -> { nidKeyValuePairs.put(c.getVarName(), c.getVarValue()); }); moduleConfigs.setNameValuePairs(nidKeyValuePairs); moduleConfigs.setUpdateTime(System.currentTimeMillis()); if(!moduleConfigs.getIsUpdating().compareAndSet(true, false)) { log.warn("【配置中心】并发更新配置缓存异常,请注意!"); } } else { log.warn("【配置中心】系统变量没有配置,{}->{}->{},请确认配置!", profile, module); } moduleConfigs.setNameValuePairs(nidKeyValuePairs); } // 获取缓存模块时使用的缓存key, 默认可直接使用 模块名即可 private String getModuleCacheKey(String module) { return module; } /** * 检测配置缓存是否过期 * * @param moduleConfigs 模块的缓存 * @return true|false */ private boolean isConfigCacheExpired(ConfigNidValueStrictMapBean moduleConfigs) { return (System.currentTimeMillis() - cacheTimeout * 1000 > moduleConfigs.getUpdateTime()); } }