多个系统之间需要同步数据,比如A系统需要B系统的商品数据,每次变更和新增都要同步到A系统中。
同步方案
主动推送
B系统新增和更新变化时,直接调用A系统中接口进行新增或更新,同步需要最后更新时间或者是版本号
B系统的伪代码
public void addOrUpdate(){
// 保存或更新到本地数据库中
addOrUpdate();
// 通知到A系统
notifyA();
}
public void notifyA(){
// 调用A系统的接口
resttemplate.syncData();
}
A系统的伪代码
@Post("syncData")
public void syncData(){
// 同步数据
}
上述方法通过直接调用接口的方式,这种方式耦合性很高,并且A系统挂了或其他错误都会导致B系统的保存和更新不正确,也影响到了B系统接口的响应速度。如果为了不影响B系统可以优化为调用接口以线程执行的方式来完成。
主动拉取
A系统以定时任务主动拉取最新的更新数据
A系统的伪代码
// 定时任务
@Scheduled(cron = "0 1 * * * *")
public void handlerScheduled(){
// 查询本地数据的最后更新时间
Date lastTime = queryLastData();
// 调用系统B接口获取数据
restTemplate.queryLastTimeData(lastTime);
// 保存或更新到本地数据库中
addOrUpdate();
}
public void addOrUpdate(){
// 保存或更新到本地数据库中
}
// B系统的伪代码
// B系统的数据更新和保存,都更新lastTime
@Post("queryLastTimeData")
public void queryLastTimeData(Date lastTime){
// 查询最新时间的数据
if(lastTime == null) {
// 拉取全部数据
} else {
// 拉取 大于等于 lastTime的数据
}
}
上述方法B系统的变更不需要通知到A系统,A系统主动查询,控制查询频次。
以中间件的方式通知
B系统新增和更新变化时,通知以中间件的方式来完成,不在直接以接口的调用方式为主。下面是已rocketmq中间件的方式。
B系统的伪代码
public void addOrUpdate(){
// 保存或更新到本地数据库中
addOrUpdate();
// 通知到A系统
notifyA(data);
}
public void notifyA(data){
// 将变更通知到rocketmq中
rocketmq.send(topic, data)
}
A系统的伪代码
// 消费topic
public void syncData(){
// 同步数据
}
使用canal工具同步
利用binlog日志记录了update,insert,delete的数据的方式,读取binlog,获取更新数据,发送到rocketmq中,A系统消费。开源工具alibaba的canal工具。利用mysql主从备份的机制,canal会发送消息到mysql,我是slave,同步数据,mysql会将binlog发送到canal中
四种方案优缺点
1方案主动推送耦合性很高,如果使用了线程来通知,此时就无法保证两个系统的数据一致性
2方案定时任务主动查询,实时性依赖于定时任务的查询的频次,频次太高,影响了A和B系统的性能。
3方案是弥补了方案1的缺点,但是需要引入中间件
4方案有大量的数据需要同步,并且不影响各个系统的