Limit Q promise concurrency in Node js
有什么方法可以限制在节点js中一次执行的并发Q承诺的数量吗?
我正在构建一个Web爬网程序,该爬网程序必须请求和解析更多3000多个页面,并且不限制我提出的某些请求,这些请求没有及时得到响应,因此连接中断了,所需的响应(html代码)变得不可用。
为了解决这个问题,我发现限制问题的请求数量已经消失了。
我尝试了以下方法,但无济于事:
- Q承诺中的并发限制-节点
- 如何限制Q许诺并发?
- https://gist.github.com/gaearon/7930162
- https://github.com/ForbesLindesay/throat
我需要请求一个url数组,一次仅在阵列中的所有url完成后执行一次请求,然后将结果返回到数组中。
1 2 3 4 5 6 7 8 9 10 11 12 13 | function processWebsite() { //computed by this stage urls = [u1,u2,u3,u4,l5,u6,u7,u8,u9]; var promises = throttle(urls,1,myfunction); // myfunction returns a Q promise and takes a considerable // amount of time to resolve (approximately 2-5 minutes) Q.all(promises).then(function(results){ //work with the results of the promises array }); } |
我会这样做,它将遍历每个URL,构建一个当前一个结束时运行的诺言链,并以一系列请求结果进行解析。
1 2 3 4 5 6 7 | return urls.reduce(function(acc, url){ return acc.then(function(results) return myfunction(url).then(function(requestResult){ return results.concat(requestResult) }); }); }, Q.resolve([])); |
您也可以将其变成一个助手:
1 2 3 4 5 6 7 8 9 10 11 | var results = map(urls, myfunction); function map(items, fn){ return items.reduce(function(acc, item){ return acc.then(function(results) return fn(item).then(function(result){ return results.concat(result) }); }); }, Q.resolve([]) } |
注意,
1 | return Bluebird.map(urls, myfunction, {concurrency: 1}); |
这是我为Q设置受限制的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | function qMap(items, worker, concurrent) { var result = Q.defer(); var work = []; var working = 0; var done = 0; concurrent = parseInt(concurrent, 10) || 1; function getNextIndex() { var i; for (i = 0; i < items.length; i++) { if (typeof work[i] ==="undefined") return i; } } function doneWorking() { working--; done++; result.notify( +((100 * done / items.length).toFixed(1)) ); if (!startWorking() && done === items.length) { result.resolve(work); } } function startWorking() { var index = getNextIndex(); if (typeof index !=="undefined" && working < concurrent) { working++; work[index] = worker(items[index]).finally(doneWorking); return true; } } while (startWorking()); return result.promise; } |
它接受
-
一个
items 数组(要使用的网址), -
worker (必须是接受项目并返回promise的函数) -
并且在任何给定时间最多可以处理
concurrent 个项目。
它返回
- 一个承诺和
- 当所有工人都完成后,解决一系列已兑现的承诺。
它不会失败,您必须检查单个的承诺以确定操作的总体状态。
在您的情况下,您可以这样使用它,例如15个并发请求:
1 2 3 4 5 6 7 8 9 10 | // myfunction returns a Q promise and takes a considerable // amount of time to resolve (approximately 2-5 minutes) qMap(urls, myfunction, 15) .progress(function (percentDone) { console.log("progress:" + percentDone); }) .done(function (urlPromises) { console.log("all done:" + urlPromises); }); |
可以使用递归来解决。
这个想法是,最初,您发送的请求数量将达到允许的最大值,并且这些请求中的每一个都应在完成时递归地继续发送自己。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | function processWebsite(urls, concurrentRequestsLimit) { return new Promise(resolve => { var pages = []; var index = 0; function recursiveFetch() { if (index === urls.length) { return; } fetch(urls[index++]).then(r => { pages.push(r.text()); if (pages.length === urls.length) { resolve(pages); } else { recursiveFetch(); } }); } for (var i = 0; i < concurrentRequestsLimit; i++) { recursiveFetch(); } }); } var urls = [ 'http://www.example.com/page_1', 'http://www.example.com/page_2', 'http://www.example.com/page_3', ... 'http://www.example.com/page_3000' ]; processWebsite(urls, 5).then(pages => { //process all 3000 pages here }); |
您可以在
1 2 3 4 5 6 7 | myFunction(urls[0]).then(function(result) { myFunction(urls[1]).then(function(result) { myFunction(urls[2]).then(function(result) { ... }); }); }); |
当然,这将是它的动态行为。解决诺言后,我将保留一个队列并从单个网址出队。然后提出另一个要求。也许有一个将URL与结果相关的哈希对象。
第二点:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | var urls = ...; var limit = ...; var dequeue = function() { return an array containing up to limit }; var myFunction = function(dequeue) { var urls = dequeue(); $q.all(process urls); }; myFunction(dequeue).then(function(result) { myFunction(dequeue).then(function(result) { myFunction(dequeue).then(function(result) { ... }); }); }); |