繁体   English   中英

Node.js:在整个应用程序中共享连接 object

[英]Node.js: share connection object throughout the application

我在使用 puppeteer 实现generic-pool时遇到问题。 以下是我的相关代码部分。

更新

感谢@Jacob 的帮助,我对这个概念及其工作原理更加清楚,并且代码也更具可读性和清晰性。 我仍然遇到在每个请求上都创建通用池的问题。 我如何确保每次都使用相同的通用池而不是创建新池

浏览器-pool.js

const genericPool = require('generic-pool');
const puppeteer = require('puppeteer');

class BrowserPool {
  static async getPool() {
    const browserParams = process.env.NODE_ENV == 'development' ? {
      headless: false,
      devtools: false,
      executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
     }
     :
     {
       headless: true,
       devtools: false,
       executablePath: 'google-chrome-unstable',
       args: ['--no-sandbox', '--disable-dev-shm-usage']
     };

    const factory = {
      create: function() {
        return puppeteer.launch(browserParams);
      },
      destroy: function(instance) {
        console.log('closing browser in hrere.....');
        instance.close();
      }
    };

    const opts = {
      max: 5
    };

    this.myBrowserPool = genericPool.createPool(factory, opts);
  }

  static async returnPool() {
    if (this.myBrowserPool == "") {
      getPool();
    }

    return this.myBrowserPool.acquire();
  }
}

BrowserPool.myBrowserPool = null;
module.exports =  BrowserPool;

进程-export.js

const BrowserPool = require('./browser-pool');
async function performExport(params){
  const myPool = BrowserPool.getPool();
  const resp = BrowserPool.myBrowserPool.acquire().then(async function(client){
    try {
      const url = config.get('url');
      const page = await client.newPage();

      await page.goto(url, {waitUntil: ['networkidle2', 'domcontentloaded']});
      let gotoUrl = `${url}/dashboards/${exportParams.dashboardId}?csv_export_id=${exportParams.csvExportId}`;
//more processing
      await page.goto(gotoUrl, {waitUntil: 'networkidle2' })
      await myPool().myBrowserPool.release(client);
      return Data;
    } catch(err) {
      try {
        const l = await BrowserPool.myBrowserPool.destroy(client);
      } catch(e) {
      }
      return err;
    }
  }).catch(function(err) {
    return err;
  });
  return resp;
}

module.exports.performExport = performExport;

我的理解是

1)当应用程序启动时,我可以启动例如2 chromium实例,然后当我想访问一个页面时,我可以使用两个连接中的任何一个,因此浏览器基本上是打开的,我们提高了性能,因为浏览器启动可以慢慢来。 这个对吗?

2)我在哪里放置acquire()代码,我知道这应该在app.js中,所以我们在应用程序启动时获取实例,但是我的pupeteer代码在不同的文件中,我如何通过浏览器在包含我的 pupeteer 代码的文件中引用。

当我使用上面的代码时,每次都会启动一个新的浏览器实例,并且不考虑max属性,它会打开请求的多个实例。

我很抱歉,如果它非常考验,我可能没有完全理解这个概念。 澄清这一点的任何帮助都会非常有帮助。

使用池时,您需要使用.acquire()来获取 object,然后在完成后使用 .release( .release() ,以便将 object 返回到池中并提供给其他东西。 如果不使用.release() ,您可能根本就没有池。 我喜欢在池中使用这个辅助模式:

class BrowserPool {
  // ...

  static async withBrowser(fn) {
    const pool = BrowserPool.myBrowserPool;
    const browser = await pool.acquire();
    try {
      await fn(browser);
    } finally {
      pool.release(browser);
    }
  }
}

这可以像这样在代码中的任何地方使用:

await BrowserPool.withBrowser(async browser => {
  await browser.doSomeThing();
  await browser.doSomeThingElse();
});

关键是finally子句确保无论您的任务完成还是抛出错误,您每次都会干净地将浏览器释放回池中。

听起来您可能也向后考虑了max选项的概念,并且期望浏览器实例被生成到max 相反, max的意思是“只创建max数量的资源”。 例如,如果您尝试在没有释放任何内容的情况下获取第六个资源,则acquire(...)调用将阻塞,直到一个项目返回到池中。

另一方面, min选项的意思是“始终至少保留这么多物品”,您可以使用它来预先分配资源。 如果要提前创建 5 个项目,请将min设置为 5。如果要创建 5 个项目并且只创建 5 个项目,请将minmax都设置为 5。

更新

我注意到在您的原始代码中,您会在出现错误时销毁并在没有错误时释放。 仍然希望像我的一样使用包装器 function 来集中所有资源获取/释放逻辑(SRP 方法)的好处。 以下是如何更新它以自动销毁错误:

class BrowserPool {
  // ...

  static async withBrowser(fn) {
    const pool = BrowserPool.myBrowserPool;
    const browser = await pool.acquire();
    try {
      await fn(browser);
      pool.release(browser);
    } catch (err) {
      await pool.destroy(browser);
      throw err;
    }
  }
}

附录

如果您采用异步 function 而不是混合异步 function 内容和 Promise 回调内容,那么弄清楚代码中发生了什么会更容易。 下面是如何重写它:

async function performExport(params){
  const myPool = BrowserPool.myBrowserPool;
  const client = await myPool.acquire();

  try {
    const url = config.get('url');
    const page = await client.newPage();

    await page.goto(url, {waitUntil: ['networkidle2', 'domcontentloaded']});
    let gotoUrl = `${url}/dashboards/${exportParams.dashboardId}?csv_export_id=${exportParams.csvExportId}`;
//more processing
    await page.goto(gotoUrl, {waitUntil: 'networkidle2' })
    await myPool.release(client);
    return Data;
  } catch(err) {
    try {
      const l = await myPool.destroy(client);
    } catch(e) {
    }
    return err; // Are you sure you want to do this? Would suggest throw err.
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM