通过NodeJS

时间:2017-05-11 12:29:05

标签: node.js http

我需要通过HTTP检查本地服务器上大约300.000个URL的可用性。这些文件不在本地文件系统中,而是在键值存储中,目标是检查每个需要访问这些文件的系统是否都可以使用vial HTTP。

为此,我会使用HTTP HEAD请求为每个找到的文件返回HTTP 200,为每个找不到的文件返回404.

问题是,如果我一次做太多请求,我会受到nginx或本地代理的限制,因此无法确定文件是否真的可以访问。

我查找文件可用性的方法如下:

...
const request = require('request'); // Using the request lib.
... 
const checkEntity = entity => {
logger.debug("HTTP HEAD ", entity);
return request({ method: "HEAD", uri: entity.url })
    .then(result => {
        logger.debug("Successfully retrieved file: " + entity.url);
        entity.valid = result != undefined;
    })
    .catch(err => {
        logger.debug("Failed to retrieve file.", err);
        entity.valid = false;
    });
}

如果我多次调用此函数,事情会按预期工作。当试图在递归承诺中运行它时,我很快超过了最大堆栈。为每个调用设置一个承诺会导致内存使用过多。

怎么能解决这个问题?

1 个答案:

答案 0 :(得分:0)

这个问题可以通过以下步骤解决:

  1. 定义一个队列并存储您的所有entities(所有需要检查的网址)。
  2. 定义要并行发送的HTTP请求数。这个数字不应太小或太大。如果它太小,该程序效率不高。如果它太大,将发生当前的请求数量限制问题。我们将其设为N,您可以根据服务器状态定义合理的数字。
  3. 在开始时并行发送N个HTTP请求。
  4. 当1个请求完成后,从队列中获取新的entity并发送新请求。要在请求完成后收到通知,您可以在checkEntity函数中添加回调参数。
  5. 这样,最大HTTP请求数永远不会超过N

    以下是基于代码段的伪代码示例:

    let allEntities = [...]; // 300000 URLs
    let finishedEntities = [];
    
    const request = require('request'); // Using the request lib.
    ...
    const checkEntity = function(entity, callback) {
      logger.debug("HTTP HEAD ", entity);
      return request({ method: "HEAD", uri: entity.url })
        .then(result => {
          logger.debug("Successfully retrieved file: " + entity.url);
          entity.valid = result != undefined;
          callback(entity);
        })
        .catch(err => {
          logger.debug("Failed to retrieve file.", err);
          entity.valid = false;
          callback(entity)
        });
    }
    function checkEntityCallback(entity) {
      finishedEntities.push(entity);
      let newEntity = allEntities.shift();
      if (newEntity) {
        checkEntity(allEntities.shift(), checkEntityCallback);
      }
    }
    
    for (let i=0; i<10; i++) {
      checkEntity(allEntities.shift(), checkEntityCallback);
    }
    

    为了便于理解,您可以更改request的使用并删除所有承诺内容:

    const checkEntity = function(entity, callback) {
      logger.debug("HTTP HEAD ", entity);
      request({ method: "HEAD", uri: entity.url }, function(error, response, body) {
        if (error) {
          logger.debug("Failed to retrieve file.", error);
          entity.valid = false;
          callback(entity);
          return;
        }
        logger.debug("Successfully retrieved file: " + entity.url);
        entity.valid = body != undefined;
        callback(entity);
      });
    }