業務中碰到的需求(抽象描述一下):針對不一樣的用戶可以實現不一樣時間的間隔循環任務。好比在用戶註冊成功24小時後給用戶推送相關短信等相似需求。javascript
使用crontab?過重,且基本不現實,不可能給每個用戶在服務器上生成一個定時任務。java
定時輪詢?IO頻繁且效率過低node
想到常常的使用的redis能夠設置緩存時間,應該會有過時的事件通知吧,查了一下文檔,果真有相關配置,叫作「鍵空間事件通知」。具體說明可參考官方文檔。redis
redis / nodeJs / koa緩存
核心代碼
const { saveClient, subClient } = require('./db/redis') // 存儲實例和訂閱實例須要爲兩個不一樣的實例
const processor = require('./service/task')
const config = require('./config/index')
const innerDistributedLockKey = '&&__&&' // 內部使用的分佈式鎖的key的特徵值
const innerDistributedLockKeyReg = new RegExp(`^${innerDistributedLockKey}`)
saveClient.on('ready', async () => {
saveClient.config('SET', 'notify-keyspace-events', 'Ex') // 存儲實例設置爲推送鍵過時事件
console.log('redis init success')
})
subClient.on('ready', () => { // 服務重啓後依舊能夠初始化全部processor
subClient.subscribe(`__keyevent@${config.redis.sub.db}__:expired`) // 訂閱實例負責訂閱消息
subClient.on('message', async (cahnnel, expiredKey) => {
// 分佈式鎖的key不作監聽處理
if (expiredKey.match(innerDistributedLockKeyReg)) return
// 簡易分佈式鎖,拿到鎖的實例消費event
const cackeKey = `${innerDistributedLockKey}-${expiredKey}`
const lock = await saveClient.set(cackeKey, 2, 'ex', 5, 'nx') // 這裏的用法能夠實現簡易的分佈式鎖
if (lock === 'OK') {
await saveClient.del(cackeKey)
for (let key in processor) {
processor[key](expiredKey) // processor對應的是接收到相關鍵過時通知後執行的業務邏輯,好比推送短信,而後在相關processor中再次set一個定時過時的key
}
}
})
console.log('subClient init success')
})
複製代碼
servide/task (processor)
exports.sendMessage = async function sendMessage(expiredKey, subClient) {
// 只處理相關業務的過時事件
if (expiredKey.match(/^send_message/)) {
const [prefix, userId, type] = expiredKey.split('-')
let user = getUser(userId)
if (user.phone) {
push(message) // 僞代碼
resetRedisKey(expiredKey, ttl) // 從新把key設置爲一段時間後過時,過時後會再次觸發本邏輯
}
}
}
複製代碼
所以須要權衡使用redis的過時機制實現的定時任務的使用場景。服務器
感謝閱讀,轉載請註明出處。 喜歡的朋友能夠關注個人公衆號:雨茗良記,每週會按期更新文章哦,包括但不限於技術。 我是雨茗良記,一個愛作飯的程序猿😜 網絡