基于nacos管理配置服务,这里借助starter机制与nacos一起配合管理k-v类型的数据,比如枚举数据,简化应用接入k-v数据的复杂度,降低代码复杂度。
通过配置dataid,groupid动态与nacos进行交互,并解析数据,通过接口访问。
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.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(NacosDataDictAutoConfiguration.class)
public @interface EnableNacosDataDict {
}
@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) +
'}';
}
}
public interface DataService {
/**
* 对配置的nacos dataID-group进行
* 注册变更监听
*/
void addListener() throws NacosException;
/**
* 根据nacos dataID groupid获取 配置数据
* @param dataID
* @param groupID
* @return map
*/
Map> getDataMap(String dataID, String ... groupID);
/**
* 根据nacos dataID group id获取 配置数据
* @param dataID
* @param groupID
* @return
*/
Map> getDataKVList(String dataID, String ... groupID);
}
@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());
}
}
@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>> kvEnumMap = new ConcurrentHashMap();
/**
* k:nacos中的group_id
* v:nacos中group_id 对应的数据ListKVPair
*/
private ConcurrentHashMap>> kvPairMap = new ConcurrentHashMap();
/**
* k:nacos中的group_id
* v:nacos中group_id 对应的数据ListKVPair
*/
private ConcurrentHashMap>>> kvParentPairMap = new ConcurrentHashMap();
@Override
public void addListener() throws NacosException {
Map 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> getDataMap(String dataID, String ... groupIDArr) {
Map> dataMap = new HashMap<>();
Map> AllDataMap = kvEnumMap.get(dataID);
for (String groupId : groupIDArr){
StrictMap strictMap = AllDataMap.get(groupId);
if(strictMap != null){
dataMap.put(groupId,strictMap);
}
}
return dataMap;
}
@Override
public Map> getDataKVList(String dataID, String ... groupID) {
Map> map = new HashMap<>();
Map> AllDataMap = kvPairMap.get(dataID);
for (String groupId : groupIDArr){
StrictList 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> mapMap = new HashMap<>();
mapMap.put(groupID, dataStrictMap);
kvEnumMap.put(dataID,mapMap);
}
if(!dataStrictList.isEmpty()){
Map> map = new HashMap<>();
map.put(dataID, dataStrictList);
kvPairMap.put(groupID,map);
}
if(!dataStrictKVPList.isEmpty()){
Map>> map = new HashMap<>();
map.put(dataID, dataStrictKVPList);
kvParentPairMap.put(groupID, map);
}
logger.info(array[0] + " has data changed,dataId={},groupID={}......................................",dataID,groupID);
}
}
/**
* 自定义list
* @author fcs
* @param
*/
public class StrictList 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
* @param 用一个name表示这个map存的是什么数据
*/
public class StrictMap extends HashMap {
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 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 "";
}
}