基础工具组件starter-datadict-nacos设计与实现

一、功能描述

基于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 代码解析

  1. 定义enable注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(NacosDataDictAutoConfiguration.class)
public @interface EnableNacosDataDict {
}
  1. 定义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) +
                '}';
    }
}
  1. 定义DataService接口
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);


}
  1. 定义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());
    }

}
  1. 定义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>> 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);
    }
}
  1. 自定义list数据结构
/**
 * 自定义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 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);
    }
}
  1. 自定义map数据结构
/**
 * 自定义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 "";
    }

}

你可能感兴趣的:(模块提取专题,分布式理论&实践,基础工具组件,spring,starter,nacos,datadict)