【问题标题】:Any way to reduce the amount of concurrent requests to fetch data and cache in nodejs?有什么方法可以减少在nodejs中获取数据和缓存的并发请求量?
【发布时间】:2016-03-11 21:30:13
【问题描述】:

我有一个 Express 应用程序,它需要非常低的响应率 ~

我们计划从数据库中获取一条数据,如果在 Redis 中找到,则返回数据,如果没有,则触发请求并将其保存到 redis,以便下一个请求可以从 Redis 获取它。

我正在进行一些测试,想知道是否有办法减少数据库获取请求的数量?

例如,目前我们的应用程序每个盒子有 300req/s。我们在 AWS 上运行了六个盒子。如果这是第一次在 Redis 中不可用该数据,则可能有大约 500 个请求试图从数据库中获取数据并将其缓存在 Redis 中。我们正在努力减少这个数字。不确定 Node.js 或 Redis 是否有办法处理这个问题。

这是我正在测试的代码。

client.getAsync('key').then(function (data) {
  if(data) {
    console.log(data); // Return this data if found
    res.send(data);
  } else {
    // I'm trying to reduce the number of calls for concurrent requests in this block.
    console.log('not found');
    var dataFromDb = // fetch data from DB
    client.set('key', dataFromDb); // Fire and forget 
    res.send('not found'); // Return not found right away
  }
});

我使用 ab 测试通话

ab -n 20 -c 10 http://localhost:8081/redis

这是我得到的结果

not found
not found
not found
not found
not found
not found
something
not found
something
something
something
something
something
something
something
something
something
something

在此示例中,有 7 个请求尝试获取具有相同数据的数据库并保存到 Redis。

我的问题是,无论如何我可以减少请求的数量吗?因为目前获取数据库的速度非常慢~900ms(我们正在尝试优化)

【问题讨论】:

  • 视情况而定。例如,您可以在缓存被刷新或销毁或重新发布应用程序时预热缓存,您可以在从数据库中获取数据时创建分布式锁,以仅允许一个客户端访问它,然后从 redis 中获取它
  • 感谢您的回答。有什么我可以效仿的例子吗?我不熟悉分布式锁定。
  • 有Redis团队提供的redlock算法
  • 如果你实际上有多个 Redis 实例,分布式锁很好,但如果你只有一个,我发现the same page 中描述的简单锁定机制,因为分布式锁机制运行良好。

标签: node.js caching express concurrency redis


【解决方案1】:

是的,有。我做了同样的事情。我将在这里只描述逻辑。 fetchCache 的方法应该返回一个承诺。您还保留 { cacheKey,promise } 的数组。每次您发送请求时 - 您都会向此数组添加密钥。下次你需要获取缓存时 - 你首先检查数组,如果关键在那里抓住了这个承诺。否则调用 fetchCache。

这是我的代码。它有效,但可能难以阅读。应该给你一个基本的了解。

class DictTranslatableRepo {

    constructor(model) {
        var self = this;
        self.title = model + "s Repo";

        self.model = models[model];
        self.running = {};
        self.curItems = {};
    }


    *start() {
        var self = this;
        var curItems = yield self.model.findAll();
        _.forEach(curItems, function(row) {
            self.curItems[row.key] = row.value;
        });
    };


    *map(from) {
        var self = this;

        if (from == "") return "";

        if (!_.isUndefined(self.curItems[from])) return self.curItems[from];

        if (_.isUndefined(self.running[from])) {
            self.running[from] = [];
            return new Promise(function(resolve, reject) {
                self.running[from].push(resolve);

                self.job(from, function(err, to) {       // Main job
                    var callbackArr = self.running[from];
                    delete self.running[from];
                    _.forEach(callbackArr, function(callback) {
                        callback(to);
                    });
                });
            });
        } else {
            return new Promise(function(resolve, reject) {
                self.running[from].push(resolve);
            });
        }
    };


    job(from, callback) {
        var self = this;
        var to = "as shown";
        co(function*() {
            try {
                to = yield translator.translate(from);
                yield self.model.add({key: from, value: to});
                self.curItems[from] = to;
                callback(null, to);
            } catch (err) {
                callback(err);
                //logger.error("Cant translate entity: " + from);
            }
        }).catch(function(err) {
            // Unhandled Error
            callback(new Error(err));
        });
    };

}

我的 map 方法是你的 fetchCache 方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-04
    • 2021-08-08
    • 1970-01-01
    • 1970-01-01
    • 2016-11-10
    • 2014-08-01
    • 1970-01-01
    • 2022-11-22
    相关资源
    最近更新 更多