後端利用Redis隊列及哈希實現定時推送提醒的三個思路

周煦辰 2016年8月31日

本文介紹了一下本人在開發過程當中遇到「定時推送提醒」的需求的時候所思考的三種解決方案。python

明確問題

首先明確一下這個需求可能包含的幾個「坑」:redis

  1. 系統內的用戶量是否很大?所涉及的提醒任務是否會不少?
  2. 該提醒是不是用戶本身設置的?中途是否會修改?
  3. 推送的時間是否固定(如天天固定時間推送或者每隔一個小時推送等)?仍是用戶自定義推送時間?

所需工具

  • Redis
  • crontab
  • 任何一種Linux上能夠運行的腳本語言(Python、PHP等)

解決方案一:使用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
    # 這裏是自定義的推送代碼

以上這種方法的優勢在於:服務器

  1. 能夠利用Redis實現不一樣服務端應用間的數據,例如你的服務端應用是用PHP寫的,而入隊的腳本但願使用Python,則能夠採用這種方法。
  2. 能夠減輕對數據庫的壓力,且Redis的查詢效率很是高,能夠提高該功能上的性能。

不足之處:工具

  • 假如推送的消息是用戶自定義的,且中間會有修改,那這種方案就會遇到推送時的消息錯誤。

針對不足,咱們來看方案二。性能

解決方案二:仍是使用Redis隊列,可是咱們使用阻塞模式(Blocking)

這個方案咱們仍是會用到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功能。簡單來講Redis的Hash就是一個相似Python中的字典,不一樣的是Redis的一個Hash-Field只能對應一個字符串。

r.hset(hash_key, hask_field, value)

使用該方案能夠在用戶修改計劃任務時快速找到須要推送的消息並修改。出隊的時候使用HAVLS而且遍歷便可。代碼就不放了,畢竟是個偷雞摸狗的思路。

相關文章
相關標籤/搜索