Node.js 如何对在GraphQL中创建headless Chrome实例的函数的多个调用进行分组

Node.js 如何对在GraphQL中创建headless Chrome实例的函数的多个调用进行分组,node.js,graphql,puppeteer,Node.js,Graphql,Puppeteer,我有一个运行GraphQL的NodeJS服务器。我的一个查询从API获取“项目”列表并返回URL。这个URL然后被传递给另一个函数,该函数获取该网站的屏幕截图(使用一个NodeJS包,它是Puppeter的包装器) 我的问题是,当我运行这个程序时,如果有更多的项目需要去做,并为它生成一个屏幕截图。它为每个数据响应对象运行屏幕截图功能(见下文),因此在服务器上创建了一个单独的无头浏览器,因此我的服务器很快就会耗尽内存并崩溃 { "data": { "projects": [

我有一个运行GraphQL的NodeJS服务器。我的一个查询从API获取“项目”列表并返回URL。这个URL然后被传递给另一个函数,该函数获取该网站的屏幕截图(使用一个NodeJS包,它是Puppeter的包装器)

我的问题是,当我运行这个程序时,如果有更多的项目需要去做,并为它生成一个屏幕截图。它为每个数据响应对象运行屏幕截图功能(见下文),因此在服务器上创建了一个单独的无头浏览器,因此我的服务器很快就会耗尽内存并崩溃

{
  "data": {
    "projects": [
      {
        "screenshot": {
          "url": "https://someurl.com/randomid/screenshot.png"
        }
      },
      {
        "screenshot": {
          "url": "https://someurl.com/randomid/screenshot.png"
        }
      }
    ]
  }
}
这是我为上下文的屏幕截图逻辑编写的代码的简化版本:

const webshotScreenshot = (title, url) => {
  return new Promise(async (resolve, reject) => {

    /** Create screenshot options */
    const options = {
      height: 600,
      scaleFactor: 2,
      width: 1200,
      launchOptions: {
        headless: true,
        args: ['--no-sandbox']
      }
    };

    /** Capture website */
    await captureWebsite.base64(url.href, options)
      .then(async response => {
        /** Create filename and location */
        let folder = `output/screenshots/${_.kebabCase(title)}`;

        /** Create directory */
        createDirectory(folder);

        /** Create filename */
        const filename = 'screenshot.png';
        const fileOutput = `${folder}/${filename}`;

        return await fs.writeFile(fileOutput, response, 'base64', (err) => {
          if (err) {
            // handle error
          }

          /** File saved successfully */
          resolve({
            fileOutput
          });
        });
      })
      .catch(err => {
        // handle error
      });
  });
};
我想知道的是,我如何修改这个逻辑,以:

  • 避免为每个函数调用创建headless实例?基本上,对响应中提供的每个URL进行分组/批处理,并一次性处理
  • 在进行此处理时,我能做些什么来帮助减少服务器上的负载,以避免内存不足
  • 我现在已经在节点参数和设置内存限制等方面做了很多工作,但我认为现在最重要的是尽可能提高效率。

    您可以利用它来批量调用获得屏幕截图的任何函数。这个函数应该获取一个URL数组,并返回一个与结果图像数组解析的承诺

    const DataLoader = require('dataloader')
    
    const screenshotLoader = new DataLoader(async (urls) => {
      // see below
    })
    
    // Inject a new DataLoader instance into your context, then inside your resolver
    screenshotLoader.load(yourUrl)
    
    它看起来不像
    捕获网站
    支持传递多个URL。这意味着,每次调用captureWebsite.base64都会启动一个新的木偶玩家实例。所以,
    Promise.all
    已退出,但您有两个选择:

  • 按顺序处理屏幕捕获。这将是缓慢的,但应确保一次只有一个木偶演员的实例
  • 利用
    bluebird
    或类似库并行运行请求,但有一定限制:
  • 切换到直接使用木偶师,或其他支持拍摄多个屏幕截图的库
  • const DataLoader = require('dataloader')
    
    const screenshotLoader = new DataLoader(async (urls) => {
      // see below
    })
    
    // Inject a new DataLoader instance into your context, then inside your resolver
    screenshotLoader.load(yourUrl)
    
    const images = []
    for (const url in urls) {
      const image = await captureWebsite.base64(url, options)
      images.push(image)
    }
    return images
    
    const concurrency = 3 // 3 at a time
    return Bluebird.map(urls, (url) => {
      return captureWebsite.base64(url, options)
    }, { concurrency })
    
    const browser = await puppeteer.launch({args: ['--no-sandbox', '--disable-setuid-sandbox']});
    const page = await browser.newPage();
    
    for (const url in urls) {
      const image = await captureWebsite.base64(url, options)
      await page.goto(url);
      await page.screenshot(/* path and other screenshot options */);
    }
    
    await browser.close();