Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/379.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript Node.js:在整个应用程序中共享连接对象_Javascript_Node.js - Fatal编程技术网

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

Javascript Node.js:在整个应用程序中共享连接对象,javascript,node.js,Javascript,Node.js,我在使用木偶演员实现通用池时遇到问题。下面是我的代码的相关部分 更新 感谢@Jacob的帮助,我对这个概念和它的工作原理更加清楚,代码也更加可读和清晰。我仍然有一些问题,每次请求都会创建一个通用池。如何确保每次都使用相同的通用池,而不是创建新的通用池 浏览器池.js const genericPool = require('generic-pool'); const puppeteer = require('puppeteer'); class BrowserPool { static a

我在使用木偶演员实现
通用池
时遇到问题。下面是我的代码的相关部分

更新

感谢@Jacob的帮助,我对这个概念和它的工作原理更加清楚,代码也更加可读和清晰。我仍然有一些问题,每次请求都会创建一个通用池。如何确保每次都使用相同的通用池,而不是创建新的通用池

浏览器池.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;
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;
处理导出.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;
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
中,所以我们在应用程序启动时获取实例,但是我的pupeter代码位于不同的文件中,我如何在包含我的pupeter代码的文件中传递浏览器引用

当我使用上述代码时,每次都会出现一个新的浏览器实例,并且不会考虑
max
属性,它会根据请求的实例数打开


如果这是一件非常艰难的事情,我很抱歉,我可能还没有完全理解这个概念。任何澄清这一点的帮助都会非常有用。

使用池时,您需要使用
.acquire()
获取对象,然后使用
.release()
将对象返回池并提供给其他对象。如果不使用
.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个项目,请将
min
max
都设置为5

更新

我注意到,在您的原始代码中,如果出现错误,您将进行销毁,如果没有错误,则将其释放。仍然希望像我这样的包装器函数能够集中所有资源获取/释放逻辑(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;
    }
  }
}
附录

如果采用异步函数,而不是混合使用异步函数和承诺回调,那么了解代码中发生了什么将更容易。以下是如何重写它:

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.
  }
}

非常感谢你的回答,我一直努力想完全理解这一点,这个回答很有帮助。我还试图理解,当我的应用程序启动时,我应该看到chrom实例已经启动了吗?或者只有在调用acquire时实例才会启动?在这种情况下,这与不在池中使用有何不同?您的意思是不调用发布;您是否在finally子句中调用release,比如在我的代码中,在异步函数中?它应该总是在承诺解决/拒绝后被调用。
destroy
是另一回事
release
将项目返回到池中,以便以后的代码可以使用相同的对象
destroy
适用于您永远不想再次使用该对象的情况。如果您在多个位置有池获取/释放代码,我建议您进行更改,因为这样很难在脑海中清晰地描述资源是如何维护的。我展示的模式的好处是,您将所有浏览器资源管理集中在一个地方。我只在这里留下评论。游泳池运作良好。但是,该代码没有同时运行任何请求,而且它还在它们自己的子进程中运行请求,因此它们无法共享浏览器池。解决方案是不使用池,而是使用工作队列库的并发控制。