node.js - 同时对所有请求进行单个响应,node.js: 多个请求非常重

  显示原文与译文双语对照的内容
0 0

我很抱歉我无法找到一个更好的标题。

我总是有这个问题,但我觉得我的解决方案有点脏。

我是来为这个问题寻找更好的解决方案。

以下是场景:

你的服务器在特殊的http请求( 比如为 url/生成游戏服务器横幅生成带有统计信息的游戏) 上执行了非常繁重的任务。 对服务器执行HTTP请求的人将得到相同的响应。 这个响应将被缓存很长一段时间。

例如,在浏览器截图生成HTTP请求时,服务器会生成一个 phantomjs,捕获屏幕截图,并长时间缓存。 这里请求之后的HTTP请求应该命中缓存。

场景的伪代码:

server.get(":urlname.png", function(req, res, next) {
 var cached = cache.get(req.params_urlname);
 if (cached) {
 res.send(cached);
 return;
 }
//This will take very long time
 generateScreenshot(req.params_urlname, function(pngData) {
 cache.set(req.params_urlname, pngData, LONG_TIME);
 res.send(cached);
 });
});

问题是:

假设你有一个生成 URL ( http://yourserver.com/generate-screenshot/google.png )的屏幕截图。 屏幕截图未生成,也未缓存。

你在一个非常受欢迎的论坛上发布了网址,同时在那个网址上有 1000个HTTP请求 ! 这意味着你的服务器将必须生成 1000 phantomjs,并且它们将在同一时间生成 google.com 屏幕截图。

换句话说,重函数应该只执行一次来生成缓存。

我当前的代码解决方案有:

var pendingResponse = {};
server.get(":urlname.png", function(req, res, next) {
 var cached = cache.get(req.params_urlname);
 if (cached) {
 res.send(cached);
 return;
 }
//The screenshot is currently generating for other request. Let's mark this response as pending.
 if (req.params_urlname in pendingResponse) {
 pendingResponse[req.params_urlname].push(res);
 return;
 }
//The screenshot needs to be generated now. Let's mark the future response as pending.
 req.params_urlname[req.params_urlname] = [];
//This will take very long time
 generateScreenshot(req.params_urlname, function(pngData) {
 cache.set(req.params_urlname, pngData, LONG_TIME);
 res.send(cached);
//Let's respond all the pending responses with the PNG data as well.
 for (var i in pendingResponse[req.params_urlname]) {
 var pRes = pendingResponse[req.params_urlname][i];
 pRes.send(cached);
 }
//No longer mark the future responses as pending.
 delete pendingResponse[req.params_urlname];
 });
});

这个解决方案很有效,但是我认为这个解决方案很脏,因为它根本不能重用。 此外,我认为它可能会导致资源泄漏。 是否有更好的解决方案/库?

时间:原作者:6个回答

0 0

下面是使用 memoizee 包( 不仅消除了进行计算的必要性,而且还允许删除"缓存"完全) 执行这里结果缓存的proof-of-concept服务器:

var express = require('express');
var memoize = require('memoizee');
function longComputation(urlName, cb) {
 console.log('called for ' + urlName);
 setTimeout(function () {
 console.log('done for ' + urlName);
 cb();
 }, 5000);
}
var memoizedLongComputation = memoize(longComputation, {async: true, maxAge: 20000});
var app = express();
app.get('/hang/:urlname', function (req, res, next) {
 memoizedLongComputation(req.params.urlname, function () {
 res.send('hang over');
 });
});
app.listen(3000);

在这里,我们将结果缓存为 20秒。

当我启动服务器并在 shell 中运行时

for i in `seq 1 10`; do curl http://localhost:3000/hang/url1; done

如果只打开几个浏览器标签,并快速导航到 http://localhost:3000/hang/url1,我会看到控制台中的一个 "called for url1" 和1 个 "done for url1" 消息,这意味着只有一个"真实" longComputation 调用被调用。 如果我在( 小于 20秒) 之后重复它,则没有额外的消息,并且结果被暂时返回,因为它们被缓存。 如果以后重复命令( 超过 20秒),就会有一次调用。

原作者:
...