为了防止多热点,消耗大的内容被频繁请求,会大量占用服务器资源。导致服务器响应慢或者卡死的问题。控制访问数量解决此类问题。
在获取时先判断是否存在正在请求相同的缓存。存在(拿不到锁)则进行等待,定时轮询(次数可以限制次数和时长直接进行返回)。不存在(拿到锁)则进行查询,查询结束解锁(需捕获异常防止卡死)。
附上代码:
const redis = require('redis')
// 设置集合锁
global.REDIS_LOCKS = {}
// 允许的并发数量,同个请求允许有2个并发, 可以自行配置
global.REDIS_LOCKS_MAX = 2
class Cache {
/**
* 构造缓存实例
* @param {Integer} db 0-15 缓存库名
*/
constructor (db) {
this.options = { url: `${global.REDIS_CONFIG}/${db}`, retry_strategy: retryStrategy }
}
/**
* 试读取内容
* @param {Array} args Key值,可以是某个函数的参数数组arguments,也可以是字符串
* @param {Function} callback 缓存中不存在的内容,则执行某个过程,将返回值存入缓存
* @returns {Object} callback的返回值
*/
async tryGetContent (args, callback, expire = defaultExpireTime) {
let key = this._args2Key(args)
let value = await this.getContent(key)
if (value) {
return JSON.parse(value)
} else {
// 加锁
if (REDIS_LOCKS[key] && (REDIS_LOCKS[key] >= REDIS_LOCKS_MAX)) {
// 等待1秒后再查询
await new Promise(resolve => setTimeout(resolve, 1000))
return await this.tryGetContent(args, callback, expire)
} else {
REDIS_LOCKS[key] = REDIS_LOCKS[key] ? REDIS_LOCKS[key] + 1 : 1
}
let result
// 捕获错误解锁,防止异常无法解锁
try {
result = await callback()
if (result || result === 0) {
await this.setContent(key, JSON.stringify(result), expire)
}
} catch (e) {
console.log('tryGetContent', e)
}
// 解锁
REDIS_LOCKS[key] = REDIS_LOCKS[key] - 1
// else {
// console.error('获取内容不存在,id为:', key)
// }
return result
}
}
}
module.exports = Cache