阿里妹导读:网上购物的普及化带动了物流行业的迅猛发展,同时也带来了极大的压力和严峻的考验,特别是在电商大促的时节。如何有效提高整个物流链路的时效体验,给消费者更好的体验,这是菜鸟物流一直奋斗的目标。

今天,我们来深入了解菜鸟的轻量级定时任务调度引擎设计系统,学习如何在亿级别包裹中快速定位运输超时的包裹。

在中国物流快速发展的今天,日均包裹量已经突破1亿,如何确保1亿包裹在合理的时间之内送达收件人,并且能够在收件人反馈之前,及时处理那些没有在合理时间内运输的包裹,从而提高物流整个链路的时效体验,已经成为亟待解决的关键问题。

要想解决问题,首先要发现问题!有效、及时地发现问题离不开对这1亿包裹运输过程中每个环节实时监控,而要实现这个场景,就需要一个能够支撑起如此量级的实时定时调度系统。这就是本文的主题:轻量级定时任务调度引擎的设计。

传统方案

针对上文提到的问题场景,一般的做法是将定时任务写入数据库,通过一个线程定时查询出将要到期的任务,再执行任务相关逻辑。该方案的优点是实现简单,尤其适合单机或者业务量比较小的场景来。但是缺点也很明显:在分布式且业务量较大的场景中会引入很多复杂性。首先,需要设计一套合理的分库分表逻辑,以及集群任务负载逻辑。其次,即使做到这些,也会由于某些场景定时任务时间集中在某个时间点,导致集群单节点压力过大。再次,需要合理的预估容量,否则后续线性存储扩容将会非常复杂。

我们发现,上述方案的主要问题其实是定时任务时间和任务存储的耦合。如果能够将时间和存储解耦,任务的存储就等于是无状态的,这样对存储的可选择性范围会大很多,对存储的约束也大大降低!

下文将会介绍如何通过时间轮思想将定时任务的时间和任务本身进行解耦,从而在设计和性能上得到很大的提升。

时间轮基本介绍

时间轮方案将现实生活中的时钟概念引入到软件设计中,主要思路是定义一个时钟周期(比如时钟的12小时)和步长(比如时钟的一秒走一次),当指针每走一步的时候,会获取当前时钟刻度上挂载的任务并执行,整体结构如图1。

从上图可以看到,对于时间的计算是交给一个类似时钟的组件来做,而任务是通过一个指针或者引用去关联某个刻度上到期的定时任务,这样就能够将定时任务的存储和时间进行解耦,时钟组件难度不大,以何种方式存储这些任务数据,是时间轮方案的关键。

时间轮定时任务的存储

我们发现,阿里巴巴MQ(RocketMQ商业化版本,后文统称为阿里巴巴MQ)对其定时消息的改造[1]很有借鉴意义,老方案基于定时轮询的方式check消息是否到期,这种方案对于离散的时间比较受限,所以也就导致老版本只能支持几种延迟级别的定时消息。为了解决这个问题,阿里巴巴MQ团队将原方案改造为基于时间轮+链表的方案,从而既能支持离散的定时消息,也能够解决传统时间轮每个刻度需要管理各自任务列表的复杂性。

图2简明的描述了方案的关键点,其设计思路就是将任务列表写入到磁盘,并且在磁盘中采用链表的方式将任务列表串起来,要达到这种串起来的效果,需要每个任务中都有一个指向下一个任务的磁盘offset,只需要拿到链表的头便可以获取整个任务链表,于是在该方案中,时间轮的时间刻度不需要存储所有的任务列表,只需要存储链表的头即可,从而将内存的压力释放出来。

该方案对于中间件这样定位的系统来说是可以接受的,但对于一个定位在普通应用的系统来说,对部署的要求就显得过高了,因为你需要一块固定的硬盘。在当前容器化以及容量动态管理的趋势下,一个普通应用需要依赖一块固定的磁盘,对系统的运维和部署都会带来额外的复杂度。于是在此基础上形成本文重点介绍的轻量级定时任务调度引擎。

轻量级定时任务调度引擎

轻量级定时任务调度引擎借鉴了时间轮方案的核心思想,同时去除了系统对磁盘的依赖。既然不能将数据直接存储在磁盘,那只能依托专门的存储服务(比如Hbase,Redis等)来进行存储。于是就将下层的存储替换成了一种存储服务能够满足的结构:

将任务设计成一种结构化的表,并且将上面的offset替换成了一个任务ID(图2是文件的offset),并且通过ID将整个任务链表串起来,时间轮上只关联链表头的ID。这里对MQ方案改造的点只是将磁盘的offset替换成一个ID,从而解耦对磁盘的依赖。

方案到现在感觉可以行得通,但是又引出了另外两个问题:

问题1:单一链表无法并行提取,从而影响提取效率,对于某个时刻有大量定时任务的时候,定时任务处理的延迟会比较严重;

问题2:既然任务不是存储在本机磁盘了,表明整个集群的定时任务是集中存储,而集群中各个节点都拥有自己的时间轮,那么集群里面每个节点重启之后如何恢复?集群扩容&缩容如何自动管理?

任务链表分区——加速单一链表提取

链表的好处是在内存中不用存储整个任务列表,而只需要存储一个简单的ID,这样减少了内存的消耗,但是却带来了另一个问题。众所周知,链表的特性是对写友好,但读的效率却并不高,如果某个时刻需要挂载很长的任务链表,那链表的方式是完全不能利用并发来提高读的效率。

如何能够提高某个时间的任务队列提取的效率呢?这里利用分区的原理,将某个时刻的单一链表通过分区的方式拆分成多个链表,当将某个时间点的任务提取的时候,可以根据链表集合大小来并行处理,从而可以加速整个任务提取的速度。所以方案调整成图4:

集群管理——集群节点的自我识别

解决了定时任务的存储问题和单一链表提取任务的效率问题,好像整个方案都已经ready了,但是还有另一个重要的问题前面没有考虑进去,就是集群部署后节点重启后如何进行恢复?比如需要知道重启之前的时间轮刻度,需要知道重启之间时间轮刻度上的定时任务链表数据等,后面我统一将这种数据称之为时间轮元数据。如果任务写入磁盘,某个机器的重启,可以从本地磁盘加载当前节点的时间轮元数据来进行恢复;而如果不是通过磁盘来实现,那就带来问题2-1:一台机器在重启之后怎么获取它重启之前的时间轮元数据呢?

通过上面的解决定时任务存储问题,对于元数据的存储也可以借助专门的存储服务,但是由于集群的各个节点是无状态的,所以各个节点启动的时候,并不知道如何从存储服务中读取属于自己的元数据(也就是查询的条件),这便引出了问题2-2:集群节点怎么获取属于它的元数据呢?

可能第一想到的就是将元数据和节点ip或者mac地址关联上,但是在现在动态容量调度的情况下,一个机器拥有一个固定的ip也是很奢侈的,因为你不能保证你的应用会运行在哪台机器上。既然物理环境不能依赖,就只能依赖逻辑环境来对每个节点的时间轮进行区分了,于是就通过对集群每个节点分配一个在集群中唯一的逻辑ID,每台机器通过自己拿到的ID去获取时间轮数据。于是又引出了问题2-3:这个ID由谁来分配? 这就是Master节点。

在整个集群中需要选举出一个节点为Master,所有的其他节点会将自己注册到Master节点中,然后再由Master节点分配一个ID给各个节点,从而通过这个ID到存储服务中获取之前时间轮的元数据信息,最后初始化时间轮。图5和图6是该过程的描述图:

图5展示了节点注册和获取ID的过程,注意, Master节点自身也注册并且分配了ID,这是因为对一个普通应用来说,并没有Master和非Master之分,Master也是会参与整个业务的计算的,只不过它除了参与业务计算之外,额外还要进行集群的管理。

集群自动化管理——自动感知扩容&缩容

上文引出了Master节点,并且所有节点都会将自己注册给Master(包括Master自己),于是基于Master就可以构建出一套集群管理的机制。对于普通应用来说,集群的扩容缩容是很正常的操作,在动态调度的场景下,扩缩容操作甚至连应用owner都不感知,那么集群能够感知自己被扩容了和缩容了么?答案是肯定的,因为存在一个Master节点,而Master可以感知到集群的其他节点的存活状态,Master发现集群的容量变化,从而做出集群的负载调整。

定时任务提取集群化——集群压力软负载

上文通过将时间轮上的某个时间关联单一链表拆分成多个链表来提高提取任务的并发度,同时也已构建出了一套集群管理方案。如果这个并发度只是在单节点,只让该节点自己提取,将会因为某个时刻的当前节点到期的任务数量很大,从而导致集群中某些节点的压力会很大。为了能够更加合理的利用集群计算能力,那么可以基于上面的集群管理能力,对方案再进一步优化:将提取的到期任务链表集合通过软负载的方式分发给集群其他节点,同时由于任务的数据是集中存储的,只要其他节点能够拿到任务链表头的ID,便可以提取得到该链表的所有任务,从而集群的压力也将被平摊,图7是该过程的交互过程:

总结

上面对整个方案的演进,以及各个阶段遇到的问题进行了详细的介绍,可能还有一些点没有太深入,大家可以回复留言中详细讨论。下面结合上面的内容,做了一个简单的流程梳理,以方便大家对全文的理解。

引用:[1] 消息的处理方法、装置和电子设备:中国,201710071742.7[P].2017-10-07.

如何让快递更快?菜鸟自研定时任务调度引擎首次公开相关推荐

  1. 如何更快地渲染?深入了解3D渲染性能的指南!(2)

    如何更快地渲染-概述: 我将把本文分为两个主要部分: 可以优化的内部渲染因子 可以优化的外部渲染因子 您会发现内部渲染因子与您想要更快地优化/渲染的场景紧密相关,需要更改场景元素和渲染设置. 另一方面 ...

  2. 双十一消费近万亿!1亿人见证数字物流,“尾款人”收货更快了?购物狂欢七大趋势浮现

    来源: 券商中国 作者: 段久惠 国人买买买,双十一期间交易额首次进入万亿元时代. 今年双十一分为两个阶段,11月初就开始预售,一方面减缓了商家发货的压力,另一方面在营销上商家有了两波密集营销的机会以 ...

  3. 神策数据:从技术视角看,如何更多、更好、更快地实施A/B试验

    A/B 测试被更多人熟知的是持续观察并对照按一定规则分成的 A.B 两组测试样本,基于数据反馈辅助优化决策,其背后复杂的数学理论和试验基础设施却往往被人忽视. 目前,国内一线互联网公司大多采用自研的方 ...

  4. 阿里云IoT何云飞:智物Cloud AIoT Native 为何能让设备智能更快一步

    简介:在10月21日举行的2021云栖大会-IoT云端一体硬件与应用创新峰会上,阿里云智能IoT产品总经理何云飞在会上做了主题为"阿里云 Cloud AIoT Native 年度升级&quo ...

  5. 苹果开始尝试直接从自家零售店发货 更快送达消费者手中

    10月10日消息,据国外媒体报道,苹果现在不再将所有硬件产品直接从中国或当地仓库发货,而是开始利用其庞大的实体店作为产品分销网的节点,将产品直接从专卖店发货,以便更快地送到消费者手中. 据消息人士透露 ...

  6. 更小的模型,迈向更快更环保的NLP

    写在前面 越大的模型总是越好吗? 长期以来,在屠虐各大排行榜的驱动下,NLP players对此问题的答案似乎是肯定的. 从Google于2018年10月发布BERT(base版本为1.1亿个参数)到 ...

  7. 闪送,为何能比顺丰送得更快?

    点击蓝色"陈树义"关注我哟 2015 年,当我们以为电商领域尘埃落定之时,拼多多从阿里.京东杀出一条血路,做出了拼团电商的千亿美金公司.而在快递配送领域,同样有这么一家公司 -- ...

  8. Python 3.11 ,即将变得更快!

    点击上方"菜学Python",选择"星标"公众号 超级无敌干货,第一时间送达!!! 大家好,我是菜鸟哥. 作为一门异常受欢迎的编程语言,Python的优点有很多 ...

  9. 【小睿精选·第四期】谷歌开源更快、更高效的 TensorFlow 运行时 TFRT

    [小睿精选] [小睿精选]第四弹来啦,本期共收录6条嵌入式资讯信息,希望可以帮到你.欢迎大家在文末留言,唠一唠你关注的话题,说不定下期就有你想要的惊喜! 1.中科蓝讯与阿里"平头哥" ...

最新文章

  1. eclipse @ 注释为何一写就报错
  2. 使用 Blazor 开发内部后台(三):登录
  3. linux 判断网卡是否异常_如何判断linux网卡故障?
  4. mysql高级查询面试_高级MySQL数据库面试问题 附答案
  5. C#编程(八十一)---------- 捕获异常
  6. 信息学奥赛一本通(1114:白细胞计数)
  7. 关闭word_记一次毕设消失事件始末,及mac+word文档消失恢复方法汇总
  8. CSS 清理浮动 clear属性
  9. Hadoop 2.4.1 设置问题小结【原创】
  10. 网站制作---科讯万能搜索系统的简单实用教程
  11. python 逆序_python编程题-句子的逆序
  12. 学校做计算机教室锐捷,锐捷网络云课堂:让学生爱上每一节课
  13. windows蓝屏原因速查表(常见蓝屏原因与解决方法速查(适用于Windows 7/8/8.1/10/11))
  14. PYTHON机器学习基础(初学机器学习者的福音)
  15. 5G NR MCG,SCG,PCell,PSCell,SCell,sPCell 概念
  16. Navicat 15注册时报错“Rsa Public key not find“的解决办法
  17. 小样本点云深度学习库_基于点云深度学习的点云数据集制作系统及方法与流程...
  18. 【排列问题】-全排列
  19. java 下载junit的jar包_junit4下载-Junit4.11完整包【附使用方法】-东坡下载
  20. 大数据毕业设计 LSTM时间序列预测算法 - 股票预测 天气预测 房价预测

热门文章

  1. 肝!Python 网络编程
  2. xctf secret galaxy_三星SMARTTHINGS FIND正式发布 可帮助轻松查找GALAXY设备
  3. 动态规划,分治,回溯法,全排列,切片
  4. github使用-知乎的某小姐的一篇文章
  5. Discuz!X安装配置
  6. c# winform程序调用托管dll(c#的dll),使用添加引用和动态加载dll
  7. rfcomm工具的使用方法 创建/dev/rfcomm0 并检测
  8. 嵌入式 开发板 Linux 挂载ubifs
  9. QT各版本的源码下载地址
  10. u-boot移植第四弹——2013.10u-boot增加dm9000的支持