[从源码学设计]蚂蚁金服SOFARegistry之延迟操作

0x00 摘要

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

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

本文为第十七篇,介绍SOFARegistry的延迟操作。

0x01 业务领域

1.1 业务缘由

为什么要有AfterWorkingProcess?

AfterWorkingProcess 的作用是延迟操作。猜测大致是因为某些情况下,无法执行业务,只能在后续时机进行弥补。

在官方博客有类似论述也支持我们的判断 :

在数据未同步完成之前,所有对新节点的读数据操作,将转发到拥有该数据分片的数据节点。

在数据未同步完成之前,禁止对新节点的写数据操作,防止在数据同步过程中出现新的数据不一致情况。

1.2 学习方向

可以看到类似这种业务上延迟操作应该如何实现。

0x02 实现

2.1 定义

接口定义如下:

  1. public interface AfterWorkingProcess {
  2. void afterWorkingProcess();
  3. int getOrder();
  4. }

2.2 配置

这个 afterWorkProcessors 会作为 AfterWorkingProcessHandler 的成员变量进行处理。用于处理一些业务逻辑结束后的处理动作。

  1. @Bean(name = "afterWorkProcessors")
  2. public List<AfterWorkingProcess> afterWorkingProcessors() {
  3. List<AfterWorkingProcess> list = new ArrayList<>();
  4. list.add(renewDatumHandler());
  5. list.add(datumLeaseManager());
  6. list.add(disconnectEventHandler());
  7. list.add(notifyDataSyncHandler());
  8. return list;
  9. }
  10. @Bean
  11. public AfterWorkingProcessHandler afterWorkingProcessHandler() {
  12. return new AfterWorkingProcessHandler();
  13. }

2.3 引擎

这里用法比较少见。AfterWorkingProcessHandler 也是 AfterWorkingProcess 的实现类

在其 afterWorkingProcess 函数中,会对 Bean afterWorkingProcessors 中间注册的实现类一一调用其 afterWorkingProcess 业务函数。

其中,getOrder 会指定执行优先级,这是一个常见套路。

  1. public class AfterWorkingProcessHandler implements AfterWorkingProcess {
  2. @Resource(name = "afterWorkProcessors")
  3. private List<AfterWorkingProcess> afterWorkingProcessors;
  4. @Override
  5. public void afterWorkingProcess() {
  6. if(afterWorkingProcessors != null){
  7. List<AfterWorkingProcess> list = afterWorkingProcessors.stream().sorted(Comparator.comparing(AfterWorkingProcess::getOrder)).collect(Collectors.toList());
  8. list.forEach(AfterWorkingProcess::afterWorkingProcess);
  9. }
  10. }
  11. @Override
  12. public int getOrder() {
  13. return 0;
  14. }
  15. }

2.4 调用

只有在 DataServerCache # updateDataServerStatus 函数中有调用:

  1. afterWorkingProcessHandler.afterWorkingProcess();

而在 DataServerCache 中有如下函数都会调用到 updateDataServerStatus:

  • synced
  • notifiedAll
  • checkAndUpdateStatus
  • addNotWorkingServer

图示如下:

  1. +------------------------------------------+
  2. | DataServerCache | +----------------------------------------------+
  3. | | | AfterWorkingProcess |
  4. | synced +----------------------+ | | |
  5. | | | +----------------------------+ | +------------------------------------------+ |
  6. | | | | AfterWorkingProcessHandler | | |renewDatumHandler.afterWorkingProcess | |
  7. | | | | | | | | |
  8. | v | | | | |datumLeaseManager.afterWorkingProcess | |
  9. | notifiedAll +--->updateDataServerStatus +------> afterWorkingProcess +------>+ | |
  10. | ^ ^ | | | | |disconnectEventHandler.afterWorkingProcess| |
  11. | | | | +----------------------------+ | | | |
  12. | | | | | |notifyDataSyncHandler.afterWorkingProcess | |
  13. | checkAndUpdateStatus+-----------+ | | | +------------------------------------------+ |
  14. | | | +----------------------------------------------+
  15. | addNotWorkingServer +---------------+ |
  16. | |
  17. +------------------------------------------+

手机如下:

因为是业务关联,所以不需要什么定时,异步之类。

2.5 业务实现

2.5.1 DisconnectEventHandler

  1. public class DisconnectEventHandler implements InitializingBean, AfterWorkingProcess {
  2. /**
  3. * a DelayQueue that contains client disconnect events
  4. */
  5. private final DelayQueue<DisconnectEvent> EVENT_QUEUE = new DelayQueue<>();
  6. @Autowired
  7. private SessionServerConnectionFactory sessionServerConnectionFactory;
  8. @Autowired
  9. private DataChangeEventCenter dataChangeEventCenter;
  10. @Autowired
  11. private DataServerConfig dataServerConfig;
  12. @Autowired
  13. private DataNodeStatus dataNodeStatus;
  14. private static final int BLOCK_FOR_ALL_SYNC = 5000;
  15. private static final BlockingQueue<DisconnectEvent> noWorkQueue = new LinkedBlockingQueue<>();
  16. }

在receive的正常业务操作中,如果发现本身状态不是 WORKING,则把event放入 BlockingQueue 之中。

  1. public void receive(DisconnectEvent event) {
  2. if (event.getType() == DisconnectTypeEnum.SESSION_SERVER) {
  3. SessionServerDisconnectEvent sessionServerDisconnectEvent = (SessionServerDisconnectEvent) event;
  4. sessionServerDisconnectEvent.getProcessId());
  5. } else if (event.getType() == DisconnectTypeEnum.CLIENT) {
  6. ClientDisconnectEvent clientDisconnectEvent = (ClientDisconnectEvent) event;
  7. }
  8. if (dataNodeStatus.getStatus() != LocalServerStatusEnum.WORKING) {
  9. noWorkQueue.add(event);
  10. return;
  11. }
  12. EVENT_QUEUE.add(event);
  13. }

当时机来到时候,系统再次调用afterWorkingProcess。这里会始终Block在noWorkQueue上,如果不为空,则会执行请求。

  1. public void afterWorkingProcess() {
  2. try {
  3. /*
  4. * After the snapshot data is synchronized during startup, it is queued and then placed asynchronously into
  5. * DatumCache. When the notification becomes WORKING, there may be data in the queue that is not executed
  6. * to DatumCache. So it need to sleep for a while.
  7. */
  8. TimeUnit.MILLISECONDS.sleep(BLOCK_FOR_ALL_SYNC);
  9. while (!noWorkQueue.isEmpty()) {
  10. DisconnectEvent event = noWorkQueue.poll(1, TimeUnit.SECONDS);
  11. if (event != null) {
  12. receive(event);
  13. }
  14. }
  15. }
  16. }

图示如下:

  1. +----------------------------------------------------------+
  2. | DisconnectEventHandler |
  3. | +-------------------------+ |
  4. | | receive | |
  5. | | | NOT WORKING |
  6. | | dataNodeStatus.getStatus+---------------+ |
  7. | | + | | |
  8. | | | WORKING | | add |
  9. | | | | | |
  10. | | v | | |
  11. | | EVENT_QUEUE.add(event) | | |
  12. | | | +---v---------+ |
  13. | +-------------------------+ | | |
  14. | | noWorkQueue | |
  15. | | | |
  16. | +-----------------------+ +-----+-------+ |
  17. | | afterWorkingProcess | | |
  18. | | | | poll |
  19. | | | NOT isEmpty | |
  20. | | receive(event) <----------------------+ |
  21. | | | |
  22. | | | |
  23. | +-----------------------+ |
  24. +----------------------------------------------------------+

2.5.2 NotifyDataSyncHandler

DisconnectEventHandler 和 NotifyDataSyncHandler 的实现类似。

依托一个 LinkedBlockingQueue 做缓存queue。

  1. public class NotifyDataSyncHandler extends AbstractClientHandler<NotifyDataSyncRequest> implements AfterWorkingProcess {
  2. private static final BlockingQueue<SyncDataRequestForWorking> noWorkQueue = new LinkedBlockingQueue<>();
  3. }

在doHandle的正常业务操作中,如果发现本身状态不是 WORKING,则用业务逻辑SyncDataRequestForWorking 构建一个消息 SyncDataRequestForWorking,放入 LinkedBlockingQueue 之中。

  1. @Override
  2. public Object doHandle(Channel channel, NotifyDataSyncRequest request) {
  3. final Connection connection = ((BoltChannel) channel).getConnection();
  4. if (dataNodeStatus.getStatus() != LocalServerStatusEnum.WORKING) {
  5. noWorkQueue.add(new SyncDataRequestForWorking(connection, request));
  6. return CommonResponse.buildSuccessResponse();
  7. }
  8. executorRequest(connection, request);
  9. return CommonResponse.buildSuccessResponse();
  10. }

当时机来到时候,系统再次调用afterWorkingProcess。这里会始终Block在noWorkQueue上,如果不为空,则会执行请求。

  1. @Override
  2. public void afterWorkingProcess() {
  3. while (!noWorkQueue.isEmpty()) {
  4. SyncDataRequestForWorking event = noWorkQueue.poll(1, TimeUnit.SECONDS);
  5. if (event != null) {
  6. executorRequest(event.getConnection(), event.getRequest());
  7. }
  8. }
  9. }
  10. }

图示如下:

  1. +----------------------------------------------------------+
  2. | NotifyDataSyncHandler |
  3. | +-------------------------+ |
  4. | | doHandle | |
  5. | | | NOT WORKING |
  6. | | dataNodeStatus.getStatus+---------------+ |
  7. | | + | | |
  8. | | | WORKING | | add |
  9. | | | | | |
  10. | | v | | |
  11. | | executorRequest | | |
  12. | | | +---v---------+ |
  13. | +-------------------------+ | | |
  14. | | noWorkQueue | |
  15. | | | |
  16. | +-----------------------+ +-----+-------+ |
  17. | | afterWorkingProcess | | |
  18. | | | | poll |
  19. | | | NOT isEmpty | |
  20. | | executorRequest <----------------------+ |
  21. | | | |
  22. | | | |
  23. | +-----------------------+ |
  24. +----------------------------------------------------------+

2.5.3 RenewDatumHandler

RenewDatumHandler 同 DatumLeaseManager 这两者很类似。并没有使用queue,只是提交一个线程。

其实现目的在注释中写的很清楚:

  1. /* * After the snapshot data is synchronized during startup, it is queued and then placed asynchronously into * DatumCache. When the notification becomes WORKING, there may be data in the queue that is not executed * to DatumCache. So it need to sleep for a while. */

但是细节又有所不同,这两个类是同一个作者,怀疑此君在实验比较两种不同实现方式。

RenewDatumHandler 基于 ThreadPoolExecutorDataServer 来实现。

  1. public class RenewDatumHandler extends AbstractServerHandler<RenewDatumRequest> implements
  2. AfterWorkingProcess {
  3. @Autowired
  4. private ThreadPoolExecutor renewDatumProcessorExecutor;
  5. }

renewDatumProcessorExecutor 是一个Bean,具体代码如下,ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,按FIFO原则进行排序。

  1. @Bean(name = "renewDatumProcessorExecutor")
  2. public ThreadPoolExecutor renewDatumProcessorExecutor(DataServerConfig dataServerConfig) {
  3. return new ThreadPoolExecutorDataServer("RenewDatumProcessorExecutor",
  4. dataServerConfig.getRenewDatumExecutorMinPoolSize(),
  5. dataServerConfig.getRenewDatumExecutorMaxPoolSize(), 300, TimeUnit.SECONDS,
  6. new ArrayBlockingQueue<>(dataServerConfig.getRenewDatumExecutorQueueSize()),
  7. new NamedThreadFactory("DataServer-RenewDatumProcessor-executor", true));
  8. }

ThreadPoolExecutorDataServer 主要代码如下,就是简单继承了ThreadPoolExecutor,估计这里后续会有新功能添加,现在只是占坑:

  1. public class ThreadPoolExecutorDataServer extends ThreadPoolExecutor {
  2. @Override
  3. public void execute(Runnable command) {
  4. super.execute(command);
  5. }
  6. }

对于afterWorkingProcess,就是提交了一个线程,其业务是:等待一段时间,然后设置renewEnabled。

  1. @Override
  2. public void afterWorkingProcess() {
  3. renewDatumProcessorExecutor.submit(() -> {
  4. TimeUnit.MILLISECONDS.sleep(dataServerConfig.getRenewEnableDelaySec());
  5. renewEnabled.set(true);
  6. });
  7. }

0xFF 参考

蚂蚁金服服务注册中心如何实现 DataServer 平滑扩缩容

蚂蚁金服服务注册中心 SOFARegistry 解析 | 服务发现优化之路

服务注册中心 Session 存储策略 | SOFARegistry 解析

海量数据下的注册中心 - SOFARegistry 架构介绍

服务注册中心数据分片和同步方案详解 | SOFARegistry 解析

蚂蚁金服开源通信框架SOFABolt解析之连接管理剖析

蚂蚁金服开源通信框架SOFABolt解析之超时控制机制及心跳机制

蚂蚁金服开源通信框架 SOFABolt 协议框架解析

蚂蚁金服服务注册中心数据一致性方案分析 | SOFARegistry 解析

蚂蚁通信框架实践

sofa-bolt 远程调用

sofa-bolt学习

SOFABolt 设计总结 - 优雅简洁的设计之道

SofaBolt源码分析-服务启动到消息处理

SOFABolt 源码分析

SOFABolt 源码分析9 - UserProcessor 自定义处理器的设计

SOFARegistry 介绍

SOFABolt 源码分析13 - Connection 事件处理机制的设计

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

  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. Open SSH CVE-2020-15778

    Open SSH OpenSSH 是用于使用 SSH 协议进行远程登录的一个开源实现.通过对交互的流量进行加密防止窃听,连接劫持以及其他攻击.OpenSSH 由 OpenBSD 项目的一些开发人员开发 ...

  2. Python 中的运算符重载

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

  3. 【vue-1】vue-cli3.0以上的搭建与配置(2.X的版本是不一样的)

    为什么要使用 vue-cli Vue CLI 致力于将 Vue 生态中的工具基础标准化.它确保了各种构建工具能够基于智能的默认配置即可平稳衔接,这样你可以专注在撰写应用上,而不必花好几天去纠结配置的问 ...

  4. [leetcode]53Maximum Subarray动态规划经典题目:最大子串问题

    /** * Find the contiguous subarray within an array (containing at least one number) * which has the ...

  5. .netcore 微服务快速开发框架 Anno&Viper -分布式锁是个什么鬼

    1.什么是锁 锁是为了解决多线程或者多进程资源竞争的问题. 同一进程的多个线程资源竞争可以用lock解决. lock 关键字可确保当一个线程位于代码的临界区时,另一个线程不会进入该临界区. 如果其他线 ...

  6. python谷歌序列化传输

    今日破解某app加密app当中用到了谷歌序列化传输 1.请求序列化文件 # Generated by the protocol buffer compiler. DO NOT EDIT! # sour ...

  7. springboot 启动jar正确方式

    首先需要pom.xml配置一个插件: IDEA 在右侧执行顶上m图片按钮 在command Line 中执行clean package命令 执行打包注意 打完jar包后最好解压jar查看一下META- ...

  8. HTML学习案例-仿慕课网网页制作(二)

    制作部分:网页footer部分 制作效果: 涉及知识:link部分要复习: dl- definition list dt- definition title dd - definition descr ...

  9. CSS-position 属性&元素脱离文档流引发父级边框塌陷问题

    CSS-position 属性 CSS 定位机制 CSS 有三种基本的定位机制:普通流.浮动(float)和绝对定位(position). 除非专门指定,否则所有框都在普通流中定位.也就是说,普通流中 ...

  10. Spring Boot 2.x基础教程:实现文件上传

    文件上传的功能实现是我们做Web应用时候最为常见的应用场景,比如:实现头像的上传,Excel文件数据的导入等功能,都需要我们先实现文件的上传,然后再做图片的裁剪,excel数据的解析入库等后续操作. ...