[从源码学设计]蚂蚁金服SOFARegistry 之 ChangeNotifier

0x00 摘要

SOFARegistry 是蚂蚁金服开源的一个生产级、高时效、高可用的服务注册中心。

本系列文章重点在于分析设计和架构,即利用多篇文章,从多个角度反推总结 DataServer 或者 SOFARegistry 的实现机制和架构思路,让大家借以学习阿里如何设计。

本文为第十五篇,分析如何执行ChangeNotifier 来通知相关模块:hi,这里有新数据变化来到了,兄弟们走起来。

0x01 业务范畴

1.1 概述

当有数据发布者 publisher 上下线时,会分别触发 publishDataProcessor 或 unPublishDataHandler,Handler 会往 dataChangeEventCenter 中添加一个数据变更事件,用于异步地通知事件变更中心数据的变更。事件变更中心收到该事件之后,会往队列中加入事件。

  1. 此时 dataChangeEventCenter 会根据不同的事件类型异步地对上下线数据进行相应的处理,即把这个事件变更信息变成ChangeNotifier ,进而变成Operator,放到AbstractAcceptorStore;

  2. 与此同时 DataChangeHandler 会把这个事件变更信息通过 ChangeNotifier 对外发布,通知其他节点进行数据同步。

因为篇幅限制,前文对 ChangeNotifier 这部分只是略过,本文就详细讲解下事件变更通知ChangeNotifier。 这里会再把整理流程串起来,会涉及到前面某些文章内容。

先给出图示以便大家了解 ChangeNotifier 的作用。

  1. +--------------------+
  2. | PublishDataHandler |
  3. +--------+-----------+
  4. |
  5. |
  6. | publisher
  7. |
  8. v
  9. +---------+------------+
  10. |DataChangeEventCenter |
  11. +---------+------------+
  12. |
  13. |
  14. | ChangeData
  15. v
  16. +---------+------------+
  17. | DataChangeEventQueue |
  18. +---------+------------+
  19. |
  20. |
  21. | ChangeData
  22. v
  23. +-------+----------+
  24. | DataChangeHandler|
  25. +-------+----------+
  26. |
  27. |
  28. | ChangeData
  29. v
  30. +------+--------+ +------------+
  31. | ChangeNotifier| +--------> | datumCache |
  32. +------+--------+ +------------+
  33. |
  34. |
  35. v
  36. +---+------+
  37. | notifier |
  38. +---+------+
  39. |
  40. v
  41. +-----------+---------------+
  42. | |
  43. v v
  44. +----+----------------+ +------+----------+
  45. |SessionServerNotifier| | BackUpNotifier |
  46. +----+----------------+ +------+----------+
  47. | |
  48. | |
  49. | |
  50. | v
  51. +--v------------+ +------+----------------+
  52. | sessionServer | | AbstractAcceptorStore |
  53. +---------------+ +-----------------------+

1.2 数据变化

数据变化有两个方向

  • 数据服务器节点变化;

  • 数据的变化,即Publisher和Scriber的变化;

ChangeNotifier就是负责把 Publisher和Scriber的变化 通知给相关模块。变更通知就是一种解耦

0x02 数据结构

我们首先需要看看通知的数据结构。

2.1 接口定义

IDataChangeNotifier是通知的接口定义:

  1. public interface IDataChangeNotifier {
  2. Set<DataSourceTypeEnum> getSuitableSource();
  3. /**
  4. *
  5. * @param datum
  6. * @param lastVersion
  7. */
  8. void notify(Datum datum, Long lastVersion);
  9. }

2.2 派生类

IDataChangeNotifier 有四个派生类,分别对应了具体数据变化的四种可能,从名字大约可以判断出用途。

  1. public class BackUpNotifier implements IDataChangeNotifier
  2. public class SessionServerNotifier implements IDataChangeNotifier
  3. public class SnapshotBackUpNotifier implements IDataChangeNotifier
  4. public class TempPublisherNotifier implements IDataChangeNotifier

2.3 Bean

对应的Bean如下:

  1. @Bean(name = "dataChangeNotifiers")
  2. public List<IDataChangeNotifier> dataChangeNotifiers() {
  3. List<IDataChangeNotifier> list = new ArrayList<>();
  4. list.add(sessionServerNotifier());
  5. list.add(tempPublisherNotifier());
  6. list.add(backUpNotifier());
  7. return list;
  8. }

0x03 流程

我们从头理一下流程。

3.1 放入消息

当有数据发布者 publisher 上下线时,会分别触发 publishDataProcessor 或 unPublishDataHandler ,Handler 会往 dataChangeEventCenter 中添加一个数据变更事件,用于异步地通知事件变更中心数据的变更。事件变更中心收到该事件之后,会往队列中加入事件。

在DataServer这里,具体流程如下:

3.1.1 PublishDataHandler

PublishDataHandler 响应 PublishDataRequest。当有Publisher时候,就往DataChangeEventCenter放入消息。即调用下面来放入消息

  1. dataChangeEventCenter.onChange(publisher, dataServerConfig.getLocalDataCenter());

具体代码如下:

  1. public class PublishDataHandler extends AbstractServerHandler<PublishDataRequest> {
  2. @Autowired
  3. private ForwardService forwardService;
  4. @Autowired
  5. private SessionServerConnectionFactory sessionServerConnectionFactory;
  6. @Autowired
  7. private DataChangeEventCenter dataChangeEventCenter;
  8. @Autowired
  9. private DataServerConfig dataServerConfig;
  10. @Autowired
  11. private DatumLeaseManager datumLeaseManager;
  12. @Autowired
  13. private ThreadPoolExecutor publishProcessorExecutor;
  14. @Override
  15. public Object doHandle(Channel channel, PublishDataRequest request) {
  16. Publisher publisher = Publisher.internPublisher(request.getPublisher());
  17. if (forwardService.needForward()) {
  18. CommonResponse response = new CommonResponse();
  19. response.setSuccess(false);
  20. response.setMessage("Request refused, Server status is not working");
  21. return response;
  22. }
  23. dataChangeEventCenter.onChange(publisher, dataServerConfig.getLocalDataCenter());
  24. if (publisher.getPublishType() != PublishType.TEMPORARY) {
  25. String connectId = WordCache.getInstance().getWordCache(
  26. publisher.getSourceAddress().getAddressString());
  27. sessionServerConnectionFactory.registerConnectId(request.getSessionServerProcessId(),
  28. connectId);
  29. // record the renew timestamp
  30. datumLeaseManager.renew(connectId);
  31. }
  32. return CommonResponse.buildSuccessResponse();
  33. }
  34. }

此时具体逻辑如下:

  1. +--------------------+
  2. | PublishDataHandler |
  3. +--------+-----------+
  4. |
  5. |
  6. | publisher
  7. |
  8. v
  9. +---------+------------+
  10. |DataChangeEventCenter |
  11. +---------+------------+

3.1.2 DataChangeEventCenter

DataChangeEventCenter 的核心是一个DataChangeEventQueue数组,

DataChangeEventCenter . onChange函数会首先根据Publisher的DataInfoId获取hash,根据这个hash数值来决定把 DataChangeEvent 消息放入哪个queue来处理,就是调用这个 queue的 onChange 函数。

  1. public class DataChangeEventCenter {
  2. /**
  3. * queues of DataChangeEvent
  4. */
  5. private DataChangeEventQueue[] dataChangeEventQueues;
  6. @Autowired
  7. private DatumCache datumCache;
  8. @PostConstruct
  9. public void init() {
  10. if (isInited.compareAndSet(false, true)) {
  11. queueCount = dataServerConfig.getQueueCount();
  12. dataChangeEventQueues = new DataChangeEventQueue[queueCount];
  13. for (int idx = 0; idx < queueCount; idx++) {
  14. dataChangeEventQueues[idx] = new DataChangeEventQueue(idx, dataServerConfig, this,
  15. datumCache);
  16. dataChangeEventQueues[idx].start();
  17. }
  18. }
  19. }
  20. /**
  21. * receive changed publisher, then wrap it into the DataChangeEvent and put it into dataChangeEventQueue
  22. *
  23. * @param publisher
  24. * @param dataCenter
  25. */
  26. public void onChange(Publisher publisher, String dataCenter) {
  27. int idx = hash(publisher.getDataInfoId());
  28. Datum datum = new Datum(publisher, dataCenter);
  29. if (publisher instanceof UnPublisher) {
  30. datum.setContainsUnPub(true);
  31. }
  32. if (publisher.getPublishType() != PublishType.TEMPORARY) {
  33. dataChangeEventQueues[idx].onChange(new DataChangeEvent(DataChangeTypeEnum.MERGE,
  34. DataSourceTypeEnum.PUB, datum));
  35. } else {
  36. dataChangeEventQueues[idx].onChange(new DataChangeEvent(DataChangeTypeEnum.MERGE,
  37. DataSourceTypeEnum.PUB_TEMP, datum));
  38. }
  39. }
  40. }

DataChangeEventQueue 的主要数据成员如下:

  1. public class DataChangeEventQueue {
  2. /**
  3. * a block queue that stores all data change events
  4. */
  5. private final BlockingQueue<IDataChangeEvent> eventQueue;
  6. private final Map<String, Map<String, ChangeData>> CHANGE_DATA_MAP_FOR_MERGE = new ConcurrentHashMap<>();
  7. private final DelayQueue<ChangeData> CHANGE_QUEUE = new DelayQueue();
  8. private DataChangeEventCenter dataChangeEventCenter;
  9. private DatumCache datumCache;
  10. }

其执行引擎是一个线程,其block在 BlockingQueue eventQueue 之上,当有消息时候,就取出消息,针对消息类型做不同处理。

  1. public void start() {
  2. Executor executor = ExecutorFactory
  3. .newSingleThreadExecutor(String.format("%s_%s", DataChangeEventQueue.class.getSimpleName(), getName()));
  4. executor.execute(() -> {
  5. while (true) {
  6. try {
  7. IDataChangeEvent event = eventQueue.take();
  8. DataChangeScopeEnum scope = event.getScope();
  9. if (scope == DataChangeScopeEnum.DATUM) {
  10. DataChangeEvent dataChangeEvent = (DataChangeEvent) event;
  11. //Temporary push data will be notify as soon as,and not merge to normal pub data;
  12. if (dataChangeEvent.getSourceType() == DataSourceTypeEnum.PUB_TEMP) {
  13. addTempChangeData(dataChangeEvent.getDatum(), dataChangeEvent.getChangeType(),
  14. dataChangeEvent.getSourceType());
  15. } else {
  16. handleDatum(dataChangeEvent.getChangeType(), dataChangeEvent.getSourceType(),
  17. dataChangeEvent.getDatum());
  18. }
  19. } else if (scope == DataChangeScopeEnum.CLIENT) {
  20. handleClientOff((ClientChangeEvent) event);
  21. } else if (scope == DataChangeScopeEnum.SNAPSHOT) {
  22. handleSnapshot((DatumSnapshotEvent) event);
  23. }
  24. }
  25. }
  26. });
  27. }

对于 Publisher 消息类型,handleDatum 函数会根据changeType是 COVER 还是 MERGE 来做不同处理。

在此步骤中,也会把 ChangeData 放入 CHANGE_QUEUE.put(changeData);

  1. private void handleDatum(DataChangeTypeEnum changeType, DataSourceTypeEnum sourceType,
  2. Datum targetDatum) {
  3. lock.lock();
  4. try {
  5. //get changed datum
  6. ChangeData changeData = getChangeData(targetDatum.getDataCenter(),
  7. targetDatum.getDataInfoId(), sourceType, changeType);
  8. Datum cacheDatum = changeData.getDatum();
  9. if (changeType == DataChangeTypeEnum.COVER || cacheDatum == null) {
  10. changeData.setDatum(targetDatum);
  11. } else {
  12. Map<String, Publisher> targetPubMap = targetDatum.getPubMap();
  13. Map<String, Publisher> cachePubMap = cacheDatum.getPubMap();
  14. for (Publisher pub : targetPubMap.values()) {
  15. String registerId = pub.getRegisterId();
  16. Publisher cachePub = cachePubMap.get(registerId);
  17. if (cachePub != null) {
  18. // if the registerTimestamp of cachePub is greater than the registerTimestamp of pub, it means
  19. // that pub is not the newest data, should be ignored
  20. if (pub.getRegisterTimestamp() < cachePub.getRegisterTimestamp()) {
  21. continue;
  22. }
  23. // if pub and cachePub both are publisher, and sourceAddress of both are equal,
  24. // and version of cachePub is greater than version of pub, should be ignored
  25. if (!(pub instanceof UnPublisher) && !(cachePub instanceof UnPublisher)
  26. && pub.getSourceAddress().equals(cachePub.getSourceAddress())
  27. && cachePub.getVersion() > pub.getVersion()) {
  28. continue;
  29. }
  30. }
  31. cachePubMap.put(registerId, pub);
  32. cacheDatum.setVersion(targetDatum.getVersion());
  33. }
  34. }
  35. } finally {
  36. lock.unlock();
  37. }
  38. }

此时具体逻辑如下:

  1. +--------------------+
  2. | PublishDataHandler |
  3. +--------+-----------+
  4. |
  5. |
  6. | publisher
  7. |
  8. v
  9. +---------+------------+
  10. |DataChangeEventCenter |
  11. +---------+------------+
  12. |
  13. |
  14. | ChangeData
  15. v
  16. +---------+------------+
  17. | DataChangeEventQueue |
  18. +---------+------------+

3.2 消费消息&发送通知

DataChangeHandler 会针对每个DataChangeEventQueue进行消费通知。

  1. public class DataChangeHandler {
  2. @Autowired
  3. private DataChangeEventCenter dataChangeEventCenter;
  4. @Autowired
  5. private DatumCache datumCache;
  6. @Resource
  7. private List<IDataChangeNotifier> dataChangeNotifiers;
  8. @PostConstruct
  9. public void start() {
  10. DataChangeEventQueue[] queues = dataChangeEventCenter.getQueues();
  11. int queueCount = queues.length;
  12. Executor executor = ExecutorFactory.newFixedThreadPool(queueCount, DataChangeHandler.class.getSimpleName());
  13. Executor notifyExecutor = ExecutorFactory
  14. .newFixedThreadPool(dataServerConfig.getQueueCount() * 5, this.getClass().getSimpleName());
  15. for (int idx = 0; idx < queueCount; idx++) {
  16. final DataChangeEventQueue dataChangeEventQueue = queues[idx];
  17. final String name = dataChangeEventQueue.getName();
  18. executor.execute(() -> {
  19. while (true) {
  20. try {
  21. final ChangeData changeData = dataChangeEventQueue.take();
  22. notifyExecutor.execute(new ChangeNotifier(changeData, name));
  23. }
  24. }
  25. });
  26. }
  27. }
  28. }

3.2.1 DataChangeHandler

DataChangeHandler 会定期提取DataChangeEventCenter中的消息,然后进行处理。

3.2.2 类定义

  1. public class DataChangeHandler {
  2. @Autowired
  3. private DataServerConfig dataServerConfig;
  4. @Autowired
  5. private DataChangeEventCenter dataChangeEventCenter;
  6. @Autowired
  7. private DatumCache datumCache;
  8. @Resource
  9. private List<IDataChangeNotifier> dataChangeNotifiers;
  10. }

3.2.3 执行引擎

这里是一个双层线程模型。

  • executor = ExecutorFactory.newFixedThreadPool(queueCount)

  • notifyExecutor= ExecutorFactory.newFixedThreadPool(dataServerConfig.getQueueCount() * 5)

可以认为 executor 是控制线程,notifierExecutor是工作线程,工作线程是控制线程的5倍。

  • DataChangeHandler 会遍历 DataChangeEventCenter 中所有 DataChangeEventQueue,
  • 针对每一个dataChangeEventQueue调用executor的一个控制线程,
  • 在这个控制线程里面,可以从 DataChangeEventQueue 之中取出ChangeData,针对每一个ChangeData,调用notifyExecutor的一个工作线程,生成一个ChangeNotifier进行处理。
  1. @PostConstruct
  2. public void start() {
  3. DataChangeEventQueue[] queues = dataChangeEventCenter.getQueues();
  4. int queueCount = queues.length;
  5. Executor executor = ExecutorFactory.newFixedThreadPool(queueCount, DataChangeHandler.class.getSimpleName());
  6. Executor notifyExecutor = ExecutorFactory
  7. .newFixedThreadPool(dataServerConfig.getQueueCount() * 5, this.getClass().getSimpleName());
  8. for (int idx = 0; idx < queueCount; idx++) {
  9. final DataChangeEventQueue dataChangeEventQueue = queues[idx];
  10. final String name = dataChangeEventQueue.getName();
  11. executor.execute(() -> {
  12. while (true) {
  13. final ChangeData changeData = dataChangeEventQueue.take();
  14. notifyExecutor.execute(new ChangeNotifier(changeData, name));
  15. }
  16. });
  17. }
  18. }

3.2.4 业务执行

对于 ChangeData,会生成 ChangeNotifier 进行处理。会把这个事件变更信息通过 ChangeNotifier 对外发布,通知其他节点进行数据同步。

在 ChangeNotifier 之中,会判断changeData的类型做不同处理。

  • 如果是SnapshotData,则:

    • 生成SnapshotData;
    • 调用 datumCache.putSnapshot 做存储;
    • 调用notify做通知;
  • 如果是其他类型,则:
    • 对于pub or unPub merge,需要datum.updateVersion();
    • 如果是 PUB_TEMP,则notifyTempPub(datum, sourceType, changeType);
    • 如果是版本更新,则notify(datum, sourceType, lastVersion);

具体如下:

  1. private class ChangeNotifier implements Runnable {
  2. private ChangeData changeData;
  3. private String name;
  4. @Override
  5. public void run() {
  6. if (changeData instanceof SnapshotData) {
  7. ......
  8. } else {
  9. Datum datum = changeData.getDatum();
  10. String dataCenter = datum.getDataCenter();
  11. String dataInfoId = datum.getDataInfoId();
  12. DataSourceTypeEnum sourceType = changeData.getSourceType();
  13. DataChangeTypeEnum changeType = changeData.getChangeType();
  14. if (changeType == DataChangeTypeEnum.MERGE
  15. && sourceType != DataSourceTypeEnum.BACKUP
  16. && sourceType != DataSourceTypeEnum.SYNC) {
  17. //update version for pub or unPub merge to cache
  18. //if the version product before merge to cache,it may be cause small version override big one
  19. datum.updateVersion();
  20. }
  21. long version = datum.getVersion();
  22. try {
  23. if (sourceType == DataSourceTypeEnum.CLEAN) {
  24. if (datumCache.cleanDatum(dataCenter, dataInfoId)) {
  25. ......
  26. }
  27. } else if (sourceType == DataSourceTypeEnum.PUB_TEMP) {
  28. notifyTempPub(datum, sourceType, changeType);
  29. } else {
  30. MergeResult mergeResult = datumCache.putDatum(changeType, datum);
  31. Long lastVersion = mergeResult.getLastVersion();
  32. if (lastVersion != null
  33. && lastVersion.longValue() == LocalDatumStorage.ERROR_DATUM_VERSION) {
  34. return;
  35. }
  36. //lastVersion null means first add datum
  37. if (lastVersion == null || version != lastVersion) {
  38. if (mergeResult.isChangeFlag()) {
  39. notify(datum, sourceType, lastVersion);
  40. }
  41. }
  42. }
  43. }
  44. }
  45. }
  46. }

此时具体逻辑如下:

  1. +--------------------+
  2. | PublishDataHandler |
  3. +--------+-----------+
  4. |
  5. |
  6. | publisher
  7. |
  8. v
  9. +---------+------------+
  10. |DataChangeEventCenter |
  11. +---------+------------+
  12. |
  13. |
  14. | ChangeData
  15. v
  16. +---------+------------+
  17. | DataChangeEventQueue |
  18. +---------+------------+
  19. |
  20. |
  21. | ChangeData
  22. v
  23. +-------+----------+
  24. | DataChangeHandler|
  25. +-------+----------+
  26. |
  27. |
  28. | ChangeData
  29. v
  30. +------+--------+ +------------+
  31. | ChangeNotifier| +--------> | datumCache |
  32. +------+--------+ +------------+

3.2.5 通知

notify函数会遍历dataChangeNotifiers,找出可以支持本Datum对应SourceType的Notifier来执行。

具体如何支持哪些函数,是由getSuitableSource设置的。

  1. private void notify(Datum datum, DataSourceTypeEnum sourceType, Long lastVersion) {
  2. for (IDataChangeNotifier notifier : dataChangeNotifiers) {
  3. if (notifier.getSuitableSource().contains(sourceType)) {
  4. notifier.notify(datum, lastVersion);
  5. }
  6. }
  7. }

对应的Bean是:

  1. @Bean(name = "dataChangeNotifiers")
  2. public List<IDataChangeNotifier> dataChangeNotifiers() {
  3. List<IDataChangeNotifier> list = new ArrayList<>();
  4. list.add(sessionServerNotifier());
  5. list.add(tempPublisherNotifier());
  6. list.add(backUpNotifier());
  7. return list;
  8. }

3.2.6 BackUpNotifier同步

就是调用 syncDataService.appendOperator 进行通知,其实就是把 Datum 变成 Operator,存到AbstractAcceptorStore。

  1. public class BackUpNotifier implements IDataChangeNotifier {
  2. @Autowired
  3. private SyncDataService syncDataService;
  4. @Override
  5. public Set<DataSourceTypeEnum> getSuitableSource() {
  6. Set<DataSourceTypeEnum> set = new HashSet<>();
  7. set.add(DataSourceTypeEnum.PUB);
  8. return set;
  9. }
  10. @Override
  11. public void notify(Datum datum, Long lastVersion) {
  12. syncDataService.appendOperator(new Operator(datum.getVersion(), lastVersion, datum,
  13. DataSourceTypeEnum.BACKUP));
  14. }
  15. }

3.2.7 SessionServerNotifier通知数据变化

SessionServerNotifier 则要复杂很多。

  1. public class SessionServerNotifier implements IDataChangeNotifier {
  2. private AsyncHashedWheelTimer asyncHashedWheelTimer;
  3. @Autowired
  4. private DataServerConfig dataServerConfig;
  5. @Autowired
  6. private Exchange boltExchange;
  7. @Autowired
  8. private SessionServerConnectionFactory sessionServerConnectionFactory;
  9. @Autowired
  10. private DatumCache datumCache;
  11. @Override
  12. public Set<DataSourceTypeEnum> getSuitableSource() {
  13. Set<DataSourceTypeEnum> set = new HashSet<>();
  14. set.add(DataSourceTypeEnum.PUB);
  15. set.add(DataSourceTypeEnum.SYNC);
  16. set.add(DataSourceTypeEnum.SNAPSHOT);
  17. return set;
  18. }
  19. }
3.2.7.1 时间轮

建立了一个500毫秒的时间轮。

  1. @PostConstruct
  2. public void init() {
  3. ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder();
  4. threadFactoryBuilder.setDaemon(true);
  5. asyncHashedWheelTimer = new AsyncHashedWheelTimer(threadFactoryBuilder.setNameFormat(
  6. "Registry-SessionServerNotifier-WheelTimer").build(), 500, TimeUnit.MILLISECONDS, 1024,
  7. dataServerConfig.getSessionServerNotifierRetryExecutorThreadSize(),
  8. dataServerConfig.getSessionServerNotifierRetryExecutorQueueSize(), threadFactoryBuilder
  9. .setNameFormat("Registry-SessionServerNotifier-WheelExecutor-%d").build(),
  10. new TaskFailedCallback() {
  11. @Override
  12. public void executionRejected(Throwable e) {
  13. LOGGER.error("executionRejected: " + e.getMessage(), e);
  14. }
  15. @Override
  16. public void executionFailed(Throwable e) {
  17. LOGGER.error("executionFailed: " + e.getMessage(), e);
  18. }
  19. });
  20. }

从业务角度看,当有publisher相关消息来临时候,

DataChangeHandler的notify函数会遍历dataChangeNotifiers,找出可以支持本Datum对应SourceType的Notifier来执行。

  1. private void notify(Datum datum, DataSourceTypeEnum sourceType, Long lastVersion) {
  2. for (IDataChangeNotifier notifier : dataChangeNotifiers) {
  3. if (notifier.getSuitableSource().contains(sourceType)) {
  4. notifier.notify(datum, lastVersion);
  5. }
  6. }
  7. }

到了SessionServerNotifier这里的notify函数,会遍历目前缓存的所有Connection,逐一通知。

  1. @Override
  2. public void notify(Datum datum, Long lastVersion) {
  3. DataChangeRequest request = new DataChangeRequest(datum.getDataInfoId(),
  4. datum.getDataCenter(), datum.getVersion());
  5. List<Connection> connections = sessionServerConnectionFactory.getSessionConnections();
  6. for (Connection connection : connections) {
  7. doNotify(new NotifyCallback(connection, request));
  8. }
  9. }

具体通知函数:

  1. private void doNotify(NotifyCallback notifyCallback) {
  2. Connection connection = notifyCallback.connection;
  3. DataChangeRequest request = notifyCallback.request;
  4. try {
  5. //check connection active
  6. if (!connection.isFine()) {
  7. return;
  8. }
  9. Server sessionServer = boltExchange.getServer(dataServerConfig.getPort());
  10. sessionServer.sendCallback(sessionServer.getChannel(connection.getRemoteAddress()),
  11. request, notifyCallback, dataServerConfig.getRpcTimeout());
  12. } catch (Exception e) {
  13. onFailed(notifyCallback);
  14. }
  15. }

而时间轮是在调用失败的重试中使用。

就是当没有达到失败重试最大次数时,进行定时重试。

  1. private void onFailed(NotifyCallback notifyCallback) {
  2. DataChangeRequest request = notifyCallback.request;
  3. Connection connection = notifyCallback.connection;
  4. notifyCallback.retryTimes++;
  5. //check version, if it's fall behind, stop retry
  6. long _currentVersion = datumCache.get(request.getDataCenter(), request.getDataInfoId()).getVersion();
  7. if (request.getVersion() != _currentVersion) {
  8. return;
  9. }
  10. if (notifyCallback.retryTimes <= dataServerConfig.getNotifySessionRetryTimes()) {
  11. this.asyncHashedWheelTimer.newTimeout(timeout -> {
  12. //check version, if it's fall behind, stop retry
  13. long currentVersion = datumCache.get(request.getDataCenter(), request.getDataInfoId()).getVersion();
  14. if (request.getVersion() == currentVersion) {
  15. doNotify(notifyCallback);
  16. }
  17. }, getDelayTimeForRetry(notifyCallback.retryTimes), TimeUnit.MILLISECONDS);
  18. }
  19. }

具体逻辑如下:

  1. +--------------------+
  2. | PublishDataHandler |
  3. +--------+-----------+
  4. |
  5. |
  6. | publisher
  7. |
  8. v
  9. +---------+------------+
  10. |DataChangeEventCenter |
  11. +---------+------------+
  12. |
  13. |
  14. | ChangeData
  15. v
  16. +---------+------------+
  17. | DataChangeEventQueue |
  18. +---------+------------+
  19. |
  20. |
  21. | ChangeData
  22. v
  23. +-------+----------+
  24. | DataChangeHandler|
  25. +-------+----------+
  26. |
  27. |
  28. | ChangeData
  29. v
  30. +------+--------+ +------------+
  31. | ChangeNotifier| +--------> | datumCache |
  32. +------+--------+ +------------+
  33. |
  34. |
  35. v
  36. +---+------+
  37. | notifier |
  38. +---+------+
  39. |
  40. v
  41. +-----------+---------------+
  42. | |
  43. v v
  44. +----+----------------+ +------+----------+
  45. |SessionServerNotifier| | BackUpNotifier |
  46. +----+----------------+ +------+----------+
  47. | |
  48. | |
  49. | |
  50. | v
  51. +--v------------+ +------+----------------+
  52. | sessionServer | | AbstractAcceptorStore |
  53. +---------------+ +-----------------------+

0x04 总结

本文是把注册中的一个点“事件变更通知ChangeNotifie“进行细化展开,以 SessionServerNotifier 和 BackUpNotifier 为例,为大家进行解释ChangeNotifier的原理和使用。把包括 dataChangeEventCenter 等功能也梳理了一下,希望对大家有所帮助。

在 DataServer,数据变化有两个方向:

  • 数据服务器节点变化;

  • 数据的变化,即 Publisher 和 Scriber 的变化;

ChangeNotifier就是负责把 Publisher 和 Scriber 的变化 通知给相关模块。变更通知就是一种解耦

0xFF 参考

[从源码学设计]蚂蚁金服SOFARegistry之服务上线

[从源码学设计]蚂蚁金服SOFARegistry 之 服务注册和操作日志

[从源码学设计]蚂蚁金服SOFARegistry 之 ChangeNotifier的更多相关文章

  1. [从源码学设计]蚂蚁金服SOFARegistry之程序基本架构

    [从源码学设计]蚂蚁金服SOFARegistry之程序基本架构 0x00 摘要 之前我们通过三篇文章初步分析了 MetaServer 的基本架构,MetaServer 这三篇文章为我们接下来的工作做了 ...

  2. [从源码学设计]蚂蚁金服SOFARegistry之网络封装和操作

    [从源码学设计]蚂蚁金服SOFARegistry之网络封装和操作 目录 [从源码学设计]蚂蚁金服SOFARegistry之网络封装和操作 0x00 摘要 0x01 业务领域 1.1 SOFARegis ...

  3. [从源码学设计]蚂蚁金服SOFARegistry网络操作之连接管理

    [从源码学设计]蚂蚁金服SOFARegistry网络操作之连接管理 目录 [从源码学设计]蚂蚁金服SOFARegistry网络操作之连接管理 0x00 摘要 0x01 业务领域 1.1 应用场景 0x ...

  4. [从源码学设计]蚂蚁金服SOFARegistry之消息总线

    [从源码学设计]蚂蚁金服SOFARegistry之消息总线 目录 [从源码学设计]蚂蚁金服SOFARegistry之消息总线 0x00 摘要 0x01 相关概念 1.1 事件驱动模型 1.1.1 概念 ...

  5. [从源码学设计]蚂蚁金服SOFARegistry之消息总线异步处理

    [从源码学设计]蚂蚁金服SOFARegistry之消息总线异步处理 目录 [从源码学设计]蚂蚁金服SOFARegistry之消息总线异步处理 0x00 摘要 0x01 为何分离 0x02 业务领域 2 ...

  6. [从源码学设计]蚂蚁金服SOFARegistry之存储结构

    [从源码学设计]蚂蚁金服SOFARegistry之存储结构 目录 [从源码学设计]蚂蚁金服SOFARegistry之存储结构 0x00 摘要 0x01 业务范畴 1.1 缓存 1.2 DataServ ...

  7. [从源码学设计]蚂蚁金服SOFARegistry之推拉模型

    [从源码学设计]蚂蚁金服SOFARegistry之推拉模型 目录 [从源码学设计]蚂蚁金服SOFARegistry之推拉模型 0x00 摘要 0x01 相关概念 1.1 推模型和拉模型 1.1.1 推 ...

  8. [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用

    [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用 目录 [从源码学设计]蚂蚁金服SOFARegistry之时间轮的使用 0x00 摘要 0x01 业务领域 1.1 应用场景 0x02 定 ...

  9. [从源码学设计]蚂蚁金服SOFARegistry 之 自动调节间隔周期性任务

    [从源码学设计]蚂蚁金服SOFARegistry 之 自动调节间隔周期性任务 目录 [从源码学设计]蚂蚁金服SOFARegistry 之 自动调节间隔周期性任务 0x00 摘要 0x01 业务领域 0 ...

随机推荐

  1. oracle 11g打补丁错误(Missing command :fuser)

    在给oracle 11g数据库打补丁的时候出现以下错误: [oracle@node01 31537677]$ $ORACLE_HOME/OPatch/opatch apply Oracle Inter ...

  2. vue 分支结构

    分支循环结构 分支循环结构指令 v-if v-else v-else-if v-show v-if 指令 可以直接在元素中添加指令,添加判断的值 最后运行可以得到结果是:  v-show v-show ...

  3. element Cascader 多选 点击文字选中

    html 部分 1 <el-form-item label="A部署位置" > 2 <el-cascader 3 v-model="itemType.a ...

  4. vue第八单元(组件通信 子父,父子组件通信 自定义事件 事件修饰符 v-model props验证 )

    第八单元(组件通信 子父,父子组件通信 自定义事件 事件修饰符 v-model props验证 ) #课程目标 掌握使用props让父组件给子组件传参(重点) 掌握props属性的使用以及prop验证 ...

  5. Flink开发中的问题

    1. 流与批处理的区别 流处理系统 流处理系统,其节点间数据传输的标准模型是:当一条数据被处理完成后,序列化到缓存中,然后立刻通过网络传输到下一个节点,由下一个节点继续处理. 批处理系统 批处理系统, ...

  6. SQL注入fuzz字典

    length Length + handler likeLiKe selectSeleCT sleepSLEEp databaseDATABASe delete having oroR asAs -~ ...

  7. Linux下安装ffmpeg,视频格式转换

    下载ffmpeg 从ffmpeg官网:http://ffmpeg.org/download.html 下载最新的ffmpeg安装包,然后通过如下命令解压: 解压 ffmpeg-*.tar.bz2 文件 ...

  8. 记一次真实的webpack优化经历

    前言 公司目前现有的一款产品是使用vue v2.0框架实现的,配套的打包工具为webpack v3.0.整个项目大概有80多个vue文件,也算不上什么大型项目. 只不过每次头疼的就是打包所耗费的时间平 ...

  9. openstack高可用集群17-openstack集成Ceph准备

    Openstack集成Ceph准备 Openstack环境中,数据存储可分为临时性存储与永久性存储. 临时性存储:主要由本地文件系统提供,并主要用于nova虚拟机的本地系统与临时数据盘,以及存储gla ...

  10. Python 中的运算符重载

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理 一种运算符对于不同类型的对象,有不同的使用方式.例如, + 用于整型对象,表示两个数相加:用于字符串 ...