javascript - 如何减少 Node JS 中多个异步函数的运行时间?
问题描述
我想在 Node js 中抓取一些网页并从中获取一些数据。我的代码正在运行,但完成抓取并返回所有数据需要将近 1 分钟。我已经为每个网站使用了异步功能,并承诺收集所有信息。我已经处理了最多 10000 个链接。我认为运行时间太多了。我的代码结构(request-promise、promises、async、await 等的使用)中是否存在导致延迟的问题?所有功能都可以并行/异步运行,但我的限制是我需要等到所有结果都来自每个网站。我将每个请求的超时时间限制为 10 秒。如果我进一步减少它,现有的 ETIMEDOUT、ECONNRESET、ESOCKETTIMEDOUT 错误(我仍然无法摆脱)会增加。
这是我的抓取功能之一:
const rp = require('request-promise');
const cheerio = require('cheerio');
const fs = require("fs");
const Promise = require("bluebird");
async function ntv() {
var posts = [];
try {
const baseUrl = 'http://www.ntv.com';
const mainHtml = await rp({uri: baseUrl, timeout: 10000});
const $ = cheerio.load(mainHtml);
const links =
$(".swiper-slide")
.children("a")
.map((i, el) => {
return baseUrl + $(el).attr("href");
}).get();
posts = await Promise.map(links, async (link) => {
try {
const newsHtml = await rp({uri: link, timeout: 10000});
const $ = cheerio.load(newsHtml);
return {
title: $("meta[property='og:title']").attr("content"),
image: $("meta[property='og:image']").attr("content"),
summary: $("meta[property='og:description']").attr("content")
}
} catch (err) {
if (err.message == 'Error: ETIMEDOUT') console.log('TIMEOUT error ' + link);
else if (err.message == 'Error: read ECONNRESET') console.log('CONNECTION RESET error ' + link);
else if (err.message == 'Error: ESOCKETTIMEDOUT') console.log('SOCKET TIMEOUT error ' + link);
else console.log(err);
}
})
} catch (e) {
console.log(e)
}
return posts;
}
我运行所有这些抓取功能的主要功能是:
var Promise = require("bluebird")
var fs = require("fs")
async function getData() {
const sourceFunc = [func1(), func2(), ... , func10()];
var news = [];
await Promise.map(sourceFunc, async (getNews) => {
try {
const currentNews = await getNews;
news = news.concat(currentNews);
} catch (err) {
console.log(err);
}
},{concurrency:10});
news.sort(function(a,b){
return new Date(b.time) - new Date(a.time);
});
fs.writeFile('./news.json', JSON.stringify(news, null, 3), (err) => {
if (err) throw err;
});
return news;
}
解决方案
我将首先向您的脚本添加一些基准。找出哪个步骤在ntv()
功能上花费的时间最多并进行调整。
我的另一个猜测是用cheerio 解析整个html 是一个瓶颈。使用String.prototype.substring()
或RegExp()
提取链接和发布信息可能会更高效。
更新:
查看并发 TCP 连接是否不是瓶颈。以下是有关如何检查/调整它的一些提示。
如果并发是问题,那么将作业拆分为几个程序可能是有意义的。例如
- 过程 #1 生成要获取的 URL 列表
- 进程 #2 从列表中获取一个 URL,从中获取 HTML 并保存在本地
- 进程 #3 获取一个 HTML 并解析它
如果你像这样拆分工作,你可以更好地并行化它。例如,节点仅在一个内核上工作,通过并行化,您可以运行多个进程,例如进行提取,从而从多个内核中受益。并且还规避了对连接等的任何每个进程的限制。
如果将 URL 和 HTML 保存到共享数据库中,您可以在多台机器之间分配任务,从而进一步提高性能。
推荐阅读
- javascript - 注销时清除超时的问题
- mysql - 在 select 语句和子查询中使用时,表不存在错误
- mongodb - 使用 jenkins 管道在 docker mongo 实例中运行我的测试
- c - 为什么单链表程序没有按照预期的方式工作
- keras - python中3D矩阵的归一化(LSTM/Keras输入)
- python - 基于Python中的zip函数从列表中的字符串文字中提取数字
- python - Python Pandas : Return column header/name where values equal the other in the dataframe
- java - 如何验证param实际上是Json,而不仅仅是一个字符串
- vue.js - 重置一个 Vuetify 步进器
- pandas - 按以某个 str 开头的元素列表切片行 df