请教个Nodejs管理批量请求的问题
请教个Nodejs管理批量请求的问题
我使用nodegrass写了个根据页面获取图片的爬虫,为什么量大的时候会报错呢?代码大致如下:
code: for (var i = 0; i < urlArray.length; i++) {
// 根据单个页面请求 ng.get(urlArray[i], function(data, status, headers) {
// 根据页面请求结果获取页面对应的图片元素 var imageArray = parse(data)… // htmlparse处理,返回图片url列表 for (var j = 0; j < imageArray.length; j++) {
// 根据图片url列表请求 并且保存至本地 ngi.getFile(imageArray[j], ‘/tmp/image’, function(err) {
}) } }) }
这样看的话逻辑有没有问题呢?
错误内容: events.js:72 throw er; // Unhandled ‘error’ event ^ Error: socket hang up at createHangUpError (http.js:1472:15) at Socket.socketOnEnd [as onend] (http.js:1568:23) at Socket.g (events.js:180:16) at Socket.EventEmitter.emit (events.js:117:20) at _stream_readable.js:920:16 at process._tickCallback (node.js:415:13)
在处理大规模请求时,Node.js 的事件循环可能会因为大量并发请求而变得不堪重负,导致出现诸如 “socket hang up” 的错误。这是因为默认情况下 Node.js 的 HTTP 客户端无法处理大量的并发请求。为了解决这个问题,我们可以采用批量处理或者限流的方式来管理这些请求。
改进方案
我们可以使用 async
库来帮助我们更好地管理异步操作,并通过限制并发数量来避免服务器过载。
示例代码:
const async = require('async');
const nodegrass = require('nodegrass');
// 假设 urlArray 是包含所有页面 URL 的数组
const urlArray = ['http://example.com/page1', 'http://example.com/page2'];
// 定义一个函数来处理每个页面的请求
function processPage(pageUrl, callback) {
nodegrass.get(pageUrl, (data, status, headers) => {
if (status !== 200) {
return callback(new Error(`Failed to fetch page ${pageUrl}`));
}
const imageArray = parse(data); // 假设 parse 函数解析 HTML 并返回图片 URL 列表
// 使用 async.eachLimit 来限制并发请求的数量
async.eachLimit(imageArray, 5, (imageUrl, cb) => {
nodegrass.getFile(imageUrl, '/tmp/image', (err) => {
if (err) {
return cb(err);
}
console.log(`Saved image from ${imageUrl}`);
cb();
});
}, (err) => {
if (err) {
console.error(`Error processing images for page ${pageUrl}:`, err);
} else {
console.log(`Finished processing page ${pageUrl}`);
}
callback();
});
});
}
// 使用 async.eachLimit 来限制并发页面请求的数量
async.eachLimit(urlArray, 3, processPage, (err) => {
if (err) {
console.error('Error processing all pages:', err);
} else {
console.log('All pages processed successfully');
}
});
解释
-
限制并发请求:我们使用
async.eachLimit
来限制同时处理的页面数量(这里是3),以及每个页面中图片请求的数量(这里是5)。这可以有效防止服务器过载。 -
错误处理:在每个异步操作中添加了错误处理,确保即使某个请求失败也不会影响其他请求。
-
日志记录:在成功保存图片或完成页面处理时输出日志,便于调试。
通过这种方式,我们可以在不增加服务器负载的情况下,有效地管理大规模的请求。
从你的描述来看,问题可能出在你尝试并发地处理大量的HTTP请求。当同时发起大量请求时,可能会导致服务器响应不过来,甚至可能导致Node.js事件循环堵塞或网络资源耗尽,从而引发诸如socket hang up
这样的错误。
为了更好地管理这些请求,可以使用一些库来帮助你限制并行请求的数量,比如p-limit
或者async
模块中的queue
功能。以下是一个使用p-limit
的例子:
const pLimit = require('p-limit');
const ng = require('nodegrass');
const urlArray = [...]; // 你的URL数组
const maxParallelRequests = 5; // 最大并发请求数
const limit = pLimit(maxParallelRequests);
for (let i = 0; i < urlArray.length; i++) {
const url = urlArray[i];
ng.get(url, async function(data, status, headers) {
try {
const imageArray = parse(data); // 解析得到图片URL列表
for (const imageUrl of imageArray) {
await limit(async () => {
await ngi.getFile(imageUrl, '/tmp/image', function(err) {
if (err) console.error(err);
});
});
}
} catch (e) {
console.error(e);
}
});
}
这段代码通过p-limit
来限制并发请求数量,确保不会一次性发起太多请求,从而减轻服务器和网络的压力,避免出现socket hang up
等错误。
如果你不想使用额外的库,也可以考虑使用Node.js内置的async
库中的queue
功能,同样可以实现限制并发请求数量的效果。