設計思路:
1. 獲取到consul中的配置文件內容;
通過consul的官網查看consul對外提供的RESTAPI,找到獲取consul中所有配置文件的名稱接口;找到根據配置文件名稱獲取配置文件內容的接口;
2. 保存到數據庫中;
通過mongoTemplate操作MongoDB數據庫;每次配置文件修改后保存的時候在mongo表里新增一條數據;
3. 保存之前判斷當前版本和數據庫版本是否一致;
mongo數據庫里存儲當前版本的hash值,如果新版本的hash值和當前版本的hash值一致就放棄本次保存操作,視為配置沒有改動,保留原來版本;
4. 保留20個版本;
新版本插入之前獲取當前版本編號,如果當前沒有版本則插入到第一個版本;如果當前有版本和當前版本號小於20則插入到當前最大版本+1的版本;如果當前版本=20在則刪除第1個版本,之后每個版本遞減1,新版本插入到第20版本;
5consul配置:
consul可視化界面修改配置后點擊保存時調用自己的rest接口
{
"watches":[
{
"type":"keyprefix",
"prefix":"config/",
"token":"p2BE1AtpwPbrxZdC6k+eXA",
"handler_type":"http",
"http_handler_config":{
"path":" http://127.0.0.1/consul/config/"
}
}
]
}
廢話不多說,先上代碼
1. 調用 consul提供的RESTAPI接口的讀取配置;
2. @Autowired
private RestTemplate restTemplate;
@Override
public String readConsul(String url, String ymlName, String token) {
try {
String reqJsonStr;
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType("text/html; charset=UTF-8");
headers.setContentType(type);
headers.set("X-Consul-Token", token);
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<String> exchange = restTemplate.exchange(new URI(url + ymlName), HttpMethod.GET, entity, String.class);
String result = exchange.getBody();
List<RestGetResultDo> conList = JSON.parseArray(result, RestGetResultDo.class);
return conList.get(0).getValue();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
3. 獲取consul中所有yml名稱接口
4. @Override
public List<String> getConsulConfigYmlNameList(String url, String token) {
RestTemplate restTemplate2 = new RestTemplate();
try {
String reqJsonStr;
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType("text/html; charset=UTF-8");
headers.setContentType(type);
headers.set("X-Consul-Token", token);
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<String> exchange = restTemplate.exchange(new URI(url), HttpMethod.GET, entity, String.class);
String result = exchange.getBody();
result = result.replaceAll("\r|\n| |\\[|\\]|\\\"", "");
String[] split = result.split(",");
return Arrays.asList(split);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
3.consul同步到mongoDB接口
public static final String VERSION_NUMBER = "versionNumber";
public static final int MAX_VERSION = 20;
@Autowired
private ConsulConfigManage consulConfigManage;
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private ConsulProperties consulProperties;
@Value("${spring.cloud.consul.token}")
private String consulToken;
/**
* consul配置持久化到MongoDB的實現類
*
* @return
*/
@Override
public Boolean consulToMongoDB() {
int versionNumber = 1;
int oldFirstDocumentHash = 1;
int newDocumentHash = 1;
boolean isSync = false;
try {
ConsulConfigDo configDo = new ConsulConfigDo();
//獲取到所有配置文件的名稱
List<String> consulConfigYmlNameList = consulConfigManage.getConsulConfigYmlNameList("http://" + consulProperties.getHost() + ":" + consulProperties.getPort() + "/v1/kv/" + "?keys", consulToken);
//把每個配置文件的name和content放到map中
Map map = new HashMap();
for (String ymlName : consulConfigYmlNameList) {
String ymlContent = consulConfigManage.readConsul("http://" + consulProperties.getHost() + ":" + consulProperties.getPort() + "/v1/kv/", ymlName, consulToken);
ymlName = ymlName.contains(".") ? ymlName = ymlName.substring(0, ymlName.indexOf(".")) : ymlName;
map.put(ymlName, ymlContent);
}
//查詢MongoDB中最近添加的一個版本
List<ConsulConfigDo> consulConfigDos = mongoTemplate.find(new Query().with(new Sort(new Sort.Order(Sort.Direction.DESC, "_id"))), ConsulConfigDo.class);
ConsulConfigDo first = null;
//初始版本
if (consulConfigDos.size() == 0) {
configDo.set_id(System.currentTimeMillis());
configDo.setConsul_content(map);
configDo.setVersionNumber(String.format("%02d", versionNumber));
configDo.setHash(hash(configDo.getConsul_content()));
mongoTemplate.insert(configDo);
isSync = true;
} else {
first = consulConfigDos.get(0);
//最近添加的一個版本hash
oldFirstDocumentHash = first.getHash();
configDo.setConsul_content(map);
newDocumentHash = hash(configDo.getConsul_content());
if (newDocumentHash == oldFirstDocumentHash) {
//不同步
return isSync;
} else {
//同步
String versionNumber1 = first.getVersionNumber();
Integer integer = Integer.valueOf(versionNumber1);
//如果數據小於20
if (integer < MAX_VERSION) {
versionNumber = integer + 1;
} else {
//刪掉第一條數據
mongoTemplate.remove(new Query(Criteria.where(VERSION_NUMBER).is("01")), ConsulConfigDo.class);
//每條數據的versionNumber遞減1
List<ConsulConfigDo> documents = mongoTemplate.find(new Query().with(new Sort(new Sort.Order(Sort.Direction.ASC, "_id"))), ConsulConfigDo.class);
for (ConsulConfigDo document : documents) {
Integer versionNum = Integer.valueOf(document.getVersionNumber());
String format = String.format("%02d", versionNum -= 1);
mongoTemplate.updateFirst(new Query(Criteria.where(VERSION_NUMBER).is(document.getVersionNumber())), Update.update(VERSION_NUMBER, format), ConsulConfigDo.class);
}
versionNumber = 20;
}
//插入到第20個版本
configDo.setVersionNumber(String.format("%02d", versionNumber));
configDo.setHash(hash(configDo.getConsul_content()));
configDo.set_id(System.currentTimeMillis());
mongoTemplate.insert(configDo);
isSync = true;
}
}
return isSync;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 計算hash值
*
* @param key
* @return
*/
private int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
4.consul返回結構類
@Data
public class RestGetResultDo implements Serializable {
private int LockIndex;
private String Key;
private int Flags;
private String Value;
private int CreateIndex;
private int ModifyIndex;
}
5.MongoDB結構
@Validated
@Data
@ToString
@Document(collection = "app_yml_config")
public class ConsulConfigDo {
private Long _id;
private Map consul_content;
private String versionNumber;
private int hash;
}
