这两天老大给了一个需求,把商城的热点数据同步到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)注:timerblock中用到了两个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-2210:01:20现在更新runTimeTable的时候,他会添加60个任务队列它的钥匙。密钥将是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-2210:01:21),它仍然会更新runTimeTable数据键值到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-2210:01:21秒之前没用。此数据永远不会被消耗,也永远不会被删除。所以过一段时间就会出现内存溢出。所以解决的办法是在清理消费数据的同时清理过期数据。将cleanRunTimeTable中的if($value['runStatus']===self::FINISH){更改为$currentTime=time();if($value['runStatus']===self::FINISH||$value['sec']<$currentTime){这篇文章是我学习过程的记录。如有描述不当的地方,希望大家指出。点我阅读原文
