Java Cancer 实现心跳机制的数据同步框架

背景介绍

项目上需要用到一个类似与zookeeper的心跳加节点数据同步的功能,但由于项目上不使用zookeeper,便决定自己实现一套,以便以后有类似需求可以直接使用。
这是一个可以处理节点间心跳和基于心跳进行数据同步的框架,此框架重在心跳实现,如果要实现数据同步需要人工处理,下面会说明如何基于此框架实现数据同步。

数据流转图

Java Cancer 实现心跳机制的数据同步框架_第1张图片

  • msgQueue: 预同步数据队列
  • receiveQueue: 已同步数据队列
  • ackMap: 消息确认机制

结构介绍

  • tonatyw.heart.cancer
    • base 实现协议需要继承的抽象类包,目前只实现了udp协议
      AbstractAgreement 协议抽象类

    • constants 常量类包
      Constants 常量类,存储一些文件路径和共用的字符串

    • handle 接收参数时的处理包

      • base 消息处理基类包
        BaseHandle 消息处理基类

      AckHandle 在发送方接收到接收方返回的回执消息时调用
      ReceiveHandle 在接收方接收到发送方发送的消息时调用

    • thread 线程包
      Receive 接收线程
      Send 发送线程
      StatusThread 节点状态更新线程

    • util 工具包
      HeartBox 心跳盒子,存储心跳期间需要使用的变量
      ProcessEngine 暂不用,留着
      Udp udp协议实现,udp心跳入口

数据流

  • msgQueue: 预同步数据队列
  • receiveQueue: 已同步数据队列
  • ackMap: 消息确认机制

使用

启动心跳

启动心跳线程前,需要将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");

下一次更新计划

  • 基于用户实现数据同步
  • 增加http协议心跳同步
  • 修复bug(如果有- -)
    由于工作关系,看反响如何(如果有…),随缘更新

这里是git地址

你可能感兴趣的:(心跳)