Nacos Config的SpringBoot api使用起來不太靈活, 我們更希望要的是一個(gè)抽象的緩存工具藕溅,Nacos Config只是其中一種實(shí)現(xiàn)关翎。
Nacos Config的java api并不提供本地緩存戒悠,每次請(qǐng)求都會(huì)去配置中心拉取數(shù)據(jù)抓歼,所以需要我們動(dòng)手封裝一下败富。
定義配置工具接口
import java.util.Map;
import java.util.Properties;
public interface IConfig {
String getStringConfig(String path);
Object getJsonConfig(String path, String key);
Map<String, Object> getJsonConfig(String path);
Properties getPropertiesConfig(String path);
}
Nacos Config 實(shí)現(xiàn)
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 com.qiejk.commons.util.base.JsonTool;
import com.qiejk.commons.util.base.LogTool;
import com.qiejk.commons.util.base.SpringContextTool;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
@Component
public class NacosConfigTool implements IConfig {
private static final String GROUP = "COMMON_CONFIG";
private ConfigService service;
// 本地緩存
private Map<String, String> cache = new ConcurrentHashMap<>();
private Map<String, Object> lockMap = new ConcurrentHashMap<>();
public NacosConfigTool() {
try {
service = NacosFactory.createConfigService(SpringContextTool.getBean(ConfigConfiguration.class).getNacosServerAddr());
} catch (NacosException e) {
LogTool.error("NacosConfigTool init error.", e);
}
}
private String getConfig(String dataId) {
String ret;
if ((ret = cache.get(dataId)) != null) {
return ret;
}
// Nocos 的 addLisener 竟然有線程安全問題悔醋,其實(shí)是同一個(gè)節(jié)點(diǎn)可以add多個(gè)lisener 這里需要做一個(gè)鎖定
Object lock = lockMap.computeIfAbsent(dataId, k -> new Object());
synchronized (lock) {
if ((ret = cache.get(dataId)) == null) {
ret = getConfigFromRemote(dataId);
if (ret != null) {
cache.put(dataId, ret);
}
}
lockMap.remove(dataId);
}
return ret;
}
private String getConfigFromRemote(String dataId) {
try {
return service.getConfigAndSignListener(dataId, GROUP, 5000, new MNacosConfigListener(dataId, cache));
} catch (NacosException e) {
LogTool.error("NacosConfigTool getConfigFromRemote error. dataId=" + dataId, e);
}
return null;
}
// 配置監(jiān)聽
static class MNacosConfigListener implements Listener {
private String dataId;
private Map<String, String> cache;
public MNacosConfigListener(String dataId, Map<String, String> cache) {
this.dataId = dataId;
this.cache = cache;
}
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
cache.put(dataId, configInfo);
System.out.println("MNacosConfigListener.receiveConfigInfo " + dataId + " refresh config. " + configInfo);
}
}
@Override
public String getStringConfig(String dataId) {
return getConfig(dataId);
}
@Override
public Object getJsonConfig(String dataId, String key) {
return getJsonConfig(dataId).get(key);
}
@Override
public Map<String, Object> getJsonConfig(String dataId) {
return JsonTool.json2Map(getConfig(dataId));
}
@Override
public Properties getPropertiesConfig(String dataId) {
return null;
}
工具類
import com.qiejk.commons.util.base.u.IConfig;
import java.util.Map;
import java.util.Properties;
public class ConfigTool {
private static IConfig config = SpringContextTool.getBean(IConfig.class);
private ConfigTool() {
}
public static Object getJsonConfig(String path, String key) {
return config.getJsonConfig(path, key);
}
public static Map<String, Object> getJsonConfig(String path) {
return config.getJsonConfig(path);
}
public static Properties getPropertiesConfig(String path) {
return config.getPropertiesConfig(path);
}
public static String getStringConfig(String path){
return config.getStringConfig(path);
}
}
這樣我們就得到了一個(gè)不知道底層是啥實(shí)現(xiàn)的配置工具,并且默認(rèn)實(shí)現(xiàn)是Nacos Config兽叮,并且默認(rèn)有本地緩存芬骄。
效果
@Test
public void testNacosConfig(){
while(true){
Uninterruptibles.sleepUninterruptibly(3, TimeUnit.SECONDS);
System.out.println("test = " + ConfigTool.getStringConfig("test"));
}
}
test = 5
MNacosConfigListener.receiveConfigInfo test refresh config. 6
test = 6
test = 6
MNacosConfigListener.receiveConfigInfo test refresh config. 7
test = 7
test = 7
優(yōu)化
其實(shí)還還有很大優(yōu)化空間,比如本地緩存換成guava鹦聪。