swoft中Crontab定時器的坑

這兩天老大給了個需求想把商城熱點數據同步到redis緩存。咱們項目使用的是swoft框架,因此我就想到用框架的Crontab定時器。可是在測試的時候發現把Table的size設置爲1024時(實際上設置爲任何大小都同樣,貼上swoole的解釋)發現內存溢出了html

clipboard.png

普及一下Table(來自swoole文檔):
Table底層是創建在共享內存之上的HashTable數據結構。$size最大行數,決定了HashTable的總行數。因爲Table是在共享內存之上,因此沒法動態擴容。這個$size必須在建立前設置好。
$size參數指定表格的最大行數,若是$size不是爲2的N次方,如102四、8192,65536等,底層會自動調整爲接近的一個數字,若是小於1024則默認成1024,即1024是最小值

先把框架任務投遞流程走一下:redis

  1. 首先當框架啓動一秒後,啓動定時器每秒去更新執行一次Task(任務)。更新任務以前先去隊列內存表中清理已完成的隊列數據(這點很重要)
  2. 而後獲取出全部的任務中的隊列(能夠理解爲獲取全部的Task類中的方法),以任務規則,以及taskClass,分鐘,時間戳這些數據以md5方式加密獲得每一個任務隊列的key值,保存在runTimeTable 中。(originTable,以及runTimeTable 的結構)

clipboard.png

注:在定時器這塊使用到兩個Table 一個是originTable用於存儲任務的(Task)實例。另外一個是runTimeTable 存儲任務隊列實例,通俗地說就是存須要執行的任務實例

再看看任務執行流程,任務的執行就很簡單了數組

  1. 首先經過getExecTasks這個方法把全部知足條件的隊列任務放在一個數組,而後經過遍歷數據把runStatus的值改成self::START
  2. 以後執行全部runStatus的值爲self::START的隊列任務
  3. 把執行後的隊列任務的runStatus的值改成self::FINISH
  4. 最後把runStatus的值改成self::FINISH的剔除掉

從新梳理一下咱們邏輯
當咱們新建執行一個任務的時候,系統每秒鐘都回去更新執行一個每一個任務中的隊列數。
代碼以下:
clipboard.png緩存

clipboard.png

經過代碼咱們可以發現每一分鐘他都會往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')swoole

當時間到下一秒(是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) {

clipboard.png

本文爲本人學習過程記錄。若是有哪些地方描述不當望各位大佬指出。

點我閱讀原文學習

相關文章
相關標籤/搜索