如何使用Redis让周期异步任务变得Fault-tolerant且Dynamic

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介:         Python技术栈的同学一定都非常了解Celery——基于消息队列的分布式任务调度系统。(具体用法介绍不在此赘述)。通过Celery可以快速高效的将大规模的任务实时分发到众多的不同的机器上,让用户只关注每个单独任务的处理,而非调度分配任务本身。

        Python技术栈的同学一定都非常了解Celery——基于消息队列的分布式任务调度系统。(具体用法介绍不在此赘述)。通过Celery可以快速高效的将大规模的任务实时分发到众多的不同的机器上,让用户只关注每个单独任务的处理,而非调度分配任务本身。另外,Celery中有一个非常好用的周期任务发生器Beat,通过此进程可以帮助我们快速开发出大规模的周期任务,也就是任务的生产者。例如我们当前的应用每天就会产生15万以上的周期任务,但是与之相伴随的就是一个非常重要的问题,原生Beat在Celery中是一个单点进程,不具备容错能力。所以如何让周期任务发生器变得具有容错性(Fault-tolerant)——即周期任务产生器进程Beat挂掉后(或者机器宕机)快速恢复进程,是必须要考虑的问题。Celery在某种程度上是具有容错性的,但高可用性也是由条件的:需要保证所使用的消息中间件的高可用性,并且只保证消息存储和消费过程的容错性。而另一个问题就是原生Beat不支持动态添加/删除/修改(Dynamic)周期任务的方法,当应用的周期任务越来越多,不能因为新增一个任务而重启整个Beat进程,导致其他任务周期产生影响。

        当然,开源对于这两类问题,已经有很多成熟的解决方法,往往大家都殊途同归,借助消息中间件的高可用性,来进行故障转移。这里我也是站在别人的肩上,看看redbeat如何借助Redis来做到周期任务的高可用性(Fault-tolerant),以及周期任务的弹性动态变化能力(Resilient)。

一、原生Celery4.x Beat是如何产生周期任务的?

        Celery4.x中默认使用的是PersistentScheduler作为默认调度器的持久化方式,即使用写文件的方式(序列化)记录所有周期任务的最后执行时间last_run_at以及周期调度的频率(interval/crontab),Celery支持用户自己实现持久化的方式如将每次调度任务的时间信息,记录在mysql数据库,redis/mongodb等。用户可以通过重载PersistentScheduler,自己实现存储调度任务的信息的方式。如对于django用户可以使用django-celery-beat实现通过控制台进行动态的创建,编辑,删除周期性任务。但django-celery-beat暂不确定是否可以保证容错性的beat(即允许多个beat进程同时调度)。不过,我们可以确定,要实现Beat的Fault-tolerant&Dynamic,必须要将待调度的周期性任务的相关信息,存储到某种类型的后端数据库,无论是mysql还是nosql的redis等。

        下面我们用一张图来说明整个原生Beat的调度方式

        由上图可以了解到,Celery4.x中使用了最小堆的方式来计算下一次需要触发调度的周期性任务(Celery3.x并未使用此数据结构)。同时会在内存后端存储(数据库/文件)中记录每个任务最后一次的调度时间。但是从原生Beat中也可以看出,只有当重启beat进程时才能对周期性任务进行增删改操作,并无法进行动态修改,并且不支持容错行为。

二、Redbeat如何重载原生Scheduler?

        使beat做到Fault-tolerant特性,重要的一点是有多节点的beat进程冗余。而要想动态的增加/删出/改变周期性任务,则需将每个任务的调度相关信息,存储在某一中心化存储中,如mysql/redis。将调度信息在后端进行存储也进一步保证了beat调度的任务准确性和唯一性。 在实现上需要做到以下五点。

  • 重载Scheduler:初始化加载以及更新所有的周期性任务,将所有周期性任务的定时信息,参数等防止到redis中存储。
  • 重载ScheduleEntry: 每一个周期性任务对应一个entry,记录任务的调度周期,参数以及最近最后一次调度时间,并序列化至redis,用key-value方式记录这些信息。
  • 使用redis中的Sorted Set数据结构存储每个任务下一次调度的时间,代替最小堆。
  • 需要保证所有的任务的定时信息如interval/crontab等参数,均获取自redis/任何中心化存储(如diamond),保证任务调度信息可以动态变化
  • 利用简易的redis分布式锁实现Fault-tolerant,同时保证一个时刻只能有一个beat进程真实进行调度

      下图简单的表达整个Redbeat的调度流程。

        可以看到beat进程的执行中,通过分布式锁保证同一时刻仅有一个beat进程进行调度,其他进程均会被阻塞,直到调度进程挂掉,超时后释放锁。另外通过redis中Sorted Set数据结构模拟原生beat中的最小堆判断本次应该调度的任务。整个过程相对精简直观。

        对于动态改变Beat中周期任务的行为,可以分为新增/删除/修改定期任务执行周期三种。

  • 新增任务:1. 将新增任务名插入至statics中,2.并且任务定义调度等信息放入对应任务名的hash对象中。3.同时把任务及对应的当前时间插入至schedule的Redis对象中,触发下一轮调度。
  • 删除任务:1. 把任务相关定义调度信息所对应的hash对象删除,从而使下一轮调度时无法获取对应信息,进而调度失败。2. 删除statics中改任务对应的名称
  • 修改定期任务执行的周期:直接修改任务名在redis中对应的hash对象内的调度信息即可

        至此,一个简单的具有容错和动态改变任务调度信息的周期性任务即完成。

三、总结:

      当然此实现也有一些小的缺陷。比如当beat挂掉后,最长的恢复时间取决于redis_lock的超时时间,并且保证高可用性的最为重要的问题,即要保证redis的高可用性。对于这个问题,本人暂未深入研究。 另外对于整个当前Redbeat实现的性能的极限也并没有进行测试,对于我们的应用来说,共有400+的周期性任务,并且平均一天内这些任务一共会执行15万次,此实现对于满足我们的需求已足够。

参考文章源码:

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
3天前
|
存储 NoSQL Java
Redis 实现延迟任务的深度解析
【4月更文挑战第17天】
106 0
|
17天前
|
缓存 NoSQL Java
面试官:Redis如何实现延迟任务?
延迟任务是计划任务,用于在未来特定时间执行。常见应用场景包括定时通知、异步处理、缓存管理、计划任务、订单处理、重试机制、提醒和数据采集。Redis虽无内置延迟任务功能,但可通过过期键通知、ZSet或Redisson实现。然而,这种方法精度有限,稳定性较差,适合轻量级需求。Redisson的RDelayedQueue提供更简单的延迟队列实现。
239 9
|
2月前
|
存储 NoSQL API
【小小思考】Redis实现去重任务队列
【2月更文挑战第1天】思考一下如何用Redis实现去重的任务队列,主要有List 、List + Set/Hash/Bloom Filter、ZSet、Lua和开源库等方式。
79 1
|
7月前
|
NoSQL 安全 容灾
1分钟实现Redis数据迁移任务
NineData 基于全量复制、增量日志复制技术,提供了高效、安全可靠的 Redis 不停机迁移方案。当然,除了 Redis,NineData 已经支持数十种常见数据库的迁移复制,实现数据库迁移、数据容灾、数据双活、数据仓库实时集成等业务场景。同时,除了 SAAS 模式外,还提供了企业专属集群模式,满足企业最高的数据安全合规要求。
160 0
|
7月前
|
监控 NoSQL Java
面试官:Redis分布式锁超时了,任务还没执行完怎么办?
今天主要分享的是面试中常见的redis的一些面试内容。如果你正好需要刚好可以帮你回顾一下,如果不需要可以收藏起来后面用到的时候翻出来回顾。
|
10月前
|
NoSQL Redis
【Redis原理机制 四】基于Redis实现延时任务
【Redis原理机制 四】基于Redis实现延时任务
34 0
|
机器学习/深度学习 Prometheus 运维
使用Redis实现延时任务(二)
前一篇文章通过Redis的有序集合Sorted Set和调度框架Quartz实例一版简单的延时任务,但是有两个相对重要的问题没有解决:分片、监控。这篇文章的内容就是要完善这两个方面的功能。前置文章:使用Redis实现延时任务(一)。
306 0
使用Redis实现延时任务(二)
|
机器学习/深度学习 JSON 监控
使用Redis实现延时任务(一)(下)
最近在生产环境刚好遇到了延时任务的场景,调研了一下目前主流的方案,分析了一下优劣并且敲定了最终的方案。这篇文章记录了调研的过程,以及初步方案的实现。
126 0
使用Redis实现延时任务(一)(下)
|
消息中间件 存储 NoSQL
使用Redis实现延时任务(一)(上)
最近在生产环境刚好遇到了延时任务的场景,调研了一下目前主流的方案,分析了一下优劣并且敲定了最终的方案。这篇文章记录了调研的过程,以及初步方案的实现。
367 0
使用Redis实现延时任务(一)(上)
|
消息中间件 监控 NoSQL
Hyperf结合Redis异步队列任务async-queue实现后台操作日志写入
Hyperf结合Redis异步队列任务async-queue实现后台操作日志写入
378 0
Hyperf结合Redis异步队列任务async-queue实现后台操作日志写入

热门文章

最新文章