一致性协议算法Distro阿里自己的创的算法吧,网上能找到的资料很少。Distro用于处理ephemeral类型数据
- Distro协议算法看代码大体流程是:
- nacos启动首先从其他远程节点同步全部数据
- nacos每个节点是平等的都可以处理写入请求,同时把新数据同步到其他节点
- 每个节点只负责部分数据,定时发送自己负责数据校验值到其他节点来保持数据一致性
Distro代码分析, nacos负责数据一致性的服务是ConsistencyService接口,DistroConsistencyServiceImpl是EphemeralConsistencyService和ConsistencyService实现类。
Distro协议算法数据存储实现笔记简单,DataStore是存储实现类临时节点的数据都存储在ConcurrentHashMap里面
/** * Store of data * * @author nkorange * @since 1.0.0 */@Componentpublic class DataStore { private Map<String, Datum> dataMap = new ConcurrentHashMap<>(1024); public void put(String key, Datum value) { dataMap.put(key, value); } public Datum remove(String key) { return dataMap.remove(key); } public Set<String> keys() { return dataMap.keySet(); } public Datum get(String key) { return dataMap.get(key); } public boolean contains(String key) { return dataMap.containsKey(key); } public Map<String, Datum> batchGet(List<String> keys) { Map<String, Datum> map = new HashMap<>(128); for (String key : keys) { Datum datum = dataMap.get(key); if (datum == null) { continue; } map.put(key, datum); } return map; } public int getInstanceCount() { int count = 0; for (Map.Entry<String, Datum> entry : dataMap.entrySet()) { try { Datum instancesDatum = entry.getValue(); if (instancesDatum.value instanceof Instances) { count += ((Instances) instancesDatum.value).getInstanceList().size(); } } catch (Exception ignore) { } } return count; } public Map<String, Datum> getDataMap() { return dataMap; }} nacos启动首先从其他节点同步全部数据
DistroConsistencyServiceImpl类构造完成后启动同步线程直到首次同步成功
@org.springframework.stereotype.Service("distroConsistencyService")public class DistroConsistencyServiceImpl implements EphemeralConsistencyService { private volatile Notifier notifier = new Notifier(); private LoadDataTask loadDataTask = new LoadDataTask(); @PostConstruct public void init() { GlobalExecutor.submit(loadDataTask); GlobalExecutor.submitDistroNotifyTask(notifier); } /** * 从其它节点同步数据任务线程 */ private class LoadDataTask implements Runnable { @Override public void run() { try { load(); if (!initialized) { GlobalExecutor.submit(this, globalConfig.getLoadDataRetryDelayMillis()); } } catch (Exception e) { Loggers.DISTRO.error("load data failed.", e); } } } /** * 初始化时从其它节点同步数据 * @throws Exception */ public void load() throws Exception { //如果单列模式则返回 if (SystemUtils.STANDALONE_MODE) { initialized = true; return; } // size = 1 means only myself in the list, we need at least one another server alive: while (serverListManager.getHealthyServers().size() <= 1) { Thread.sleep(1000L); Loggers.DISTRO.info("waiting server list init..."); } //只要有一个节点同步成功则返回 for (Server server : serverListManager.getHealthyServers()) { if (NetUtils.localServer().equals(server.getKey())) { continue; } // try sync data from remote server: if (syncAllDataFromRemote(server)) { initialized = true; return; } } } /** * 从指定nacos节点同步数据 * @param server 目标nacos节点 * @return 是否同步成功 */ public boolean syncAllDataFromRemote(Server server) { try { byte[] data = NamingProxy.getAllData(server.getKey()); processData(data); return true; } catch (Exception e) { return false; } } /** * 处理同步报文信息 * 更新数据存储和通知监听器 * @param data * @param data * @throws Exception */ public void processData(byte[] data) throws Exception { if (data.length > 0) { Map<String, Datum<Instances>> datumMap = serializer.deserializeMap(data, Instances.class); for (Map.Entry<String, Datum<Instances>> entry : datumMap.entrySet()) { dataStore.put(entry.getKey(), entry.getValue()); if (!listeners.containsKey(entry.getKey())) { // pretty sure the service not exist: if (switchDomain.isDefaultInstanceEphemeral()) { // create empty service Loggers.DISTRO.info("creating service {}", entry.getKey()); Service service = new Service(); String serviceName = KeyBuilder.getServiceName(entry.getKey()); String namespaceId = KeyBuilder.getNamespace(entry.getKey()); service.setName(serviceName); service.setNamespaceId(namespaceId); service.setGroupName(Constants.DEFAULT_GROUP); // now validate the service. if failed, exception will be thrown service.setLastModifiedMillis(System.currentTimeMillis()); service.recalculateChecksum(); listeners.get(KeyBuilder.SERVICE_META_KEY_PREFIX).get(0).onChange(KeyBuilder.buildServiceMetaKey(namespaceId, serviceName), service); } } } for (Map.Entry<String, Datum<Instances>> entry : datumMap.entrySet()) { if (!listeners.containsKey(entry.getKey())) { // Should not happen: continue; } try { for (RecordListener listener : listeners.get(entry.getKey())) { listener.onChange(entry.getKey(), entry.getValue().value); } } catch (Exception e) { continue; } // Update data store if listener executed successfully: dataStore.put(entry.getKey(), entry.getValue()); } } }}public class NamingProxy { /** * 获取指定nacos节点所有数据 * @param server 目标nacos节点 * @return 返回到数据 * @throws Exception */ public static byte[] getAllData(String server) throws Exception { Map<String, String> params = new HashMap<>(8); / HttpClient.HttpResult result = HttpClient.httpGet("http://" + server + RunningConfig.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT + ALL_DATA_GET_URL, new ArrayList<>(), params); if (HttpURLConnection.HTTP_OK == result.code) { return result.content.getBytes(); } throw new IOException("failed to req API: " + "http://" + server + RunningConfig.getContextPath() + UtilsAndCommons.NACOS_NAMING_CONTEXT + DATA_GET_URL + ". code: " + result.code + " msg: " + result.content); }}Distro两种数据的同步方式:1.定时同步 2.本节点接收到服务变更同步
DataSyncer启动定时同步线程和负责数据同步
TaskDispatcher负责接受服务变更后分发到DataSyncer即时同步
@Component@DependsOn("serverListManager")public class DataSyncer { @PostConstruct //启动定时同步的定时任务 public void init() { startTimedSync(); } public void startTimedSync() { //每5秒一次 //dataSyncExecutor.scheduleWithFixedDelay(runnable, PARTITION_DATA_TIMED_SYNC_INTERVAL=5000, PARTITION_DATA_TIMED_SYNC_INTERVAL=5000, TimeUnit.MILLISECONDS); GlobalExecutor.schedulePartitionDataTimedSync(new TimedSync()); } public class TimedSync implements Runnable { @Override public void run() { try { // send local timestamps to other servers: Map<String, String> keyChecksums = new HashMap<>(64); for (String key : dataStore.keys()) { //只同步自己负责的那部分数据 // int index = healthyList.indexOf(NetUtils.localServer()); // int target = distroHash(serviceName) % healthyList.size(); if (!distroMapper.responsible(KeyBuilder.getServiceName(key))) { continue; } Datum datum = dataStore.get(key); if (datum == null) { continue; } keyChecksums.put(key, datum.value.getChecksum()); } if (keyChecksums.isEmpty()) { return; } for (Server member : getServers()) { if (NetUtils.localServer().equals(member.getKey())) { continue; } //请求路径/nacos/v1/ns/distro/checksum NamingProxy.syncCheckSums(keyChecksums, member.getKey()); } } catch (Exception e) { Loggers.DISTRO.error("timed sync task failed.", e); } } }}TaskDispatcher负责接受服务变更后分发到DataSyncer即时同步
/** * Data sync task dispatcher * * @author nkorange * @since 1.0.0 */@Componentpublic class TaskDispatcher { @Autowired private GlobalConfig partitionConfig; @Autowired private DataSyncer dataSyncer; private List<TaskScheduler> taskSchedulerList = new ArrayList<>(); private final int cpuCoreCount = Runtime.getRuntime().availableProcessors(); @PostConstruct public void init() { for (int i = 0; i < cpuCoreCount; i++) { TaskScheduler taskScheduler = new TaskScheduler(i); taskSchedulerList.add(taskScheduler); GlobalExecutor.submitTaskDispatch(taskScheduler); } } /** * 接收服务变更后同步任务 * @param key 服务唯一标识 */ public void addTask(String key) { taskSchedulerList.get(UtilsAndCommons.shakeUp(key, cpuCoreCount)).addTask(key); } public class TaskScheduler implements Runnable { private int index; private int dataSize = 0; private long lastDispatchTime = 0L; private BlockingQueue<String> queue = new LinkedBlockingQueue<>(128 * 1024); public TaskScheduler(int index) { this.index = index; } public void addTask(String key) { queue.offer(key); } public int getIndex() { return index; } @Override public void run() { List<String> keys = new ArrayList<>(); while (true) { try { String key = queue.poll(partitionConfig.getTaskDispatchPeriod(), TimeUnit.MILLISECONDS); if (dataSyncer.getServers() == null || dataSyncer.getServers().isEmpty()) { continue; } if (StringUtils.isBlank(key)) { continue; } if (dataSize == 0) { keys = new ArrayList<>(); } keys.add(key); dataSize++; //批量同步; 1 数量达到batchSyncKeyCount=1000,2:距上次同步事件大于taskDispatchPeriod=2000 if (dataSize == partitionConfig.getBatchSyncKeyCount() || (System.currentTimeMillis() - lastDispatchTime) > partitionConfig.getTaskDispatchPeriod()) { for (Server member : dataSyncer.getServers()) { if (NetUtils.localServer().equals(member.getKey())) { continue; } SyncTask syncTask = new SyncTask(); syncTask.setKeys(keys); syncTask.setTargetServer(member.getKey()); //分发任务到dataSyncer同步数据 dataSyncer.submit(syncTask, 0); } lastDispatchTime = System.currentTimeMillis(); dataSize = 0; } } catch (Exception e) { Loggers.DISTRO.error("dispatch sync task failed.", e); } } } }}@Component@DependsOn("serverListManager")public class DataSyncer { /** * * @param task 同步数据 * @param delay 延迟时间 */ public void submit(SyncTask task, long delay) { //各种监测 .................. GlobalExecutor.submitDataSync(() -> { // 1. check the server .................. // 2. get the datums by keys and check the datum is empty or not .................. byte[] data = serializer.serialize(datumMap); long timestamp = System.currentTimeMillis(); // DATA_ON_SYNC_URL = "/nacos/v1/ns/distro/datum" boolean success = NamingProxy.syncData(data, task.getTargetServer()); .............. //不成功重试 }, delay); }}原文转载:http://www.shaoqun.com/a/489725.html
grab:https://www.ikjzd.com/w/841
笨鸟转运:https://www.ikjzd.com/w/1550
美森:https://www.ikjzd.com/w/1693
一致性协议算法Distro阿里自己的创的算法吧,网上能找到的资料很少。Distro用于处理ephemeral类型数据Distro协议算法看代码大体流程是:nacos启动首先从其他远程节点同步全部数据nacos每个节点是平等的都可以处理写入请求,同时把新数据同步到其他节点每个节点只负责部分数据,定时发送自己负责数据校验值到其他节点来保持数据一致性Distro代码分析,nacos负责数据一致性的服务是
法瑞儿:https://www.ikjzd.com/w/412
cares:https://www.ikjzd.com/w/1302
2020年春节深圳欢乐谷有什么优惠活动?:http://tour.shaoqun.com/a/39761.html
亚马逊联手拼多多,是长远谋划还是无奈之举?:https://www.ikjzd.com/home/111600
云南普洱里坎瀑布自驾游怎么走?:http://tour.shaoqun.com/a/43122.html