项目上需要用到一个类似与zookeeper的心跳加节点数据同步的功能,但由于项目上不使用zookeeper,便决定自己实现一套,以便以后有类似需求可以直接使用。
这是一个可以处理节点间心跳和基于心跳进行数据同步的框架,此框架重在心跳实现,如果要实现数据同步需要人工处理,下面会说明如何基于此框架实现数据同步。
base 实现协议需要继承的抽象类包,目前只实现了udp协议
AbstractAgreement 协议抽象类
constants 常量类包
Constants 常量类,存储一些文件路径和共用的字符串
handle 接收参数时的处理包
BaseHandle 消息处理基类
AckHandle 在发送方接收到接收方返回的回执消息时调用
ReceiveHandle 在接收方接收到发送方发送的消息时调用
thread 线程包
Receive 接收线程
Send 发送线程
StatusThread 节点状态更新线程
util 工具包
HeartBox 心跳盒子,存储心跳期间需要使用的变量
ProcessEngine 暂不用,留着
Udp udp协议实现,udp心跳入口
启动心跳线程前,需要将heartBox放在全局的地方,因为所有的信息都在于操作这一个对象,比如
public class Sets {
private Sets(){}
/** 心跳相关 */
public static HeartBox heartBox = new HeartBox();
/** 同步列表 结构为 标识->消息*/
public static Map<String,Map<String,Object>> synMap = new HashMap<String,Map<String,Object>>();
}
int port = 8108;
String pIp = "127.0.0.1";
int pPort = 8191;
long timeout = 10000;
// 启动心跳线程
new Udp(ip, port, pIp, pPort, timeout, Sets.heartBox).begin();
如果没有父级
new Udp(ip, port, timeout, heartBox).begin();
// 此步为数据同步需要,启动消息同步线程
new Thread(new MessageSynThread(Sets.heartBox)).start();
messageSynThread内容如下
/**
* 消息同步线程
* @ClassName MessageSynThread
* @Description: 消息同步线程
* @date 2019年6月12日 下午5:19:07
*/
public class MessageSynThread implements Runnable{
/** 心跳相关信息 */
private HeartBox heartBox;
public MessageSynThread(HeartBox heartBox) {
this.heartBox = heartBox;
}
@Override
public void run() {
// 轮询取同步消息
while(true){
try {
/**
同步消息 分为三个字段
同步数据:data;
同步数据类型:type -> task,resource;
同步方式: method -> add update del
*/
// receiveQueue为当前节点接收到的同步消息
String data = heartBox.getReceiveQueue().take();
JSONObject json = JSON.parseObject(data);
if(!Sets.synMap.containsKey(json.getString("type"))){
Sets.synMap.put(json.getString("type"), new HashMap<String,Object>());
}
Map<String,Object> typeMap = Sets.synMap.get(json.getString("type"));
try {
Map<String,Object> dataMap = JSON.parseObject(json.getString("data"), new HashMap<String,Object>().getClass());
ReflectUtil.processMethod(MessageSynHandle.class, json.getString("method"), typeMap,dataMap);
} catch (Exception e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
MessageSynHandle
/**
* 数据同步操作
* @ClassName MessageSynHandle
* @Description: 数据同步操作
* @author Tangjc
* @date 2019年6月12日 下午7:54:07
*/
public class MessageSynHandle {
/**
* 添加操作
* @Title: add
* @Description: 添加操作
* @author Tangjc
* @param synMap 同步集合
* @param dataMap 待同步数据
* @return void
*/
public void add(HashMap<String,Object> synMap,HashMap<String,Object> dataMap){
synMap.putAll(dataMap);
}
/**
* 修改操作
* @Title: add
* @Description: 修改操作
* @author Tangjc
* @param synMap 同步集合
* @param dataMap 待同步数据
* @return void
*/
public void update(HashMap<String,Object> synMap,HashMap<String,Object> dataMap){
synMap.putAll(dataMap);
}
/**
* 修改操作
* @Title: add
* @Description: 修改操作
* @author Tangjc
* @param synMap 同步集合
* @param dataMap 待同步数据
* @return void
*/
public void del(HashMap<String,Object> synMap,HashMap<String,Object> dataMap){
dataMap.forEach((key,value)->{
synMap.remove(key);
});
}
}
在此心跳程序和同步线程启动好后,如果有需要同步的数据,请调出heartBox.getMsgQueueMap()
// 接收该同步消息的的ip 只有建立了心跳才能同步
String ip = "192.168.1.100"
JSONObject json = new JSONObject();
// 该此同步为 添加 除此之外还有update 修改,del 删除
json.put("method", "add");
// 标识 根据业务需要来 同步过去后变为 synMap的key
json.put("type", "resource");
// 数据 规定使用map格式
Map<String,String> dataMap = new HashMap<String,String>();
dataMap.put("userId",1);
json.put("data", dataMap);
// 根据ip选择需要同步的节点
Sets.heartBox.getMsgQueueMap().get(ip).put(json.toJSONString());
心跳线程会根据每一次心跳读取队列同步消息
MessageSynThread同步线程里已经将ReceiveQueue的消息放入了synMap里这时候取出就好
String type = "resource"
Object userId = Sets.synMap.get(type).get("userId");
这里是git地址