swoft中Crontab定时器的坑

48次阅读

共计 1922 个字符,预计需要花费 5 分钟才能阅读完成。

这两天老大给了个需求想把商城热点数据同步到 redis 缓存。我们项目使用的是 swoft 框架,所以我就想到用框架的 Crontab 定时器。但是在测试的时候发现把 Table 的 size 设置为 1024 时(实际上设置为任何大小都一样,贴上 swoole 的解释)发现内存溢出了

普及一下 Table(来自 swoole 文档):Table 底层是建立在共享内存之上的 HashTable 数据结构。$size 最大行数,决定了 HashTable 的总行数。由于 Table 是在共享内存之上,所以无法动态扩容。这个 $size 必须在创建前设置好。$size 参数指定表格的最大行数,如果 $size 不是为 2 的 N 次方,如 1024、8192,65536 等,底层会自动调整为接近的一个数字,如果小于 1024 则默认成 1024,即 1024 是最小值
先把框架任务投递流程走一下:

首先当框架启动一秒后,启动定时器每秒去更新执行一次 Task(任务)。更新任务之前先去队列内存表中清理已完成的队列数据(这点很重要)
然后获取出所有的任务中的队列(可以理解为获取所有的 Task 类中的方法),以任务规则,以及 taskClass,分钟,时间戳这些数据以 md5 方式加密得到每个任务队列的 key 值,保存在 runTimeTable 中。(originTable,以及 runTimeTable 的结构)

注:在定时器这块使用到两个 Table 一个是 originTable 用于存储任务的(Task)实例。另一个是 runTimeTable 存储任务队列实例, 通俗地说就是存需要执行的任务实例
再看看任务执行流程,任务的执行就很简单了

首先通过 getExecTasks 这个方法把所有满足条件的队列任务放在一个数组,然后通过遍历数据把 runStatus 的值改为 self::START
之后执行所有 runStatus 的值为 self::START 的队列任务
把执行后的队列任务的 runStatus 的值改为 self::FINISH
最后把 runStatus 的值改为 self::FINISH 的剔除掉

重新梳理一下我们逻辑当我们新建执行一个任务的时候,系统每秒钟都回去更新执行一个每个任务中的队列数。代码如下:

通过代码我们能够发现每一分钟他都会往 runTimeTable 中添加 60 个任务队列但是当我们 getExecTasks 获取将要执行的任务队里的时候是根据当前的时候是否等于执行时间而标志状态的那么现在就会出现一个问题。当前时间往任务队里中添加数据的时候 他把前面执行过的任务队列再次添加进 runTimeTable 中举个栗子:假如我有个异步任务 Sync, 其中有个每秒执行一次的方法 cronTask,现在时间是 2019-03-22 10:01:20 现在往更新 runTimeTable 的时候 他会往里面添加 60 的任务队列 key 分别会是 MD5(” “.’Sync’.’cronTask’.’01’.’00’)MD5(” “.’Sync’.’cronTask’.’01’.’01’)MD5(” “.’Sync’.’cronTask’.’01’.’02’)MD5(” “.’Sync’.’cronTask’.’01’.’03’)MD5(” “.’Sync’.’cronTask’.’01’.’04’)…MD5(” “.’Sync’.’cronTask’.’01’.’59’)
当时间到下一秒(是 2019-03-22 10:01:21)的时候后 依然会往更新 runTimeTable 数据 key 值为 MD5(” “.’Sync’.’cronTask’.’01’.’00’)MD5(” “.’Sync’.’cronTask’.’01’.’01’)MD5(” “.’Sync’.’cronTask’.’01’.’02’)MD5(” “.’Sync’.’cronTask’.’01’.’03’)MD5(” “.’Sync’.’cronTask’.’01’.’04’)…MD5(” “.’Sync’.’cronTask’.’01’.’59’)
那么我们可以很明确地看出来在 2019-03-22 10:01:21 秒前的数据都是没用的了。这些数据永远不会被消费,也不会被删除。因此一段时间后会出现内存溢出的情况。所以解决方法是在清理消费数据的时候把过期数据也同时清理把 cleanRunTimeTable 中的
if ($value[‘runStatus’] === self::FINISH) {
改为
$currentTime = time();
if ($value[‘runStatus’] === self::FINISH || $value[‘sec’] < $currentTime) {

本文为本人学习过程记录。如果有哪些地方描述不当望各位大佬指出。

正文完
 0