我有一台节点服务器执行以下操作:
我在外部服务器中有一个URL列表,称之为URLServer。当用户点击我的NODE服务器时,我的节点服务器向URLServer发出请求并获取20个URL的列表。一旦我们得到这20个URL,我希望我的节点服务器去获取每个URL的标题,这意味着我将获取URL并创建DOM然后提取标题,我也得到其他数据,所以这就是它必须完成的方式。完成后,我希望将URL和URL的标题保存在内部存储器和/或数据库中。所以我有一个URL缓存和一个标题缓存(我不想一直提取URL)。
我有这样的事情: if(URL-cache为空)从URLServer获取URL并缓存这些URL
然后,我想检查每个网址,看看他们的标题是否在我的缓存中,所以我这样做: 对于每个URL 如果title-cache [URL],那很好 否则获取站点,创建DOM,提取标题+其他数据和缓存
这适用于一个用户,但是当我在服务器中尝试重负载时,服务器将挂起。我已经断定服务器挂起的原因如下:
用户1请求 - 空缓存 - 获取URL以及完成后获取URL的内容 用户2请求 - 缓存仍然看起来是空的,因为用户1的请求尚未完成!因此,用户2再次强制获取URL及其各自的内容。 用户3请求 - 用户1和用户2请求尚未完成,因此同样的问题......
所以,假设我需要获取10个URL,而不是打开10个连接,每个URL一个,然后缓存数据,如果我有20个用户在同一时间点击服务器,我将打开200个连接(每个用户打开10个连接)。
如何阻止用户X(其中X> 1)导致这些事件?我基本上希望服务器关闭一个门并让每个用户等到它已经填充了缓存,然后在填充这些缓存后打开门,有没有办法做到这一点?
答案 0 :(得分:3)
这可以通过使用EventEmitter类来完成。 您设置了一个EventEmitter
var events = require('events');
var eventEmitter = new events.EventEmitter();
然后你处理传入的请求
// here you check for url in cache with your own logic
if(weHaveUrl){
// Respond directly
} else {
// Add one time event watcher for that url
eventEmitter.once('url-' + url, function(data){
// We now have data so respond
});
// Initiate search
searchUrl(url);
}
并将搜索功能包装为发出事件
var urlSearchList = [];
function searchUrl(url){
// We check in case we are already looking for the data
if(urlSearchList.indexOf(url) === -1){
// Append url to list so we won't start a second search
urlSearchList.push(url);
// Your logic for searching url data
// Once recieved we emit the event
eventEmitter.emit('url-' + url);
// And optionally remove from search array
// if we want to repeat the search at some point
urlSearchList.splice(urlSearchList.indexOf(url));
}
}
如果结果在缓存中,或者让他们等待搜索结果然后返回结果,此方法将立即回复请求。
由于我们记录了哪些搜索已启动,因此我们不会多次开始搜索相同的网址,并且每个请求都会在结果出现后立即获得响应。
答案 1 :(得分:1)
避免此事件的最简单方法(顺便称之为“雷鸣般的群体问题”)就是不让任何用户运行fetchURLs
代码。相反,如果缓存检查失败,则将作业添加到作业队列以刷新此数据。然后返回一条消息,说明“我们很抱歉,我们现在没有这些数据 - 请等我们为您取货”。然后,您只需轮询端点以获取数据,一旦它在缓存中,您就可以完成设置并准备就绪。
为了防止作业被100个用户提交到队列,请向另一个全局可用的数据结构添加一个标志(可能与您用于作业队列的结果相同,但不一定如此)。当您遇到缓存未命中时,检查是否存在该缓存密钥的标志,如果该缓存密钥不存在,请设置该标志并将作业提交到您的作业队列。在伪代码中:
if url not in cache:
if url not in jobLocks:
jobLocks.add(url)
jobQueue.add("fetchURLs", data=url)
return "Please wait while we fetch your data"
else:
return cache[url]
当缓存中的数据过时时,您可以使用相同的过程来避免更新时出现雷鸣般的群体。不是删除数据然后重新获取数据,而是将过时数据服务并将作业放入队列中以更新缓存。