基礎工具元件starter-datadict-nacos設計與實現
阿新 • • 發佈:2021-01-09
技術標籤:輕蝸牛租房技術&業務架構系列模組提取專題分散式理論&實踐基礎工具元件spring starternacosdatadict
一、功能描述
基於nacos管理配置服務,這裡藉助starter機制與nacos一起配合管理k-v型別的資料,比如列舉資料,簡化應用接入k-v資料的複雜度,降低程式碼複雜度。
二、實現原理
通過配置dataid,groupid動態與nacos進行互動,並解析資料,通過介面訪問。
2.1 配置說明
nacos: #:資料字典型別的配置(k-v) datadict: dataIDArr: com.coderman.dict1,com.coderman.dict2,com.coderman.dict3 groupIDArr: 0-g1,0-g2,0-g3,1-g4,1-g5,2-g1
這裡說明一下groupIDArr中的0-g1的資料結構的含義:0代表dataIDArr的第一個dataId,g1代表groupId.
2.2 程式碼解析
- 定義enable註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(NacosDataDictAutoConfiguration.class)
public @interface EnableNacosDataDict {
}
- 定義NacosDataProperties類
@Configuration @ConfigurationProperties(prefix = "nacos.datadict") public class NacosDataProperties { /** * nacos的dataID * */ private String [] dataIDArr; /** * nacos的groupId * 與dataID進行對映時採用 * 0:groupId0, * 1:groupId1格式,0,1則是nacos dataID 在dataIDArr * 的索引位置 */ private String [] groupIDArr; public String[] getDataIDArr() { return dataIDArr; } public void setDataIDArr(String[] dataIDArr) { this.dataIDArr = dataIDArr; } public String[] getGroupIDArr() { return groupIDArr; } public void setGroupIDArr(String[] groupIDArr) { this.groupIDArr = groupIDArr; } @Override public String toString() { return "NacosDataConfig{" + "dataIDArr=" + Arrays.toString(dataIDArr) + ", groupIDArr=" + Arrays.toString(groupIDArr) + '}'; } }
- 定義DataService介面
public interface DataService { /** * 對配置的nacos dataID-group進行 * 註冊變更監聽 */ void addListener() throws NacosException; /** * 根據nacos dataID groupid獲取 配置資料 * @param dataID * @param groupID * @return map<k,v> */ Map<String,StrictMap<String>> getDataMap(String dataID, String ... groupID); /** * 根據nacos dataID group id獲取 配置資料 * @param dataID * @param groupID * @return */ Map<String, StrictList<KVPair>> getDataKVList(String dataID, String ... groupID); }
- 定義NacosDataDictAutoConfiguration類
@Configuration
@EnableConfigurationProperties(NacosDataProperties.class)
@ConditionalOnProperty(prefix = "nacos.datadict", havingValue = "true",name = "nacos.datadict")
public class NacosDataDictAutoConfiguration {
@Autowired
private NacosDataProperties nacosDataProperties;
/**
* 對外暴露的資料服務
* @return
*/
@Bean(name = "nacosDataService")
public NacosDataServiceImpl demoService(){
return new NacosDataServiceImpl(nacosDataProperties.getDataIDArr(), nacosDataProperties.getGroupIDArr());
}
}
- 定義DataService介面實現
@Service
public class NacosDataServiceImpl implements DataService{
private Logger logger = LoggerFactory.getLogger(this.getClass());
public NacosDataServiceImpl(){}
public NacosDataServiceImpl(String [] dataIDArr, String [] groupIDArr){}
/**
* nacos的dataID
*
*/
private String [] dataIDArr;
/**
* nacos的groupId
* 與dataID進行對映時採用
* 0:groupId0,
* 1:groupId1格式,0,1則是nacos dataID 在dataIDArr
* 的索引位置
*/
private String [] groupIDArr;
@NacosInjected
private ConfigService configService;
private ConcurrentHashMap<String, Map<String,StrictMap<String>>> kvEnumMap = new ConcurrentHashMap();
/**
* k:nacos中的group_id
* v:nacos中group_id 對應的資料ListKVPair
*/
private ConcurrentHashMap<String, Map<String,StrictList<KVPair>>> kvPairMap = new ConcurrentHashMap();
/**
* k:nacos中的group_id
* v:nacos中group_id 對應的資料ListKVPair
*/
private ConcurrentHashMap<String, Map<String,List<KVParentPair<String,String,String>>>> kvParentPairMap = new ConcurrentHashMap();
@Override
public void addListener() throws NacosException {
Map<String,String> dataIDMap = new HashMap<>();
if(dataIDArr != null && dataIDArr.length > 0){
for (int i = 0;i < dataIDArr.length; i++){
dataIDMap.put(i+"", dataIDArr[i]);
}
}
if(groupIDArr != null && groupIDArr.length >0){
logger.warn("nacos data not fund groupIdArr ,please check your config application-nacosdatadict.yml");
return;
}
for (String str : groupIDArr){
String [] strArr = str.split("-");
String dataID = dataIDMap.get(strArr[0]);
if(StringUtils.isNotEmpty(dataID)){
convertNacosData(dataID,strArr[1]);
addListener(dataID,strArr[1]);
}
}
}
/**
* 針對不同的dataid,groupid新增listener
* @param dataId
* @param groupID
* @throws NacosException
*/
private void addListener(String dataId, final String groupID) throws NacosException {
configService.addListener(dataId, groupID, new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
exeConvert(configInfo,dataId,groupID);
}
@Override
public Executor getExecutor() {
return null;
}
});
}
@Override
public Map<String,StrictMap<String>> getDataMap(String dataID, String ... groupIDArr) {
Map<String,StrictMap<String>> dataMap = new HashMap<>();
Map<String,StrictMap<String>> AllDataMap = kvEnumMap.get(dataID);
for (String groupId : groupIDArr){
StrictMap<String> strictMap = AllDataMap.get(groupId);
if(strictMap != null){
dataMap.put(groupId,strictMap);
}
}
return dataMap;
}
@Override
public Map<String,StrictList<KVPair>> getDataKVList(String dataID, String ... groupID) {
Map<String,StrictList<KVPair>> map = new HashMap<>();
Map<String,StrictList<KVPair>> AllDataMap = kvPairMap.get(dataID);
for (String groupId : groupIDArr){
StrictList<KVPair> list = AllDataMap.get(groupId);
if(list != null){
map.put(groupId,list);
}
}
return map;
}
/**
* 解析配置內容
* @param dataID
* @param groupID
* @throws NacosException nacos異常
*/
private void convertNacosData(String dataID, String groupID) throws NacosException {
String content = configService.getConfig(dataID, groupID, 3000);
exeConvert(content,dataID,groupID);
}
private void exeConvert(String content,String dataID,String groupID){
String[] array = content.split("\n");
StrictMap dataStrictMap = new StrictMap<>(array[0]);
StrictList dataStrictList = new StrictList<>(array[0]);
StrictList dataStrictKVPList = new StrictList<>(array[0]);
for (int i = 1; i < array.length; i++) {
String[] kvArr = array[i].replace(" ", "").replace("\r", "").split(",");
if(kvArr.length == 3){
dataStrictKVPList.add(new KVParentPair<>(kvArr[1], kvArr[0], kvArr[2]));
}
else if(kvArr.length == 2){
dataStrictMap.put(kvArr[1], kvArr[0]);
dataStrictList.add(new KVPair<>(kvArr[1], kvArr[0]));
}
}
if(!dataStrictMap.isEmpty()){
Map<String,StrictMap<String>> mapMap = new HashMap<>();
mapMap.put(groupID, dataStrictMap);
kvEnumMap.put(dataID,mapMap);
}
if(!dataStrictList.isEmpty()){
Map<String,StrictList<KVPair>> map = new HashMap<>();
map.put(dataID, dataStrictList);
kvPairMap.put(groupID,map);
}
if(!dataStrictKVPList.isEmpty()){
Map<String,List<KVParentPair<String,String,String>>> map = new HashMap<>();
map.put(dataID, dataStrictKVPList);
kvParentPairMap.put(groupID, map);
}
logger.info(array[0] + " has data changed,dataId={},groupID={}......................................",dataID,groupID);
}
}
- 自定義list資料結構
/**
* 自定義list
* @author fcs
* @param <E>
*/
public class StrictList<E> extends ArrayList {
private final String name;
public StrictList(String name, int initialCapacity) {
super(initialCapacity);
this.name = name;
}
public StrictList(String name) {
super();
this.name = name;
}
public StrictList(String name, Collection<? extends E> m) {
super(m);
this.name = name;
}
@Override
public boolean add(Object value) {
if (super.contains(value)) {
throw new IllegalArgumentException(name + " already contains value for " + value);
}
return super.add(value);
}
}
- 自定義map資料結構
/**
* 自定義map
* @param <V> 用一個name表示這個map存的是什麼資料
*/
public class StrictMap <V> extends HashMap<String, V> {
private static final long serialVersionUID = -4950446264854982944L;
private final String name;
public StrictMap(String name, int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
this.name = name;
}
public StrictMap(String name, int initialCapacity) {
super(initialCapacity);
this.name = name;
}
public StrictMap(String name) {
super();
this.name = name;
}
public StrictMap(String name, Map<String, ? extends V> m) {
super(m);
this.name = name;
}
@SuppressWarnings("unchecked")
@Override
public V put(String key, V value) {
if (containsKey(key)) {
throw new IllegalArgumentException(name + " already contains value for " + key);
}
return super.put(key, value);
}
@Override
public V get(Object key) {
V value = super.get(key);
if (value == null) {
//throw new IllegalArgumentException(name + " does not contain value for " + key);
return null;
}
return value;
}
@Override
public String toString(){
return "";
}
}