摘要:所以需要理解運(yùn)行的原理,才能方便優(yōu)化。如果啟動(dòng)時(shí)能綁定到某個(gè)核上也能提升速度單核上進(jìn)行進(jìn)程切換耗費(fèi)的時(shí)間更少。優(yōu)化執(zhí)行流程接下來(lái)我們?cè)俣鄮?yōu)化對(duì)應(yīng)的頁(yè)面。參考文章性能優(yōu)化與執(zhí)行速度提升利用優(yōu)化
Puppeteer自身不會(huì)消耗太多資源,耗費(fèi)資源的大戶是Chromium Headless。所以需要理解Chromium運(yùn)行的原理,才能方便優(yōu)化。
Chromium消耗最多的資源是CPU,一是渲染需要大量計(jì)算,二是Dom的解析與渲染在不同的進(jìn)程,進(jìn)程間切換會(huì)給CPU造成壓力(進(jìn)程多了之后特別明顯)。其次消耗最多的是內(nèi)存,Chromium是以多進(jìn)程的方式運(yùn)行,一個(gè)頁(yè)面會(huì)生成一個(gè)進(jìn)程,一個(gè)進(jìn)程占用30M左右的內(nèi)存,大致估算1000個(gè)請(qǐng)求占用30G內(nèi)存,在并發(fā)高的時(shí)候內(nèi)存瓶頸最先顯現(xiàn)。
優(yōu)化最終會(huì)落在內(nèi)存和CPU上(所有軟件的優(yōu)化最終都要落到這里),通常來(lái)說(shuō)因?yàn)椴l(fā)造成的瓶頸需要優(yōu)化內(nèi)存,計(jì)算速度慢的問(wèn)題要優(yōu)化CPU。使用Puppeteer的用戶多半會(huì)更關(guān)心計(jì)算速度,所以下面我們談?wù)勅绾蝺?yōu)化Puppeteer的計(jì)算速度。
優(yōu)化Chromium啟動(dòng)項(xiàng)通過(guò)查看Chromium啟動(dòng)時(shí)都有哪些參數(shù)可以配置,能找到大部分線索,因?yàn)镃hromium這種頂級(jí)的開(kāi)源產(chǎn)品,文檔與接口都是非常清晰的,肯定可以找到相關(guān)配置項(xiàng)來(lái)定制啟動(dòng)方式。Chromium 啟動(dòng)參數(shù)列表
我們需要找到下面幾種配置來(lái)提升速度:
如果將Dom解析和渲染放到同一進(jìn)程,肯定能提升時(shí)間(進(jìn)程上下文切換的時(shí)間)。對(duì)應(yīng)的配置是??single-process?
部分功能disable掉,比如GPU、Sandbox、插件等,減少內(nèi)存的使用和相關(guān)計(jì)算。
如果啟動(dòng)Chromium時(shí)能綁定到某個(gè)CPU核上也能提升速度(單核上進(jìn)行進(jìn)程切換耗費(fèi)的時(shí)間更少)。可惜沒(méi)有找到對(duì)應(yīng)的配置,官方文檔寫(xiě)的是Chromium啟動(dòng)時(shí)會(huì)自動(dòng)綁定CPU大核(ARM架構(gòu)的CPU通常有大小核之分),依此推測(cè)Chromium啟動(dòng)時(shí)是會(huì)綁核的。(此處我并未驗(yàn)證)
最后配置如下:
const browser = await puppeteer.launch(
{
headless:true,
args: [
‘–disable-gpu’,
‘–disable-dev-shm-usage’,
‘–disable-setuid-sandbox’,
‘–no-first-run’,
‘–no-sandbox’,
‘–no-zygote’,
‘–single-process’
]
});
Chromium 啟動(dòng)參數(shù)列表?文檔中的配置項(xiàng)都可以嘗試看看,我沒(méi)有對(duì)所有選項(xiàng)做測(cè)試,但可以肯定存在某些選項(xiàng)能提升Chromium速度。
優(yōu)化Chromium執(zhí)行流程接下來(lái)我們?cè)俣鄮?yōu)化Chromium對(duì)應(yīng)的頁(yè)面。我之前的文章中提過(guò),如果每次請(qǐng)求都啟動(dòng)Chromium,再打開(kāi)tab頁(yè),請(qǐng)求結(jié)束后再關(guān)閉tab頁(yè)與瀏覽器。流程大致如下:
請(qǐng)求到達(dá)->啟動(dòng)Chromium->打開(kāi)tab頁(yè)->運(yùn)行代碼->關(guān)閉tab頁(yè)->關(guān)閉Chromium->返回?cái)?shù)據(jù)
真正運(yùn)行代碼的只是tab頁(yè)面,理論上啟動(dòng)一個(gè)Chromium程序能運(yùn)行成千上萬(wàn)的tab頁(yè),可不可以復(fù)用Chromium每次只打開(kāi)一個(gè)tab頁(yè)然后關(guān)閉呢?當(dāng)然是可以的,Puppeteer提供了?puppeteer.connect()??方法,可以連接到當(dāng)前打開(kāi)的瀏覽器。流程如下:
請(qǐng)求到達(dá)->連接Chromium->打開(kāi)tab頁(yè)->運(yùn)行代碼->關(guān)閉tab頁(yè)->返回?cái)?shù)據(jù)
代碼如下:
const MAX_WSE = 4; //啟動(dòng)幾個(gè)瀏覽器
let WSE_LIST = []; //存儲(chǔ)browserWSEndpoint列表
init();
app.get("/", function (req, res) {
let tmp = Math.floor(Math.random()* MAX_WSE);
(async () => {
let browserWSEndpoint = WSE_LIST[tmp];
const browser = await puppeteer.connect({browserWSEndpoint});
const page = await browser.newPage();
await page.goto("file://code/screen/index.html");
await page.setViewport({
width: 600,
height: 400
});
await page.screenshot({path: "example.png"});
await page.close();
res.send("Hello World!");
})();
});
function init(){
(async () => {
for(var i=0;itrue,
args: [
"--disable-gpu",
"--disable-dev-shm-usage",
"--disable-setuid-sandbox",
"--no-first-run",
"--no-sandbox",
"--no-zygote",
"--single-process"
]});
browserWSEndpoint = await browser.wsEndpoint();
WSE_LIST[i] = browserWSEndpoint;
}
console.log(WSE_LIST);
})();
}
利用cluster優(yōu)化Puppeteer
通常情況下我們會(huì)使用??.map()??搭配??Promise.all()??的方式并行處理異步,但是在使用?Puppeteer?批量截圖時(shí)發(fā)現(xiàn)?Promise.all?會(huì)打開(kāi)多個(gè)瀏覽器,導(dǎo)致機(jī)器性能急劇下降。
?Promise.all()??并行處理
利用??Reduce??是多個(gè)?Promise?順序執(zhí)行
await tasks.reduce((sequence, url, idx) => {
return sequence.then(() => {
// doAnalyze 是個(gè)異步函數(shù)
return doAnalyze(url, idx);
});
}, Promise.resolve())
場(chǎng)景:有40個(gè)URL,需要獲取每個(gè)博客的首頁(yè)截圖
如果是?Promise.all()?,程序啟動(dòng)會(huì)同時(shí)打開(kāi)20+的chromium瀏覽器,導(dǎo)致機(jī)器卡死。
使用?reduce?緩解了壓力,但沒(méi)充分利用多核性能
參入?Cluster?
// cluster_index.js 入口文件
const cluster = require("cluster");
(async () => {
let run;
if (cluster.isMaster) {
run = require("./cluster_master");
} else {
run = require("./cluster_worker");
}
try {
await run();
} catch (e) {
// 追蹤函數(shù)的調(diào)用軌跡
console.trace(e);
}
})();
// cluster_master.js master進(jìn)程分配任務(wù)
const cluster = require("cluster");
const numCPUs = require("os").cpus().length;
// 處理的任務(wù)列表
let arr = [
"https://github.com/guoguoya",
"http://www.52cik.com",
"http://zhalice.com",
"https://www.yzqroom.cn",
"http://zxh.name",
"https://fogdong.github.io/",
"http://github.com/elsieyin",
"https://summer.tlb058.com",
"https://skymon4.cn",
"http://www.jiweiqing.cn",
"http://effect.im",
"http://dingkewz.com",
"http://xcdh.me",
"http://d2g.io",
"http://codingdemon.com",
"http://blog.leanote.com/dujuncheng",
"http://niexiaotao.com",
"http://zhengchengwen.com",
"http://blog.tophefei.com",
"https://zh-rocco.github.io",
"http://wangyn.net",
"http://dscdtc.ml",
"http://jweboy.github.io",
"http://www.wenghaoping.com",
"http://zhoujingchao.github.io",
"http://kyriejoshua.github.io/jo.github.io/",
"http://www.withyoufriends.com",
"http://if2er.com",
"https://github.com/zhou-yg",
"http://github/suoutsky",
"http://richardsleet.github.io",
"http://www.89io.com",
"https://guoshencheng.com",
"http://www.landluck.com.cn",
"http://www.89io.com",
"http://myoungxue.top",
"https://github.com/Wangszzju",
"http://www.hacke2.cn",
"https://github.com/enochjs",
"https://i.jakeyu.top",
"http://muyunyun.cn",
];
module.exports = async () => {
// 每個(gè) CPU 分配 N 個(gè)任務(wù)
const n = Math.floor(arr.length / numCPUs);
// 未分配的余數(shù)
const remainder = arr.length % numCPUs;
for (let i = 1; i <= numCPUs; i += 1) {
const tasks = arr.splice(0, n + (i > remainder ");"exit", (worker) => {
console.log(`worker #${worker.id} PID:${worker.process.pid} died`);
});
cluster.on("error", (err) => {
console.log(`worker #${worker.id} PID ERROR: `, err);
});
};
// cluster_worker.js worker進(jìn)程 完成任務(wù)
const cluster = require("cluster");
const puppeteer = require("puppeteer");
// 禁止直接啟動(dòng)
if (cluster.isMaster) {
console.log("----", cluster.worker.id)
process.exit(0);
}
module.exports = async () => {
const env = process.env.tasks;
let tasks = [];
if (/^[.*]$/.test(env)) {
tasks = JSON.parse(env);
}
if (tasks.length === 0) {
console.log("");, tasks)
// 非法啟動(dòng), 釋放進(jìn)程資源
process.exit(0);
}
console.log(`worker #${cluster.worker.id} PID:${process.pid} Start`);
await tasks.reduce((sequence, url, idx) => {
return sequence.then(() => {
return doAnalyze(url, idx);
});
}, Promise.resolve())
console.log(cluster.worker.id + " 順利完成");
process.exit(0);
};
async function doAnalyze(url, i) {
try {
const browser = await (puppeteer.launch({
// 若是手動(dòng)下載的chromium需要指定chromium地址, 默認(rèn)引用地址為 /項(xiàng)目目錄/node_modules/puppeteer/.local-chromium/
// executablePath: "/Users/huqiyang/Documents/project/z/chromium/Chromium.app/Contents/MacOS/Chromium",
//設(shè)置超時(shí)時(shí)間
timeout: 30000,
//如果是訪問(wèn)https頁(yè)面 此屬性會(huì)忽略https錯(cuò)誤
ignoreHTTPSErrors: true,
// 打開(kāi)開(kāi)發(fā)者工具, 當(dāng)此值為true時(shí), headless總為false
devtools: false,
// 關(guān)閉headless模式, 會(huì)打開(kāi)瀏覽器
headless: false
}));
const page = await browser.newPage();
await page.setViewport({width: 1920, height: 1080});
await page.goto(url);
await page.waitFor(4000);
console.log(cluster.worker.id, url, i, "截圖中...");
await page.screenshot({
path: `./img_cluster/${cluster.worker.id}-${i}.png`,
// path: "3.png",
type: "png",
// quality: 100, 只對(duì)jpg有效
// fullPage: true,
// 指定區(qū)域截圖,clip和fullPage兩者只能設(shè)置一個(gè)
// clip: {
// x: 0,
// y: 0,
// width: 1920,
// height: 600
// }
});
browser.close();
} catch (error) {
console.log(cluster.worker.id, url, i)
console.log(error)
}
};
多個(gè)page輪詢與多個(gè)browser輪詢
為了性能,現(xiàn)有解決方案是初始化若干個(gè)browser,請(qǐng)求打過(guò)來(lái)時(shí),直接在browserList中取一個(gè)browser實(shí)例使用。 作為對(duì)比,可以參考初始化一個(gè)browser,預(yù)先打開(kāi)若干個(gè)page,請(qǐng)求打過(guò)來(lái)時(shí),直接在pageList中取一個(gè)page實(shí)例使用。
參考文章:Puppeteer性能優(yōu)化與執(zhí)行速度提升 利用cluster優(yōu)化Puppeteer
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/7302.html
摘要:前端每周清單第期現(xiàn)狀分析與優(yōu)化策略單元測(cè)試爬蟲(chóng)作者王下邀月熊編輯徐川前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開(kāi)發(fā)者了解一周前端熱點(diǎn)分為新聞熱點(diǎn)開(kāi)發(fā)教程工程實(shí)踐深度閱讀開(kāi)源項(xiàng)目巔峰人生等欄目。 showImg(https://segmentfault.com/img/remote/1460000011008022); 前端每周清單第 29 期:Web 現(xiàn)狀分析與優(yōu)化策略...
摘要:導(dǎo)言對(duì)于大多數(shù)前端開(kāi)發(fā)者而言,談到命令行工具,大家肯定都用過(guò)。但是談到開(kāi)發(fā)命令行工具,估計(jì)就沒(méi)幾人有了解了。如何優(yōu)化這個(gè)圖片爬蟲(chóng)工具目前還有點(diǎn)啊,我們的目標(biāo)是要開(kāi)發(fā)一個(gè)交互式的命令行應(yīng)用,肯定不能止于此。 導(dǎo)言:對(duì)于大多數(shù)前端開(kāi)發(fā)者而言,談到命令行工具,大家肯定都用過(guò)。但是談到開(kāi)發(fā)命令行工具,估計(jì)就沒(méi)幾人有了解了。本文旨在用最短的時(shí)間內(nèi),幫您開(kāi)發(fā)一個(gè)實(shí)用(斜眼笑)的圖片爬蟲(chóng)命令行應(yīng)用。...
摘要:發(fā)布是由團(tuán)隊(duì)開(kāi)源的,操作接口庫(kù),已成為事實(shí)上的瀏覽器操作標(biāo)準(zhǔn)。本周正式發(fā)布,為我們帶來(lái)了,,支持自定義頭部與腳部,支持增強(qiáng),兼容原生協(xié)議等特性變化。新特性介紹日前發(fā)布了大版本更新,引入了一系列的新特性與提升,本文即是對(duì)這些變化進(jìn)行深入解讀。 showImg(https://segmentfault.com/img/remote/1460000012940044); 前端每周清單專注前端...
摘要:前端每周清單年度總結(jié)與盤(pán)點(diǎn)在過(guò)去的八個(gè)月中,我?guī)缀踔蛔隽藘杉拢ぷ髋c整理前端每周清單。本文末尾我會(huì)附上清單線索來(lái)源與目前共期清單的地址,感謝每一位閱讀鼓勵(lì)過(guò)的朋友,希望你們能夠繼續(xù)支持未來(lái)的每周清單。 showImg(https://segmentfault.com/img/remote/1460000010890043); 前端每周清單年度總結(jié)與盤(pán)點(diǎn) 在過(guò)去的八個(gè)月中,我?guī)缀踔蛔隽?..
摘要:由于文件中版本號(hào)的特點(diǎn),下面三個(gè)版本號(hào)在安裝的時(shí)候代表不同的含義。安裝版本統(tǒng)一為了防止拉取到不同的版本,有一個(gè)鎖定文件記錄了被確切安裝上的模塊的版本號(hào)。 showImg(https://segmentfault.com/img/bVbs8Rg?w=1920&h=1080); 一位用不好包管理器的前端,是一個(gè)入門(mén)級(jí)前端,一個(gè)用不好webpack的前端,是一個(gè)初級(jí)前端 三個(gè)包管理器是可以一...
閱讀 2236·2021-11-24 11:15
閱讀 3080·2021-11-24 10:46
閱讀 1378·2021-11-24 09:39
閱讀 3924·2021-08-18 10:21
閱讀 1478·2019-08-30 15:53
閱讀 1395·2019-08-30 11:19
閱讀 3321·2019-08-29 18:42
閱讀 2321·2019-08-29 16:58