请教个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)


2 回复

在处理大规模请求时,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');
    }
});

解释

  1. 限制并发请求:我们使用 async.eachLimit 来限制同时处理的页面数量(这里是3),以及每个页面中图片请求的数量(这里是5)。这可以有效防止服务器过载。

  2. 错误处理:在每个异步操作中添加了错误处理,确保即使某个请求失败也不会影响其他请求。

  3. 日志记录:在成功保存图片或完成页面处理时输出日志,便于调试。

通过这种方式,我们可以在不增加服务器负载的情况下,有效地管理大规模的请求。


从你的描述来看,问题可能出在你尝试并发地处理大量的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功能,同样可以实现限制并发请求数量的效果。

回到顶部