数据的同步机制
数据的同步在集群环境中至关重要,虽然image-manage
已经避免在pod中使用单节点数据,控制各个pod状态一致,但是出于io性能的尝试,image-manage会每个节点都备份
一份数据
# 1.1 image-manage启动同步
image-image启动会视情况选择复制一个pod内所有的数据或者在数据库表中进行数据比照,同步自己缺少的数据
。
public void startSy(){
log.info("开始同步文件");
String bestPod = podUtil.getBestPod();
log.info("寻找最优同步节点:"+bestPod);
String fileUrl = httpUtil.questProtocol+bestPod+dnsSuffix+":"+serverPort+ "?token="+PodDataSynConfig.SY_KEY_VALUE;
// Send an HTTP GET request to the file URL
byte[] fileData = httpUtil.getBytes(fileUrl);
if (fileData != null) {
FileUtil.unZip(fileData,imgFolderPath);
}else {
log.warn("无法获取最优节点文件,准备进行数据库扫表同步");
}
//开始数据库同步
partSy();
}
public void partSy(){
PodDataSynConfig.syedFlag = false;
allFileName = FileUtil.listFilesInDirectory(imgFolderPath);
log.info("开始扫表同步文件");
List<ImageMetaDataAddFileName> imageMetaDataList = fileDao.getAllImg();
if(imageMetaDataList != null){
for(ImageMetaDataAddFileName imageMetaData : imageMetaDataList){
if(!imageMetaData.getLocalUrl().contains(PodDataSynConfig.CURRENT_POD)){
Stack<String>stack = new Stack<>(){{
for(String pod :imageMetaData.getLocalUrl()){
push(pod);
}
}};
String metaFileName = imageMetaData.getMd5() + "." + FileUtil.splitFileNameAndExtension(imageMetaData.getImageName())[1];
byte[]array = accessServers(stack,metaFileName);
if(array != null){
File file = new File(imgFolderPath+"/"+metaFileName);
try(FileOutputStream fileOutputStream = new FileOutputStream(file)) {
fileOutputStream.write(array);
addPodRecord(new ImageMetaData(imageMetaData));
} catch (IOException e) {
log.warn("文件写入异常:=>"+imageMetaData.getMd5());
throw new RuntimeException();
}
}
}
}
}
# 1.2 image-manage push同步
@Async
public void sendFile(MultipartFile file,String metaFileName){
podUtil.flushPod();
for(String pod : podUtil.getOthersPods()){
String url = httpUtil.questProtocol +pod+dnsSuffix + ":" +serverPort +"/k8s/receiveImg";
MultiValueMap<String, Object> body = null;
try {
body = getStringObjectMultiValueMap(file);
body.add("metaFileName",metaFileName);
} catch (IOException e) {
throw new RuntimeException(e);
}
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
try {
sendFile(url,requestEntity);
} catch (Exception e) {
log.error("推送到服务器url出错==>"+url);
}
}
}
提示
如果某个pod接受到文件,pod会尝试把文件推送到其他pod中,确保数据一致
# 1.3 image-manage pull同步
public boolean getMd5File(String md5FileName){
podUtil.flushPod();
Stack<String>stack = new Stack<>(){{
for(String pod : podUtil.surPod){
push(pod);
}
}};
byte[]array = accessServers(stack,md5FileName);
if(array != null){
File file = new File(imgFolderPath+"/"+md5FileName);
try(FileOutputStream fileOutputStream = new FileOutputStream(file)) {
fileOutputStream.write(array);
} catch (IOException e) {
return false;
}
}
fileDao.updateAddLocalUrl(md5FileName,PodDataSynConfig.CURRENT_POD);
return true;
}
提示
如果image-manage 发现请求中的image-manage文件没有,image-manage会向其他pod请求该文件。
# 1.4 定时扫表同步
提示
image-manage在启动后会生随机时间的定时任务,每24小时执行一次,确保至少每天进行一个扫表同步。
if(!PodDataSynConfig.initFlag){
String hour = String.valueOf((int)(25 * Math.random()));
ScheduleUtil.start(new ScheduleTask(UUID.randomUUID().toString(),new SyTaskServiceImpl()),String.format("0 0 %s * * ?",hour));
log.info("创建数据库同步定时任务成功=>同步时间为:每日"+hour+":00");
}
# 2.总结
注意
即便这样,这也并非一个经过检验成熟的方案,无法确保数据的万无一失