我有一个HTTP Get请求,我想解析响应并将其保存到我的数据库中。
如果我独立调用crawl(i),我会得到很好的结果。但我必须从1到2000调用crawl()。 我得到了很好的结果,但有些回复似乎丢失了,有些回复是重复的。我不认为我理解如何调用数千个异步函数。我正在使用async module队列函数,但到目前为止,我仍然缺少一些数据,但仍然有一些重复。我在这做错了什么?谢谢你的帮助。
我的节点功能:
function getOptions(i) {
return {
host: 'magicseaweed.com',
path: '/syndicate/rss/index.php?id='+i+'&unit=uk',
method: 'GET'
}
};
function crawl(i){
var req = http.request(getOptions(i), function(res) {
res.on('data', function (body) {
parseLocation(body);
});
});
req.end();
}
function parseLocation(body){
parser.parseString(body, function(err, result) {
if(result && typeof result.rss != 'undefined') {
var locationTitle = result.rss.channel[0].title;
var locationString = result.rss.channel[0].item[0].link[0];
var location = new Location({
id: locationString.split('/')[2],
name: locationTitle
});
location.save();
}
});
}
N = 2 //# of simultaneous tasks
var q = async.queue(function (task, callback) {
crawl(task.url);
callback();
}, N);
q.drain = function() {
console.log('Crawling done.');
}
for(var i = 0; i < 100; i++){
q.push({url: 'http://magicseaweed.com/syndicate/rss/index.php?id='+i+'&unit=uk'});
}
[编辑]很好,经过大量的测试后,似乎我抓取的服务无法处理如此多的快速请求。因为当我按顺序执行每个请求时,我可以获得所有好的响应。
有没有办法慢慢降低ASYNC队列方法?
答案 0 :(得分:14)
你应该看看这个伟大的模块async,它简化了这样的异步任务。您可以使用队列,简单示例:
N = # of simultaneous tasks
var q = async.queue(function (task, callback) {
somehttprequestfunction(task.url, function(){
callback();
}
}, N);
q.drain = function() {
console.log('all items have been processed');
}
for(var i = 0; i < 2000; i++){
q.push({url:"http://somewebsite.com/"+i+"/feed/"});
}
如果您只调用回调函数,它将有一个正在进行的操作窗口,任务室将可用于将来的任务。不同的是,您的代码现在可以立即打开2000个连接,显然失败率很高。将其限制在合理的值,5,10,20(取决于站点和连接)将导致更好的成功率。如果请求失败,您可以再次尝试,或将任务推送到另一个异步队列以进行另一个试验。关键是在队列函数中调用callback(),以便在完成后可以使用房间。
答案 1 :(得分:9)
var q = async.queue(function (task, callback) {
crawl(task.url);
callback();
}, N);
你在开始上一个任务后立即执行下一个任务,这样,队列就没有意义了。你应该修改你的代码:
// first, modify your 'crawl' function to take a callback argument, and call this callback after the job is done.
// then
var q = async.queue(function (task, next/* name this argument as 'next' is more meaningful */) {
crawl(task.url, function () {
// after this one is done, start next one.
next();
});
// or, more simple way, crawl(task.url, next);
}, N);
答案 2 :(得分:2)
如果你想要另一种选择。香草JS没有花哨的图书馆。
var incrementer = 0;
var resultsArray = [];
var myInterval = setInterval(function() {
incrementer++
if(incrementer == 100){
clearInterval(myInterval)
//when done parse results array
}
//make request here
//push request result to array here
}, 500);
每半秒调用一次该函数。在x请求后强制同步和退出的简便方法。
答案 3 :(得分:0)
我知道我对这个问题有点迟了,但是这是我写的一个解决方案,用于在使用节点4或节点5测试api端点时减慢请求数量:
var fs = require('fs');
var supertest = require('supertest');
var request = supertest("http://sometesturl.com/api/test/v1/")
var Helper = require('./check.helper');
var basicAuth = Helper.basicAuth;
var options = Helper.options;
fs.readFile('test.txt', function(err, data){
var parsedItems = JSON.parse(data);
var urlparts = []
// create a queue
for (let year of range(1975, 2016)) {
for (var make in parsedItems[year]){
console.log(year, make, '/models/' + year + '/' + make)
urlparts.push({urlpart:'/models/' + year + '/' + make, year: year, make: make})
}
}
// start dequeue
waitDequeue();
// This function calls itself after the makeRequest promise completes
function waitDequeue(){
var item = urlparts.pop()
if (item){
makeRequest(item)
.then(function(){
// wait this time before next dequeue
setTimeout(function() {
waitDequeue();
}, 3000);
})
} else {
write(parsedItems)
}
}
// make a request, mutate parsedItems then resolve
function makeRequest(item){
return new Promise((resolve, reject)=>{
request
.get(item.urlpart)
.set(options.auth[0], options.auth[1])
.set(options.type[0], options.type[1])
.end(function(err, res) {
if (err) return done1(err);
console.log(res.body)
res.body.forEach(function(model){
parsedItems[item.year][item.make][model] = {}
});
resolve()
})
})
}
// write the results back to the file
function write(parsedItems){
fs.writeFile('test.txt', JSON.stringify(parsedItems, null, 4), function(err){
console.log(err)
})
}
})
答案 4 :(得分:0)
有点晚了,但我发现这个有效! 使用异步,您可以通过在任务处理程序内部使用来减慢队列,例如:
public ActionResult Create( NewMovieViewModel movie)
{
if (ModelState.IsValid)
{
db.Movies.Add(movie.movie);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}