如何限制Express.js中的通话次数?

时间:2019-03-29 12:15:14

标签: express promise puppeteer

我正在使用express来显示使用puppeteer进行某些网络抓取的结果,但是我遇到了性能问题。

我多次调用了scraper文件,因为我想一次获得多个结果。

例如:

const express = require('express')
const app = express()
const scraper = require('./scrapers/scraper.js');

app.get('/getResults', function(req, res, next) {
  const url = 'http://www.example.com';
    const val1 = new Promise((resolve, reject) => {
        scraper
            .getPrice(results, url, nights)
            .then(data => {
        resolve(data)
                })
        .catch(err => reject('Medium scrape failed'))
    })
  const url = 'http://www.example.com';
    const val2 = new Promise((resolve, reject) => {
        scraper
            .getPrice(results, url, nights)
            .then(data => {
        resolve(data)
                })
        .catch(err => reject('Medium scrape failed'))

  const url = 'http://www.example.com';
    const val3 = new Promise((resolve, reject) => {
        scraper
            .getPrice(results, url, nights)
            .then(data => {
        resolve(data)
                })
        .catch(err => reject('Medium scrape failed'))
  const url = 'http://www.example.com';
    const val4 = new Promise((resolve, reject) => {
        scraper
            .getPrice(results, url, nights)
            .then(data => {
        resolve(data)
                })
        .catch(err => reject('Medium scrape failed'))
  Promise.all([val1, val2, val3, val4])
    .then(data => {
        console.log(data)
    })
        .catch(err => res.status(500).send(err))
}

上面的代码将一次调用4个scraper.js文件,但是如果上一个完成后又要调用每个文件,我该怎么办?我的意思是,当val1完成时,它应该运行val2,依此类推。

实际上,我的代码调用了scraper文件18次,这对计算机性能没有好处,因为puppeteer基于Chromium,并且实际上一次打开了18次新的Chromium实例。

我什至在运行该错误时也会收到该错误:

(node:26600) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 exit listeners added. Use emitter.setMaxListeners() to increase limit

2 个答案:

答案 0 :(得分:1)

异步等待

您可以使用async等待来编写代码。有趣的是,您可以处理所有错误,并使用promises自动返回值。

app.get('/getResults', async function(req, res, next) { //<-- notice the async here
 try{
  const val1 = await scraper.getPrice(results, url, nights)
  const val2 = await scraper.getPrice(results, url, nights)
  const val3 = await scraper.getPrice(results, url, nights)
  const val4 = await scraper.getPrice(results, url, nights)
  return res.send([val1, val2, val3, val4])
 } catch(err) {
   res.status(500).send(err)
 }
})

p-limit

您可以使用名为p-limit的程序包,该程序包以有限的并发性运行多个承诺返回和异步功能。

const pLimit = require('p-limit');

const limit = pLimit(1);

const input = [
    limit(() => scraper.getPrice(results, url, nights)),
    limit(() => scraper.getPrice(results, url, nights)),
    limit(() => scraper.getPrice(results, url, nights))
];

(async () => {
    // Only one promise is run at once
    const result = await Promise.all(input);
    console.log(result);
})();

for..of循环

您可以优化这些代码并减少代码重复。借助async..await和for..of,您可以进一步减少代码,

// assuming you have these urls
const urls = [
 'http://example.com', 'http://example.com',  'http://example.com'
];

const results = []
for(let url of urls){
  const data = await scraper.getPrice(results, url, nights);
  results.push(data)
}
console.log(results)

答案 1 :(得分:0)

您知道可以连续做出承诺吗?

val1.then(v1 => return val2).then(v2=> {...})

您应该打开一个新的Chromium标签,而不是实例。 (您只是混淆了概念吗?)

最重要的是-您需要更好地管理下载过程。队列在这里最好。可以很简单:可以确保正在运行的进程不超过n个或更高级:可以监视服务器资源。

您也许可以找到一些包裹。如果没有合适的方法,请记住在出现问题时处理该情况,Node不会注意到该过程的结束。

我可以交替使用方法:

  • 将URL标记为已下载,如果在给定时间内未检索到,则返回队列(更具体地说:指定何时重新下载URL。下载时,下载后为+1分钟)例如1个月)
  • 我保存了下载过程的PID,并定期检查它是否有效

还有rate-limits控制HTTP调用的数量。在端点上,使用IP的同时订单数。