本文介紹了一下本人在開發過程當中遇到「定時推送提醒」的需求的時候所思考的三種解決方案。python
首先明確一下這個需求可能包含的幾個「坑」:redis
針對第一個問題,咱們能夠將須要推送的任務做爲一個消息隊列,這樣能夠減輕數據庫的壓力。所以這就引出第一種解決思路:使用Redis的隊列命令實現一個簡單的消息隊列。數據庫
基本思路爲,在一天中的某個時間(例如早上五點這種服務器不會遇到什麼壓力的時間段),經過crontab運行腳本,將推送任務整理完成並逐條插入Redis的隊列中。基本的代碼思路以下:json
import redis import json # 這裏是你的數據庫查詢代碼 # TODO # 這裏將你須要推送的用戶ID、內容等整合爲一個字典 reminder = { 'id': 27149, 'content': 'The meaning of life is 42' } # 將字典編碼爲json字符串 reminder_str = json.dumps(reminder) # 鏈接redis並將數據插入redis中 r = redis.StrictRedis(hostname='localhost', port=6379, db=0) print r.lpush('test_list', reminder_str) # 若是全部數據已入隊 # 能夠在最後插入一個空數據做爲結束的標誌 r.lpush('test_list', '{}')
到須要推送消息的時間(例如早上十點),經過crontab運行以下的出隊命令,進行消息的推送。緩存
import redis import json r = redis.StrictRedis(hostname='localhost', port=6379, db=0) while True: item = r.rpop('test_list') data = json.loads(item) if not data: break # 這裏是自定義的推送代碼
以上這種方法的優勢在於:服務器
不足之處:工具
針對不足,咱們來看方案二。性能
這個方案咱們仍是會用到Redis的隊列,不一樣的是咱們會使用到Redis提供的阻塞出隊接口(blpop、brpop)。阻塞出隊簡單來講,就是在出隊命令在沒有接收到隊列內的數據前,會掛起,直到在設置的阻塞時間內隊列中有新的數據入隊,則彈出數據,命令結束;若是在設置的阻塞時間內沒有數據入隊,則返回空;若是阻塞時間被設置爲0,則進程將一直被掛起直到隊列中有數據入隊。編碼
若使用阻塞模式,假設咱們的推送時間爲早上的十點,那麼在9:58左右,能夠運行入隊命令,將須要推送的數據加入Redis隊列中。代碼示例如方案一,這裏就不放了。code
到了十點整,運行以下代碼進行出隊。
import redis import json r = redis.StrictRedis(hostname='localhost', port=6379, db=0) while True: item = r.brpop('test_list', 0) data = json.loads(item) if not data: break # 這裏是自定義的推送代碼
注意第6行代碼item = r.brpop('test_list', 0)
,第二個參數即爲阻塞的等待時間。
使用這種方法基本能夠保證推送消息的準確性,一邊生成消息塞進隊列,一邊從隊列裏拿便可。可是彷佛這種方式並無發揮到「緩存」的優點。
這個方案中咱們會使用到Redis中的Hash功能。簡單來講Redis的Hash就是一個相似Python中的字典,不一樣的是Redis的一個Hash-Field只能對應一個字符串。
r.hset(hash_key, hask_field, value)
使用該方案能夠在用戶修改計劃任務時快速找到須要推送的消息並修改。出隊的時候使用HAVLS
而且遍歷便可。代碼就不放了,畢竟是個偷雞摸狗的思路。