日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

延時任務,顧名思義:過一段時間后才執行的任務。例如用戶開啟計劃后 24 小時發一條推送,提醒用戶堅持練習;電商業務中,成單后72 小時未評價,自動打5分等場景。那么這些 延時任務怎么優雅的實現呢?

首先我們想到的是 crontab: 啟動一個crontab定時任務,每小時跑一次,給開啟計劃超過24小于25小時的用戶發送push。

$userList = select uid from program_schedule where create_time >= now_time - 25 * 3600 and create_time < now_time - 24 * 3600;
foreach ($userList as $uid)  {
      (new Service_Push())->sendPush($uid, '練習提醒', '親,今天記得練習,不要偷懶哦 ^^');
}

優點:

簡單、快速實現

缺點:1、數據量大的時候,效率比較低。

2、每次需要全量查詢一次,可能會重復計算

3、時效性不夠,因為是一個小時一次,極端情況,這個用戶就是當前小時的第一秒開啟的,那就要延遲一個小時收到推送。當然可以通過修改 crontab 的輪詢頻次(改成一分鐘或者1秒鐘),這樣放大了 1、2 步的缺點。

所以這類業務一定要保證時效性,可以采用時間輪實現:

不太會用畫圖工具,只能采用農耕文明的手段了,哈哈,湊合看。

使用時間輪實現“延時任務”

 

包括兩個數據結構:1、環形隊列:一個小時對應 3600秒,一秒一個槽,避免轉的過快和過慢

2、任務隊列: 環上每個槽都是一個 隊列

當前指針就是 當前時間戳 % 3600, 每個槽都是一個隊列,隊列的每個cell包含兩個數據結構:1. cycle_num: 循環次數,表示第幾圈,比如 48 小時后執行,那 cycle_num = 47 (下標從 0 開始)

2. data: 自定義參數,包含任務名稱(task_function_name)和參數列表(params)

比如現在游標在1, 現在有個業務是7219s后執行,首先找游標,(7219 + 1) % 3600 = 20, 那就是插入下標 = 20 的槽 對應的隊列, cycle_num : floor((7219 + 1) / 3600) = 2

數據插入后,啟用 crontab 或者 go cron 實現 一個 timer,輪詢頻率是 一秒一次, 每次取當前秒對應的時間槽,這是一個隊列,從隊列中取 cycle_num =0 的 cell, 解析數據后, 直接扔給 對應的 task 執行,timer 不涉及具體的業務邏輯,然后 修改 其他的 cell, cycle_num -= 1, 表示轉了一圈了, 依次循環執行下去。

demo: 使用redis 隊列作為存儲

//產生時間輪:
private function createTimerData() {
    //一天 24 個小時    
    for ($i = 0; $i < 23; $i++) {
        //一個小時按秒算        
        for ($j = 0; $j < 3600; $j++) {
            $key = 'task_' . $j;
            $data = [
                'method' => 'send_msg',
                'params' => [
                    'uid' => $i .'--'.$j,
                    'msg' => 'No:' . $i .'--'.$j
                ],
                'cycle_num' => $i
            ];
            Util::redisRPush($key, json_encode($data));
        }
    }
}
 
//timer:
private function executeTask() {
    for ($i = 0; $i < 3600; $i++) {
        $key = 'task_' . $i;
        $len =  Util::redisLLen($key);
        for ($j = 0; $j < $len; $j++) {
            $data = Util::redisLIndex($key, $j);
            $data = json_decode($data, true);
            if ($data['cycle_num'] == 0) {
                //調用 svr-task 處理具體業務
                //從列表中刪除此下標數據                
                Util::redisLSet($key, $j, 'delete');
            } else {
                --$data['cycle_num'];
                Util::redisLSet($key, $j, json_encode($data));
            }
        }
        
        Util::redisLRem($key, 0, 'delete');
    }
}

這里使用了 redis 實現,為了保證數據一致性,可以落盤到數據庫, redis 重啟后,從數據庫 loading 到 redis 。

ps: 還有個小驚喜, redis 隊列不支持 按照對應下標刪除,用了兩個命令實現

1. 將隊列中待刪除的下標的值 標記為 特殊字符串

$redis->lSet($key, $index, 'delete');

2. 刪除此字符串對應的下標

$redis->lRem($key, 0, 'delete');
lrem(key, count, value)
根據參數 count 的值,移除列表中與參數 value 相等的元素。

count 的值可以是以下幾種:

count > 0 : 從表頭開始向表尾搜索,移除與 value 相等的元素,數量為 count 。

count < 0 : 從表尾開始向表頭搜索,移除與 value 相等的元素,數量為 count 的絕對值。

count = 0 : 移除表中所有與 value 相等的值。

分享到:
標簽:時間
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定