NodeJS – 强制用户等待全局事件完成

我有一个节点服务器,执行以下操作:

我有一个外部服务器的URL列表,称之为URLServer。 当用户点击我的NODE服务器时,我的节点服务器向URLServer发出请求并获得20个URL的列表。 只要我们获得了这20个URL,我想让我的节点服务器去获得每个URL的标题,这意味着我将获取URL并创build一个DOM,然后提取标题,我也获得其他数据,所以这是它必须完成的方式。 一旦我这样做了,我想要的URL和URL的标题保存在内部存储器和/或数据库。 所以我有一个URLcaching和一个标题caching(我不想总是抓取URL)。

我有这样的事情:如果(URLcaching为空)从URLServer获取URLS并caching这些URL

然后我想检查每个URL,看看他们的标题是否在我的caching中,所以我这样做:对于每个URL如果标题caching[URL],其他获取网站,创buildDOM,提取标题+其他数据和caching

这对于一个用户来说效果很好,但是当我在服务器上尝试一个很重的负载时,服务器就会挂起。 我得出结论服务器挂起的原因如下:

用户1请求 – 空的高速caching – 获取URL并完成获取URL的内容用户2请求 – 由于用户1的请求尚未完成,所以对于该用户来说,高速caching仍为空。 因此,用户2再次强制取回URL和它们各自的内容。 用户3请求 – 用户1和用户2请求尚未完成,所以相同的问题…

因此,假设我有10个URL需要提取,而不是打开10个连接,每个URL一个,然后caching数据,如果我有20个用户在同一时间点击服务器,我将打开200个连接(每个用户打开10个连接)。

如何阻止用户X(其中X> 1)导致这些事件? 我基本上希望服务器closures一个门,并要求每个用户等待,直到它已经填充caching,然后打开门,一旦这些填充,有没有办法做到这一点?

这可以通过使用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); } 

并包装你的searchfunction发出的事件

  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)); } } 

如果结果在caching中,这个方法会立即回答请求,或者会使它们等待search结果,然后返回结果。

由于我们logging了哪些search已启动,因此我们不会多次search相同的url,只要结果可用,每个请求都会得到响应。

避免这个事件的最简单的方法(就是这样称为“雷鸣群体问题”)是没有任何用户运行fetchURLs代码。 相反,如果高速caching检查失败,请将作业添加到作业队列以刷新此数据。 然后返回一条信息,说“我们很抱歉,我们现在没有这些数据 – 请等待,而我们为您取回”。 然后你只是轮询你的端点的数据,一旦它在caching中,你已经准备好了。

为了防止100个用户将作业提交到队列中,请将标志添加到另一个全局可用的数据结构(可能与您的作业队列中使用的数据结构相同,但不一定)。 当您遇到caching未命中时,检查是否存在该caching键的标志,如果不存在,请设置标志并将作业提交到作业队列。 在伪代码中:

 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] 

当caching中的数据过时时,可以使用相同的过程来避免更新时出现雷鸣般的群体。 而不是删除数据,然后重新获取它,服务器陈旧的数据,并在队列中放置一个作业来更新caching。