Node.js 使用redis作为restapi用户的缓存(以保存Api请求)

Node.js 使用redis作为restapi用户的缓存(以保存Api请求),node.js,rest,api,caching,redis,Node.js,Rest,Api,Caching,Redis,我是一名API用户,对于高流量网站(约1k并发访问者),我的可用请求数量有限。为了保存API请求,我想缓存不太可能更改的特定请求的响应 但是,我希望至少每15秒刷新一次这个redis密钥(API响应)。我想知道最好的方法是什么 我的想法: function players(req, res, next) { redisClient.getAsync('leaderboard:players').then((playersLeaderboard) => { if(!p

我是一名API用户,对于高流量网站(约1k并发访问者),我的可用请求数量有限。为了保存API请求,我想缓存不太可能更改的特定请求的响应

但是,我希望至少每15秒刷新一次这个redis密钥(API响应)。我想知道最好的方法是什么

我的想法:

function players(req, res, next) {
    redisClient.getAsync('leaderboard:players').then((playersLeaderboard) => {
        if(!playersLeaderboard) {
            // We need to get a fresh copy of the playersLeaderboard
        }

        res.set('Cache-Control', 's-maxage=10, max-age=10')
        res.render('leaderboards/players', {playersLeaderboard: playersLeaderboard})
    }).catch((err) => {
        logger.error(err)
    })
}
  • 我认为TTL字段对于这个场景来说很方便。只需将此键的TTL设置为15秒。当我查询这个密钥而它不存在时,我会使用API再次请求它问题:由于这是一个高流量的网站,在收到API的响应之前,我预计会收到大约20-30个请求,这将导致在几毫秒内向API发出20-30个请求。因此,我需要“暂停”所有传入请求,直到有API响应
  • 我的第二个想法是每隔15秒刷新一次钥匙。我可以设置一个每15秒运行一次的后台任务,或者根据页面请求,如果密钥需要刷新,我可以检查控制器。我更喜欢最后一个想法,但因此我需要维护redis key age,这似乎非常昂贵,而且不是内置功能
对于这个用例,您有什么建议

我的控制器代码:

function players(req, res, next) {
    redisClient.getAsync('leaderboard:players').then((playersLeaderboard) => {
        if(!playersLeaderboard) {
            // We need to get a fresh copy of the playersLeaderboard
        }

        res.set('Cache-Control', 's-maxage=10, max-age=10')
        res.render('leaderboards/players', {playersLeaderboard: playersLeaderboard})
    }).catch((err) => {
        logger.error(err)
    })
}

只需在node.js服务器启动时获取并缓存数据,然后设置15秒的间隔来获取新数据并更新缓存。避免在这个用例中使用TTL

function fetchResultsFromApi(cb) {
   apiFunc((err, result) => {
        // do some error handling
       // cache result in redis without ttl
       cb();
    });
}

fetchResultsFromApi(() => {
    app.listen(port);
    setInterval(() => {
        fetchResultsFromApi(() => {});
    }, 15000);
}
优点:

function players(req, res, next) {
    redisClient.getAsync('leaderboard:players').then((playersLeaderboard) => {
        if(!playersLeaderboard) {
            // We need to get a fresh copy of the playersLeaderboard
        }

        res.set('Cache-Control', 's-maxage=10, max-age=10')
        res.render('leaderboards/players', {playersLeaderboard: playersLeaderboard})
    }).catch((err) => {
        logger.error(err)
    })
}
  • 实现起来非常简单
  • 不需要对客户端请求进行排队
  • 超快速响应时间
  • 缺点:

    function players(req, res, next) {
        redisClient.getAsync('leaderboard:players').then((playersLeaderboard) => {
            if(!playersLeaderboard) {
                // We need to get a fresh copy of the playersLeaderboard
            }
    
            res.set('Cache-Control', 's-maxage=10, max-age=10')
            res.render('leaderboards/players', {playersLeaderboard: playersLeaderboard})
        }).catch((err) => {
            logger.error(err)
        })
    }
    
  • 缓存更新可能不会在每15秒之后执行/完成。可能是几毫秒左右。我假设这对您正在做的事情没有多大影响,您可以在15秒之前缩短更新缓存的间隔时间

  • 只需在node.js服务器启动时获取并缓存数据,然后设置15秒的间隔来获取新数据并更新缓存。避免在这个用例中使用TTL

    function fetchResultsFromApi(cb) {
       apiFunc((err, result) => {
            // do some error handling
           // cache result in redis without ttl
           cb();
        });
    }
    
    fetchResultsFromApi(() => {
        app.listen(port);
        setInterval(() => {
            fetchResultsFromApi(() => {});
        }, 15000);
    }
    
    优点:

    function players(req, res, next) {
        redisClient.getAsync('leaderboard:players').then((playersLeaderboard) => {
            if(!playersLeaderboard) {
                // We need to get a fresh copy of the playersLeaderboard
            }
    
            res.set('Cache-Control', 's-maxage=10, max-age=10')
            res.render('leaderboards/players', {playersLeaderboard: playersLeaderboard})
        }).catch((err) => {
            logger.error(err)
        })
    }
    
  • 实现起来非常简单
  • 不需要对客户端请求进行排队
  • 超快速响应时间
  • 缺点:

    function players(req, res, next) {
        redisClient.getAsync('leaderboard:players').then((playersLeaderboard) => {
            if(!playersLeaderboard) {
                // We need to get a fresh copy of the playersLeaderboard
            }
    
            res.set('Cache-Control', 's-maxage=10, max-age=10')
            res.render('leaderboards/players', {playersLeaderboard: playersLeaderboard})
        }).catch((err) => {
            logger.error(err)
        })
    }
    
  • 缓存更新可能不会在每15秒之后执行/完成。可能是几毫秒左右。我假设这对您正在做的事情没有多大影响,您可以在15秒之前缩短更新缓存的间隔时间

  • 我想这比那些典型的“帮助我的代码不工作”更像是一个架构问题

    让我解释一下你的要求

    Q:我希望缓存一些不太可能更改的HTTP请求的响应,并且希望这些缓存的响应每15秒刷新一次。可能吗

    A:是的,你要感谢
    Javascript
    是单线程的,所以它将非常简单

    这里有一些基础知识
    NodeJS
    是一个事件驱动的框架,这意味着在一个时间点,它将只执行一段代码,直到完成为止

    如果在此过程中遇到任何
    aysnc
    调用,它将调用它们并向
    event循环添加一个事件
    ,在收到响应时说“
    callback
    ”。代码例程完成后,它将从队列中弹出下一个事件来运行它们

    基于这些知识,我们知道我们可以通过构建一个
    函数
    来实现这一点,该函数在
    缓存响应每次过期时只对
    更新
    发出一个异步调用。如果异步调用已经在运行,那么只需将它们的回调函数放入队列。这样,您就不会执行多个异步调用来获取新结果

    我不熟悉
    async
    模块,因此我使用
    promises
    提供了一个伪代码示例

    伪代码:

    var fetch_queue = [];
    var cached_result = {
        "cached_result_1": {
            "result" : "test",
            "expiry" : 1501477638 // epoch time 15s in future
        }
    }
    
     var get_cached_result = function(lookup_key) {
        if (cached_result.hasOwnProperty(lookup_key)) {
            if (result_expired(cached_result[lookup_key].expiry)) {
                // Look up cached
                return new Promise(function (resolve) {
                     resolve(cached_result[lookup_key].result);
                });
            }
            else {
                // Not expired, safe to use cached result
                return update_result();
            }
        }
    
    }
    
    var update_result = function() {
        if (fetch_queue.length === 0) {
            // No other request is retrieving an updated result.
            return new Promise(function (resolve, reject) {
                // call your API to get the result.
                // When done call.
                resolve("Your result");
    
                // Inform other requests that an updated response is ready.
                fetch_queue.forEach(function(promise) {
                    promise.resolve("Your result");
                })
    
                // Compute the new expiry epoch time and update the cached_result
            })
        }
        else {
            // Create a promise and park it into the queue
            return new Promise(function(resolve, reject) {
                fetch_queue.push({
                    resolve: resolve,
                    reject: reject
                })
            });
        }
    }
    
    get_cached_result("cached_result_1").then(function(result) {
        // reply the result
    })
    
    注意:顾名思义,代码不是实际的工作解决方案,但概念就在那里

    值得注意的是,
    setInterval
    是一种方法,但它不能保证函数会在15秒时被准确调用。API只确保在预期时间之后会发生某些事情


    然而,建议的解决方案将确保只要缓存的结果过期,下一个查找它的人就会发出请求,而下面的请求将等待初始请求返回。

    我想这更像是一个架构问题,而不是典型的“帮助我的代码不工作”类型

    让我解释一下你的要求

    Q:我希望缓存一些不太可能更改的HTTP请求的响应,并且希望这些缓存的响应每15秒刷新一次。可能吗

    A:是的,你要感谢
    Javascript
    是单线程的,所以它将非常简单

    这里有一些基础知识
    NodeJS
    是一个事件驱动的框架,这意味着在一个时间点,它将只执行一段代码,直到完成为止

    如果在此过程中遇到任何
    aysnc
    调用,它将调用它们并向
    event循环添加一个事件
    ,在收到响应时说“
    callback
    ”。代码例程完成后,它将从队列中弹出下一个事件来运行它们

    基于这些知识,我们知道可以通过构建
    函数