[Redis]處理定時任務的2種思路


http://blog.csdn.net/orangleliu/article/details/52038092

Redis完成類似 at 命令的功能,例如訂單24小時后沒有支付自動關閉,定時發郵件,主要說下任務生成之后怎么觸發消費。

使用 有序集合

思路: 使用sorted Sets的自動排序, key 為任務id,score 為任務計划執行的時間戳,這樣任務在加入sets的時候已經按時間排序,這樣每隔1s(或者其他間隔)去取出sets頂部的數據,小於當前時間的可以通過pop取出來然后去執行。

redis模擬
127.0.0.1:6379> zadd cron 10001 task1 (integer) 1 127.0.0.1:6379> zadd cron 9001 task2 (integer) 1 127.0.0.1:6379> zadd cron 29001 task3 (integer) 1 127.0.0.1:6379> ZRANGE cron 0 -1 withscores 1) "task2" 2) "9001" 3) "task1" 4) "10001" 5) "task3" 6) "29001" 假設當前的時間戳是 15000 127.0.0.1:6379> ZRANGEBYSCORE cron -inf 15000 1) "task2" 2) "task1" 127.0.0.1:6379> ZREM cron task2 (integer) 1 127.0.0.1:6379> ZREM cron task1 (integer) 1 127.0.0.1:6379> ZRANGE cron 0 -1 withscores 1) "task3" 2) "29001"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

上面的測試直接把小於當前時間戳的所有任務都做了一遍,會有些bug,例如找個定時監測程序掛了2天, 對於某些任務可能有效期只有那么10分鍾,重新啟動定時監測程序,就會把過期任務也做了一遍, 那么我們選取任務的時候范圍要更精確一些。

如果當前時間戳是 29100 可以取到 task3 127.0.0.1:6379> ZRANGEBYSCORE cron 28500 29100 1) "task3" 如果當前時間戳是 30600 就無法取到 task3, 注意對過期任務的清理 127.0.0.1:6379> ZRANGEBYSCORE cron 30000 30600 (empty list or set)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

利用鍵過期通知

思路: reids 2.8 有一種 鍵空間通知的機制 Keyspace Notifications (強烈推薦看一遍), 允許客戶端去訂閱一些key的事件,其中就有 key過期的事件,我們可以把 key名稱設置為 task的id等標識(這種方式value的值無法取到,所以只用key來識別任務),expire設置為計划要執行的時間,然后開啟一個客戶端來訂閱消息過期事件,然后處理task。

需要更改redis配置,注意版本要在2.8.0以上, 如果沒有這個key 請添加上,如果有請更改為下面這樣

notify-keyspace-events Ex
  • 1
  • 1

重啟redis,第一個窗口, 開啟訂閱

liuzhizhi@lzz-rmbp|redis_test # redis-cli --csv psubscribe '__keyevent@0__:expired' Reading messages... (press Ctrl-C to quit) "psubscribe","__keyevent@0__:expired",1 "pmessage","__keyevent@0__:expired","__keyevent@0__:expired","task1" "pmessage","__keyevent@0__:expired","__keyevent@0__:expired","task2"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

第二個窗口 設置key

127.0.0.1:6379> set task1 xx OK 127.0.0.1:6379> EXPIRE task1 5 (integer) 1 127.0.0.1:6379> set task2 xx OK 127.0.0.1:6379> EXPIREAT task2 1469525560 (integer) 1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

當key過期的時候就看到第一個窗口的通知了,訂閱的key __keyevent@<db>__:expired 這個格式是固定的,db代表的是數據庫的編號,由於訂閱開啟之后這個庫的所有key過期時間都會被推送過來,所以最好單獨使用一個數據庫來進行隔離。

小結

以上就是使用redis來處理定時任務的兩種思路,常用的編程語言應該都比較容易實現。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM