diff --git a/bin/config.json b/bin/config.json index 7e4c716..cc24e8a 100755 --- a/bin/config.json +++ b/bin/config.json @@ -2,5 +2,7 @@ "logs" : "/Volumes/Angela.Fang/Phoebe/uTorrent/logs", "port" : 7003, "debug" : true, - "token" : false + "token" : false, + "size" : 4, + "ignore" : true } \ No newline at end of file diff --git a/package.json b/package.json index a1b9fd3..caf5315 100644 --- a/package.json +++ b/package.json @@ -46,7 +46,7 @@ "crypto-js": "^3.1.8", "joi": "^11.1.1", "lodash": "^4.17.4", - "puppeteer": "^1.19.0" + "puppeteer": "^2.1.1" }, "devDependencies": { "gulp": "^3.9.1", diff --git a/src/app/bootstrap.class.js b/src/app/bootstrap.class.js index 4880906..f5a5f3c 100755 --- a/src/app/bootstrap.class.js +++ b/src/app/bootstrap.class.js @@ -1,26 +1,26 @@ -import Net from 'net'; -import Path from 'path'; - -import Koa from 'koa'; -import Cors from 'koa-cors'; -import KoaLogger from 'koa-logger'; -import KoaConvert from 'koa-convert'; -import KoaFavicon from 'koa-favicon'; -import KoaBetterBody from 'koa-better-body'; - -import DefaultConfig from './feature/config/clazz/default.config.class'; - -import Logger from './frames/core/logger.core.class'; -import Service from './frames/core/service.core.class'; -import Controller from './frames/core/controller.core.class'; -import Router from './frames/core/router.core.class'; - -import AbstractService from './frames/base/service.base.class'; -import AbstractController from './frames/base/controller.base.class'; -import AbstractRouter from './frames/base/router.base.class'; -import Unify from "./shared/utils/unify.utils.class"; -import Result from "./shared/term/result.term.class"; - +import Net from 'net'; +import Path from 'path'; + +import Koa from 'koa'; +import Cors from 'koa-cors'; +import KoaLogger from 'koa-logger'; +import KoaConvert from 'koa-convert'; +import KoaFavicon from 'koa-favicon'; +import KoaBetterBody from 'koa-better-body'; + +import DefaultConfig from './feature/config/clazz/default.config.class'; + +import Logger from './frames/core/logger.core.class'; +import Service from './frames/core/service.core.class'; +import Controller from './frames/core/controller.core.class'; +import Router from './frames/core/router.core.class'; + +import AbstractService from './frames/base/service.base.class'; +import AbstractController from './frames/base/controller.base.class'; +import AbstractRouter from './frames/base/router.base.class'; +import Unify from "./shared/utils/unify.utils.class"; +import Render from "./shared/utils/render.utils.class"; +import Result from "./shared/term/result.term.class"; export default class Bootstrap { @@ -41,6 +41,7 @@ export default class Bootstrap { this._xRouter(); this._xResource(); this._xServer(); + this._xBrowser(); logger.trace('Server Start Total Cost : ' + (new Date() - this.startTime) + 'ms'); logger.trace('Current Service Version : ' + config.Version); } @@ -84,7 +85,6 @@ export default class Bootstrap { } _xBetterBody() { - // this.koa.use(new KoaBodyParser()); this.koa.use(KoaConvert(KoaBetterBody({ multipart: true, formLimit: '500kb' @@ -151,4 +151,14 @@ export default class Bootstrap { logger.trace('Finish Load Handle'); } + async _xBrowser() { + logger.trace('Chromium start is : ' + config.size); + let render = []; + for (let i = 0; i < config.size; i++) { + render[i] = await Render.init(); + } + global.browser = render; + logger.trace('Chromium start is : ' + config.size); + } + } \ No newline at end of file diff --git a/src/app/feature/config/clazz/default.config.class.js b/src/app/feature/config/clazz/default.config.class.js index f21a00d..8846bbd 100644 --- a/src/app/feature/config/clazz/default.config.class.js +++ b/src/app/feature/config/clazz/default.config.class.js @@ -9,8 +9,8 @@ export default class DefaultConfig { this.RootPath = rootPath + '/app'; this.setPathConfig(); this.setEnvConfig(); - this.setDialectConfig(); this.setServerConfig(); + this.setBrowserConfig(); } setPathConfig() { @@ -34,12 +34,13 @@ export default class DefaultConfig { } } - setDialectConfig() { - this.dialect = this.objConfig.dialect; - } - setServerConfig() { this.Port = this.objConfig.port; } + setBrowserConfig() { + this.size = this.objConfig.size; + this.ignore = this.objConfig.ignore; + } + } \ No newline at end of file diff --git a/src/app/shared/utils/render.utils.class.js b/src/app/shared/utils/render.utils.class.js index c67332f..37ebb20 100644 --- a/src/app/shared/utils/render.utils.class.js +++ b/src/app/shared/utils/render.utils.class.js @@ -4,6 +4,27 @@ import _C from '../../shared/term/criteria.term.class'; export default class Render { + static async init() { + const browser = await puppeteer.launch({ + headless: config.debug, + ignoreHTTPSErrors: config.ignore, + devtools: false, + args: [ + '--disable-gpu', + '--disable-dev-shm-usage', + '--disable-setuid-sandbox', + '--no-first-run', + '--no-sandbox', + '--no-zygote', + '--single-process', + "--proxy-server='direct://'", + '--proxy-bypass-list=*', + ], + sloMo: config.debug ? 250 : undefined + }); + return browser.wsEndpoint(); + } + /** * 根据参数信息,获取内容,生成文件 * @@ -21,7 +42,7 @@ export default class Render { height: 1200 }, goto: { - waitUntil: 'networkidle2' + waitUntil: 'networkidle0' }, output: _C.FOTMAT_TYPE_PDF, pdf: { @@ -32,7 +53,8 @@ export default class Render { type: 'png', fullPage: true }, - failEarly: false + failEarly: false, + waitFor: 6000 }, _opts); if (_.get(_opts, 'pdf.width') && _.get(_opts, 'pdf.height')) { @@ -40,25 +62,21 @@ export default class Render { opts.pdf.format = undefined; } - logger.trace('RENDER => ' + JSON.stringify({ - opts - })); + this.logOpts(opts); - const browser = await puppeteer.launch({ - headless: config.debug, - ignoreHTTPSErrors: opts.ignoreHttpsErrors, - args: ['--disable-gpu', '--no-sandbox', '--disable-setuid-sandbox'], - sloMo: config.debug ? 250 : undefined - }); - const page = await browser.newPage(); + let browserWSEndpoint = browser[Math.floor(Math.random() * config.size)]; + const chrome = await puppeteer.connect({browserWSEndpoint,ping_interval:'None',ping_timeout:'None'}); + logger.trace('RENDER => browserWSEndpoint connect ...'+ Math.floor(Math.random() * config.size)); + const page = await chrome.newPage(); + + await page.setCacheEnabled(true); page.on('error', (err) => { logger.error(`RENDER => Error event emitted: ${err}`); logger.error(err.stack); - browser.close(); + page.close(); }); - this.failedResponses = []; page.on('requestfailed', (request) => { this.failedResponses.push(request); @@ -95,7 +113,7 @@ export default class Render { await client.send('Network.setCookies', {cookies: opts.cookies}); } - if (opts.html) { + if (_.isString(opts.html)) { logger.trace('RENDER => Set HTML ..'); await page.setContent(opts.html, opts.goto); } else { @@ -107,7 +125,7 @@ export default class Render { if (_.isNumber(opts.waitFor) || _.isString(opts.waitFor)) { logger.trace(`RENDER => Wait for ${opts.waitFor} ..`); - await page.waitFor(parseInt(opts.waitFor)); + await page.waitFor(~~opts.waitFor); } if (opts.scrollPage) { @@ -140,6 +158,8 @@ export default class Render { if (opts.output === _C.FOTMAT_TYPE_PDF) { data = await page.pdf(opts.pdf); + }else if (opts.output === _C.FOTMAT_TYPE_HTML) { + data = await page.evaluate(() => document.body.innerHTML); } else { const screenshotOpts = _.cloneDeep(_.omit(opts.screenshot, ['clip'])); const clipContainsSomething = _.some(opts.screenshot.clip, val => !_.isUndefined(val)); @@ -148,23 +168,17 @@ export default class Render { } data = await page.screenshot(screenshotOpts); } - data.title = opts.attachmentName; } catch (err) { logger.trace(`RENDER <= Error when rendering page: ${err}`); logger.error(err.stack); throw err; } finally { - logger.trace('RENDER <= Closing browser..'); - await browser.close(); + logger.trace('RENDER <= Closing page..'); + await page.close(); } return data; } - /** - * 滚动到页面末尾以触发延迟加载所有元素 - * - * @param {page} Page. - */ static async scrollPage(page) { await page.evaluate(() => { const scrollInterval = 100; @@ -203,6 +217,8 @@ export default class Render { static getMimeType(opts) { if (opts.output === _C.FOTMAT_TYPE_PDF) { return 'application/pdf'; + }else if (opts.output === _C.FOTMAT_TYPE_HTML) { + return 'text/html'; } const type = _.get(opts, 'screenshot.type'); switch (type) { @@ -215,6 +231,16 @@ export default class Render { } } + static logOpts(opts) { + const supressedOpts = _.cloneDeep(opts); + if (opts.html) { + supressedOpts.html = '...'; + } + logger.trace('RENDER => ' + JSON.stringify({ + opts + })); + } + /** * 获取提交参数信息 * @@ -228,7 +254,7 @@ export default class Render { emulateScreenMedia: query.emulateScreenMedia, ignoreHttpsErrors: query.ignoreHttpsErrors, waitFor: ~~query.waitFor, - output: query.output || _C.FOTMAT_TYPE_PDF, + output: query.output || _C.FOTMAT_TYPE_PDF || _C.FOTMAT_TYPE_HTML, viewport: { width: query['viewport.width'], height: query['viewport.height'],