Запрос с прокси застревает без кода ошибки в Node JS - PullRequest
0 голосов
/ 21 июня 2019

Я пытаюсь выяснить, почему иногда некоторые запросы просто зависают.Я проанализировал некоторые ошибки, но время от времени он застревает .

Что он делает:

  1. У меня есть forEach итерациямассив доменов.
  2. После этого они начинают работать последовательно.
  3. Важно отметить, что я использую прокси-ротатор ( одиночная точка входа IP и поворот в каждом запросе ).Я знаю, что они не очень надежны, но я думаю, что есть какой-то способ просто убить запрос, если это займет более N секунд. .

Вот как выглядит код:

/**
 * Asynchronously fetches the page referred to by `url`.
 *
 * @param {String} url - the URL of the page to be fetched
 * @return {Promise} promise to a cheerio-processed page
 */
async function fetchPage(domain) {
    return new Promise((resolve, reject) => {
    request({
      url: `https://www.google.com`,
      proxy: 'http://13.82.26.121:10000',
      timeout: 5000,
      // The below parameters are specific to request-retry
      maxAttempts: 5,   // (default) try 5 times
      retryDelay: 5000,  // (default) wait for 5s before trying again
      // retryStrategy: request.RetryStrategies.HTTPOrNetworkError, // (default) retry on 5xx or network errors
      // Headers
      headers: {
        Host: 'www.google.com',
        Referer: `https://www.similarweb.com/website/${domain}`,
        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36',
        'Upgrade-Insecure-Requests': 1,
        'Cache-Control': 'no-cache',
        Connection: 'keep-alive',
        Pragma: 'no-cache',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'Accept-Language': 'en-US,en;q=0.8,es;q=0.6',
        Cookie: cookie
      }
    }, (error, response, body) => {
      if (error) {

        console.log(error.code === 'ESOCKETTIMEDOUT');
        // Set to `true` if the timeout was a ESOCKETTIMEDOUT
        console.log(error.code === 'ETIMEDOUT');
        // Set to `true` if the timeout was a connection timeout, `false` or
        console.log(error.code === 'ECONNRESET');
        // `undefined` otherwise.
        console.log(error.connect === true);
        process.exit(0);
        return reject(error)
      }

      if (response) {
        console.log('Attempts: ' + response.attempts);
      }

      const data = {
        domain: domain,
        totalVisits: '',
        avgVisitDuration: '',
        pagesPerVisit: '',
        bounceRate: ''
      }

      if (!body) {
        return resolve(data)
      }

      if (response.headers.cookie) {
        cookie = response.headers['set-cookie']
        fs.writeFile(file, cookie, () => {})
      }

      const $ = cheerio.load(body)

      var totalVisits = $('[data-type="visits"] .engagementInfo-valueNumber').html();
      var avgVisitDuration = $('[data-type="time"] .engagementInfo-valueNumber').html();
      var pagesPerVisit = $('[data-type="ppv"] .engagementInfo-valueNumber').html();
      var bounceRate = $('[data-type="bounce"] .engagementInfo-valueNumber').html();

      data.totalVisits = totalVisits;
      data.avgVisitDuration = avgVisitDuration;
      data.pagesPerVisit = pagesPerVisit;
      data.bounceRate = bounceRate;

      console.log(data);
      resolve(data)

    })
  })
}

Я пытался добавить тайм-аут в запрос и использовать другие пакеты с большим количеством функций, но мне кажется, что ничего не работает на 100%.Это всегда застревает и не выводится в консоли.

Вот как я вызываю функцию:

async function sleep(millis) {
    return new Promise(resolve => setTimeout(resolve, millis));
}

async function run() {
    const domains = await fetchUrls(INITIAL_URL);

    for (const domain of domains[0]) {
        await sleep(1000);
        console.log("Fetching: " + domain);
        const $ = await fetchPage(domain);
        // do stuff with cheerio-processed page
    }
    return
}

run();

1 Ответ

0 голосов
/ 01 июля 2019

Не могли бы вы применить собственный тайм-аут, используя Promise.race()?

Метод Promise.race () возвращает обещание, которое выполняется или отклоняется, как только одно из обещанийв итерируемом выполнении или отклонении, со значением или причиной из этого обещания.

Это не отменит исходный запрос, но убедится, что ваша функция выполнения может продолжаться, а не зависать.

Чтобы сделать его чистым, вы можете сделать небольшую функцию-обертку, которая выполняет гонку:

async function fetchPageTimeout(url, timeoutSeconds = 30) {

  let racedTimeout = null;

  const returnData = await Promise.race([
    fetchPage(url),
    new Promise((_, reject) => {
      racedTimeout = setTimeout(() => {
        return reject(new Error(`Timeout after ${timeoutSeconds} secs`));
      }, timeoutSeconds * 1000)
    })
  ]);

  if (racedTimeout) {
    clearTimeout(racedTimeout);
  }

  return returnData;
}

Вам может потребоваться перехватить исключение тайм-аута в функции run, или вы можете изменить fetchPageTimeout, так что он всегда разрешается с пустыми данными, если происходит тайм-аут.

Если он все еще застревает, вы можете проверить пакет Why-is-node-running, который может дать некоторое представление о том, что может ожидать ваш код.

...