支付宝定时任务怎么做?三层分发任务处理框架介绍

这篇具有很好参考价值的文章主要介绍了支付宝定时任务怎么做?三层分发任务处理框架介绍。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

支付宝定时任务怎么做?三层分发任务处理框架介绍

本文将从单机定时调度开始,循序渐进地带领大家了解五福定制三层分发任务处理框架。

 

一、背景介绍

技术同学对定时任务肯定不陌生。定时任务一般用来定时批量进行业务处理。支付宝卡包券到期提醒、删除过期失效券,五福大促批量给用户发放添福红包等场景,都是通过定时任务触发来完成的。
作者有幸参与了2023兔年五福大促的开发,主导完成了福气乐园分会场平分5000万大奖需求。通过学习并运用五福定制三层分发任务处理框架,最终平稳丝滑的完成了平分大奖需求任务。本文将从单机定时调度开始,循序渐进地带领大家了解五福定制三层分发任务处理框架。

二、定时任务分类

支付宝定时任务怎么做?三层分发任务处理框架介绍

本文将定时任务分为单机和集群两大类别,其中单机又分为定时调度和定时调度加批处理框架,集群分为三层分发和五福定制三层分发任务处理框架。

2.1、单机任务

单机定时任务毫无疑问是在单台机器上运行的定时任务。在业务量级不大,没有进行分库分表时,往往单机定时任务即可满足业务需求。

从复杂度上来说,单机定时任务又可分为简单的定时调度和定时调度+批处理两种。

1、定时调度

在Spring中可以通过@Scheduled 来启用定时任务。触发的方式有两种,分别是:cron 表达式和 fixedRated类配置参数。常用的案例:

// cron表达式
@Scheduled(cron="0 0/30 9-17 * * ?") //按cron规则执行,朝九晚五工作时间内每半小时
@Scheduled(cron="0 0 12 ? * WED") //按cron规则执行,表示每个星期三中午12点

// fixedRated类配置
@Scheduled(fixedRate=5000) //上一次开始执行时间点后5秒再次执行;
@Scheduled(fixedDelay=3000) //上一次执行完毕时间点后3秒再次执行;
@Scheduled(initialDelay=1000, fixedDelay=2000) //第一次延迟1秒执行,然后在上一次执行完毕时间点后2秒再次执行;

定时调度往往用于业务处理流程比较简单的场景,比如定时生成简单报表,发送通知。对于复杂耗时的场景,处理效率不高,业务高峰期会积压大量待处理数据,影响业务。

2、定时调度+批处理

为了解决复杂耗时场景下定时调度效率不高的问题,可以引入批处理框架。定时调度与批处理框架相结合,可以大幅提高数据处理的效率,提升系统稳定性,保障业务稳定运行。

以Spring Batch批处理框架为例,任务处理流程如下:

支付宝定时任务怎么做?三层分发任务处理框架介绍

Spring Batch批处理框架将任务拆分成多个Step,同时每个Step里面又分为itemReader,itemProcessor, itemWriter。通过将任务分层细化,能够让多个阶段并行处理,提高任务处理效率。批处理框架结合定时调度框架,可以在单机情况下,对大量复杂的业务进行高效的批处理。

 

2.2、集群任务

在分库分表大业务流量情况下,单机定时任务已无法满足业务需求了,这时就产生了集群定时任务。在支付宝技术架构下,用户数据按照eid进行分库分表,同时进行Zone维度的隔离。此时单机定时任务无法对全量数据做处理,于是支付宝便有了自己的分布式任务调度中间件Antscheduler,配合三层分发任务处理框架,就可以对大量数据进行定时批量处理。

1、三层分发

支付宝定时任务怎么做?三层分发任务处理框架介绍
上图描述了三层分发实现定时任务处理的过程:
1.Antscheduler任务调度中间件按照配置好的规则,定时往消息中心投递消息。
2.消息中心将定时任务消息分别投递到每个Zone中的一台机器。
3.接收到消息的机器进入三层分发的第一层,即Splitor处理流程。通常是获取当前Zone的eid分片,比如00~24。
4.第一层Splitor处理完了之后,通过TR oneway的调用方式,在当前Zone对三层分发的第二层发起调用,即进入Loader处理流程。此时调用得到扩散,eid分片为00~24的情况下,会发起25次TR调用,即最多会进入当前Zone的25台机器进行Loader处理。
5.Loader通常是获取传递过来eid分片的数据。比如一台机器的Loader接收到eid为20,则该Loader从eid为20分片的DB获取100条待处理数据。
6.第二层Loader获取到待处理数据后,同样是通过TR oneway的调用方式,在当前Zone对三层分发的第三层发起调用,即进入Executor处理流程。此时调用进一步得到扩散,一个Loader获取的100条数据,此时会发起100次TR调用,即最多会进入当前Zone的100台机器进行Executor处理。
7.Executor通常是进行真正业务处理逻辑的地方。比如对每条数据做状态变更、发送eid维度消息等等。
三层分发能很好的将任务进行分层拆分扩散,充分利用机器资源,尽量做到负载均衡。但三层分发也存在一些缺陷,主要体现在以下几个方面:
1.定时调度间隔和间隔内能够处理的数据量很难完全匹配。调度间隔时间太长,机器资源没有得到有效利用,会导致处理效率低下,任务会积压;调度间隔时间太短,有可能会导致数据被重复捞取处理,为此要做额外的防重复处理逻辑。
2.无法做到平滑的任务处理。由于在Loader层获取要处理的任务数,交由Executor层执行时,并不能限制任务执行的qps,同时待处理任务数变多时,整个集群任务的qps就变得很高,对DB和其他外围系统来说,存在稳定性风险。
3.无法最大化利用集群机器资源。考虑到稳定性和高可用,设计上每个Zone是有A/B分组的,每个分组都能获取到本Zone的eid分片,所以配置Antscheduler调度规则时,通常只会选择Zone的A/B组中的一组开启任务调度。三层分发内部每个层级之间的TR调用又只能在本Zone同组内进行,A/B组之间无法进行TR调用,所以浪费了一半的机器资源。

2、五福定制三层分发

五福大促有很多业务场景都是需要通过定时任务来进行处理的,比如生肖卡提醒、AI年画提醒,福气乐园平分5000万大奖。五福大促对稳定性和可用性的要求是非常高的,为了解决三层分发处理框架缺陷带来的效率和稳定性风险,五福在三层分发基础上做了定制化改造,改造的目标主要有两点:
1)最大化利用集群机器资源。做到真正的负载均衡,同时也能够提升集群的任务处理容量。
2)平滑的任务处理。减少任务调用的尖刺,避免对DB和外部系统造成稳定性风险。
下面分别从优化目标的两点来进行阐述。
1、最大化利用集群机器资源
由于三层分发默认只会在同一个Zone的A/B组中开启一组,导致浪费了一半的机器。显而易见,要最大化利用集群机器资源,就需要让A/B组的机器都能够参与到任务处理当中。优化的步骤如下:
1.Antscheduler定时调度平台同时开启A/B分组调度。
2.增加任务配置,配置的目的是让A组的机器只处理奇数位eid、B组的机器只处理偶数位eid。
3.Splitor层根据任务配置,将本Zone全量eid进行分组,A组只处理奇数位eid,B组只处理偶数位eid。
上面三步做完以后,就能让集群所有的机器都能参与到任务处理当中,从而最大化的利用了机器资源。

支付宝定时任务怎么做?三层分发任务处理框架介绍

以上图为例,Zone_01对应DB的eid分片为00~24,经过Splitor处理之后,Zone_01(A)组机器获得任务处理的eid都是奇数位,即01、03、...23;同理,Zone_01(B)组机器获得任务处理的eid都是偶数位,即00、02、...24。后续Loader、Executor处理都在本组内进行。通过把eid分片像A/B分组那样进行奇/偶分组,就能让所有机器都能够参与到任务处理当中。下面是eid分组的核心代码:
/**
* 根据配置中心的dataFlag过滤
* 1、默认ALL不区分
* 2、ODD 表示仅分发奇数表号
* 3、EVEN 标识仅分发偶数表号
*
* @param eidList
*/
public void filteByDataFlag(List<String> eidList) {
    String dataFlag = SchedulerConfigDrmUtil.getIndexFilterFlag();
    int strategy = -1;
    if (StringUtil.equalsIgnoreCase("ODD", dataFlag)) {
        strategy = 1;
    } else if (StringUtil.equalsIgnoreCase("EVEN", dataFlag)) {
        strategy = 0;
    }
    if (strategy == -1) {
        // ALL
        return;
    }
    // filter
    Iterator<String> it = eidList.iterator();
    while (it.hasNext()) {
        String str = it.next();
        int index = NumberUtils.toInt(str, -1);
        if (index % 2 != strategy) {
            it.remove();
        }
    }
}

通过代码可知,推送任务配置时,将A组机器的值推成“ODD”,B组机器的值推成“EVEN”,即可实现A/B组的所有机器同时执行定时任务的效果。

2、平滑的任务处理
默认的三层分发通过Loader层获取待处理的任务,然后交由Executor来执行,无法保证整个集群任务处理的量级。在待处理任务变多,或者集群机器扩缩容变化频繁的情况下,任务处理的峰值量级无法保证。同时由于各个层级之间的调用是TR oneway调用,是感知不到调用结果的,也就更难保证任务的平滑调用。为了达到任务平滑调用的目的,五福场景对Loader捞取任务数和单机任务qps做了优化调整,保证了集群任务处理效率在预期范围之内。优化的步骤如下:
1.新增任务配置。核心配置信息包括期望集群执行任务总的qps、任务调度间隔,集群参与任务机器数。
2.计算单机qps和每次DB捞取任务的数量。
3.单机执行时,根据计算好的qps来进行限流调用。
上面三步做好之后,整个集群就能够按照预期的qps进行平滑的任务处理。
为什么这三步做完了之后就能达到预期的效果呢? 重点看下任务配置的解析代码:
/**
 * 计算定时任务的相关配置
 *
 * 主要计算:
 *    scheduleSingleLimit 单机限流值
 *    scheduleLoaderCount loader捞取条数
 *
 * @param scheduleConfig
 */
public static SchedulerConfig calculateScheduleConfig(SchedulerConfig scheduleConfig) {
    final int qpsLimit = scheduleConfig.getScheduleWholeLimit();
    final int machineCounts = scheduleConfig.getScheduleMachineCounts();
    MtLogger.info(LOGGER,
        "【计算定时任务配置】-开始 任务名称:{0},机器数量:{1},任务吞吐量:{2}.", scheduleConfig.getScheduleType(),
        machineCounts, qpsLimit);

    //定时任务的调度频率是scheduleRate 秒执行一次 所以scheduleRate秒中内集群的整体吞吐量=qps限制*scheduleRate
    final int scheduleRate = scheduleConfig.getScheduleRatePerSec();
    long totalLoaderCounts = qpsLimit * TimeUnit.SECONDS.toSeconds(scheduleRate);

    //定时任务捞取的表数量为1000 所以到每个表的限制=totalLoaderCounts/1000
    long loaderCountPerTask = totalLoaderCounts / 1000;
    if (loaderCountPerTask < 1) {
        loaderCountPerTask = 1;
    }
    scheduleConfig.setScheduleLoaderCount((int) loaderCountPerTask);

    //整体限流通过单机限流实现 整体限流=单机限流*machineCounts
    final double singleQps = ((double) qpsLimit / machineCounts);
    //创建的限流需要1秒的预热
    scheduleConfig.setScheduleSingleLimit(RateLimiter.create(singleQps, 1, TimeUnit.SECONDS));

    MtLogger.info(LOGGER,
        "【计算定时任务配置】-结束 任务名称:{0},捞取条数:{1},单机限流:{2}.", scheduleConfig.getScheduleType(),
        scheduleConfig.getScheduleLoaderCount(),
        scheduleConfig.getScheduleSingleLimit().getRate());

    return scheduleConfig;

}

通过代码可知,推送的任务配置最终会生成两个重要的配置信息:

1.单个Loader捞取的任务数。集群qps和调度间隔确定了一个调度间隔内要处理的任务数,结合eid分片数量(五福是千库千表)确定每个Loader要捞取的任务数。
2.单机Executor执行任务时的qps。预期集群qps和集群机器数确定了单机执行任务时的qps,单机上通过Guava的RateLimit来达到限流的效果。如果请求超过了限制的qps,请求将会被阻塞。
下面以福气乐园平分5000万大奖的任务配置作为样例来计算:

支付宝定时任务怎么做?三层分发任务处理框架介绍

相比通常情况下指定Loader每次捞取的任务数,五福是通过集群qps和任务调度间隔来确定Loader需要捞取的任务数。因此一个调度间隔内的任务数和集群能够执行的任务数是匹配上的,加上通过单机qps限制达到集群qps限制的效果,从而让整个定时任务做到了平滑调用。简而言之,优化后的调度逻辑,能够让定时调度任务在机器维度和时间维度都能均匀平稳的执行。

支付宝定时任务怎么做?三层分发任务处理框架介绍

上面的截图是福气乐园平分5000万大奖真实发奖时的调用监控。通过监控可以看到,每隔5秒钟,整个集群Loader捞取的任务数是40000个。整个任务生命周期内,Executor执行任务的qps在8000上下小范围波动,没有出现大范围的波动,从而达到了平滑任务处理的预期效果。
五福定制三层分发任务处理框架,能够很好的解决任务效率和平滑调用的问题,但也存在一些使用限制。每种任务的任务配置需要人工手动推送,而其中的机器数实际上很多时候并不是固定的。推送的机器数和实际集群拥有的机器数不匹配的结果就是单机限流失准,导致集群限流不符合预期,也就导致任务间隔内捞取的任务数和能处理的任务数无法匹配上。五福场景下,机器数确定后一般不会出现大范围变动,而且五福各种业务都属于重点高保业务,机器变动后大家都能及时感知到,所以这种定制逻辑在五福场景下是可以很好的运行起来。日常的各种业务,通常集群机器数并不是固定的,存在各种扩缩容的情况,五福定制三层分发任务处理框架依旧不能保证任务的平滑调用。

三、结语

从单机到集群,再到五福定制集群定时任务,本文逐步做了一个框架设计上的原理介绍。每种定时任务都有自己的优点和缺陷,也都有自己的应用场景。在工作中,要结合当前的业务情况,选择合适的定时任务进行业务处理,避免设计上的失误导致业务受损。以五福定制三层分发任务处理框架为例,虽然日常业务中,因为机器数量不固定,依旧无法做到任务的平滑调用,但我们可以借鉴最大化利用集群机器资源这一点,同时开启A/B组的定时任务,从而实现任务调度真正的负载均衡,提高系统整体的稳定性。

 

作者|金盛杰(司旭)文章来源地址https://www.toymoban.com/news/detail-411180.html

到了这里,关于支付宝定时任务怎么做?三层分发任务处理框架介绍的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Zinx框架-游戏服务器开发002:框架学习-按照三层结构模式重构测试代码+Tcp数据适配+时间轮定时器

    三层结构重构原有功能 自定义消息类,继承UserData,添加一个成员变量szUserData 定义多个Role类继承Irole,重写ProcMsg函数,进行不同处理 定义protocol类,继承Iprotocol,重写四个函数,两个函数时原始 数据和用户数据之间的转换;另两个用来找消息处理对象和消息发 送对象。 定

    2024年02月05日
    浏览(57)
  • Django框架使用定时器-APScheduler实现定时任务:django实现简单的定时任务

    系统:windows10 python: python==3.9.0 djnago==3.2.0 APScheduler==3.10.1 1、创建utils包,在包里面创建schedulers包 utils/schedulers/task.py utils/schedulers/scheduler.py utils/schedulers/__init__.py 2、项目配置文件settings.py

    2024年02月12日
    浏览(46)
  • 定时任务框架快速入门

    1. Quartz 概述 Quartz 是一个开源的作业调度框架(job scheduler),几乎可以集成到任何 Java 应用程序中,从最小的独立应用程序到最大的电子商务系统。Quartz 可用于创建简单或复杂的调度来执行数十个、数百个甚至数万个作业;其任务被定义为标准 Java 组件的作业,这些组件几乎可

    2023年04月22日
    浏览(42)
  • Ruoyi框架学习--定时任务

    在实际项目开发中Web应用有一类不可缺少的,那就是定时任务。 定时任务的场景可以说非常广泛,比如某些视频网站,购买会员后,每天会给会员送成长值,每月会给会员送一些电影券; 比如在保证最终一致性的场景中,往往利用定时任务调度进行一些比对工作;比如一些

    2023年04月20日
    浏览(41)
  • Spring Task(定时任务)框架

    Spring Task 是Spring框架提供的任务调度工具,可以按照约定的时间自动执行某个代码逻辑。 应用场景: 信用卡每月还款提醒 银行贷款每月还款提醒 火车票售票系统处理未支付订单 入职纪念日为用户发送通知 等等… (只要是需要定时处理当达到场景都可以使用Spring Task) cr

    2024年02月05日
    浏览(45)
  • 分布式定时任务框架 PowerJob

    1.1 为什么需要使用定时任务调度 (1)时间驱动处理场景:整点发送优惠券,每天更新收益,每天刷新标签数据和人群数据。 (2)批量处理数据:按月批量统计报表数据,批量更新短信状态,实时性要求不高。 (3)异步执行解耦:活动状态刷新,异步执行离线查询,与内部

    2024年02月09日
    浏览(62)
  • 任务调度框架-如何实现定时任务+RabbitMQ事务+手动ACK

    比如: 1.每天早上6点定时执行 2.每月最后一个工作日,考勤统计 3.每个月25号信用卡还款 4.会员生日祝福 5.每隔3秒,自动提醒 10分钟的超时订单的自动取消,每隔30秒或1分钟查询一次订单,拿当前的时间上前推10分钟 定时任务,资源会有误差的存在,如果使用定时任务 定时

    2024年02月08日
    浏览(38)
  • 你知道怎么实现定时任务吗?

    诸位读者都知道笔者写东西都是用到才写,笔者的学习足迹自从参加工作之后就是 非系统 学习了,公司里源代码只要有笔者不知道的技术细节,笔者就会仔细的研究清楚,笔者是不喜欢给自己留下问题的那种学习习惯。 笔者最近负责了消息发送的一些业务需求,由于笔者工

    2023年04月23日
    浏览(58)
  • 分布式定时任务调度框架Quartz

    Quartz是一个定时任务调度框架,比如你遇到这样的问题: 比如淘宝的待支付功能,后台会在你生成订单后24小时后,查看订单是否支付,未支付则取消订单 比如vip的每月自动续费功能 … 想定时在某个时间,去做某件事 Quartz是一套轻量级的任务调度框架,只需要定义了 Job(

    2024年02月04日
    浏览(45)
  • 分布式任务调度,定时任务的处理方案

    适用场景: Spring 定时任务是 Spring 框架提供的一种轻量级的任务调度方案,它的特点是简单易用、轻量级。Spring 定时任务的执行是在 单个节点 上进行的,如果需要分布式任务调度,需要自己实现相应的解决方案。 1.导入依赖版本自己控制 2.启动类加上@EnableScheduling 3.编写业

    2023年04月14日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包