Asynchrone en parallèle des demandes sont en cours d'exécution de façon séquentielle
Je suis en cours d'exécution d'un serveur à l'aide de Node.js et la nécessité de demander des données à partir d'un autre serveur que je suis en cours d'exécution (localhost:3001
). J'ai besoin de faire de nombreuses demandes (~200) pour le serveur de données et de collecte de données (réponse varient de ~20Kb à ~20 mo). Chaque demande est indépendante, et je voudrais enregistrer les réponses comme un tableau géant de la forme:
[{"urlAAA": responseAAA}, {"urlCCC": responseCCC}, {"urlBBB": responseBBB}, etc ]
Notez que l'ordre des éléments sans importance, ils devraient idéalement compléter le tableau dans l'ordre que les données deviennent disponibles.
var express = require('express');
var router = express.Router();
var async = require("async");
var papa = require("papaparse");
var sync_request = require('sync-request');
var request = require("request");
var pinnacle_data = {};
var lookup_list = [];
for (var i = 0; i < 20; i++) {
lookup_list.push(i);
}
function write_delayed_files(object, key, value) {
object[key] = value;
return;
}
var show_file = function (file_number) {
var file_index = Math.round(Math.random() * 495) + 1;
var pinnacle_file_index = 'http://localhost:3001/generate?file=' + file_index.toString();
var response_json = sync_request('GET', pinnacle_file_index);
var pinnacle_json = JSON.parse(response_json.getBody('utf8'));
var object_key = "file_" + file_number.toString();
pinnacle_data[object_key] = pinnacle_json;
console.log("We've handled file: " + file_number);
return;
};
async.each(lookup_list, show_file, function (err) {});
console.log(pinnacle_data);
/* GET contact us page. */
router.get('/', function (req, res, next) {
res.render('predictionsWtaLinks', {title: 'Async Trial'});
});
module.exports = router;
Maintenant, lorsque ce programme est exécuté, il affiche:
We've handled file: 0
We've handled file: 1
We've handled file: 2
We've handled file: 3
We've handled file: 4
We've handled file: 5
etc
Maintenant que les fichiers sont de taille variable, je m'attendais à ce que ce serait d'effectuer les requêtes "en parallèle", mais il semble fonctionner de manière séquentielle, ce qui est ce que j'essayais de l'éviter grâce à l'aide de async.each()
. Actuellement, il faut environ 1-2s pour se connecter au serveur de données et donc d'effectuer cette sur de nombreux fichiers est trop longue.
Je me rends compte que je suis en utilisant synchrone de la demande, et donc aimerais idéalement remplacer:
var response_json = sync_request('GET', pinnacle_file_index);
avec quelque chose de similaire à
request(pinnacle_file_index, function (error, response, body) {
if (!error && response.statusCode == 200) {
pinnacle_data[object_key] = JSON.parse(body);
}
});
Toute aide serait grandement appréciée.
En outre, j'ai regardé à essayer:
- La conversion de la liste des url dans une liste de fonctions anonymes et à l'aide de
async.parallel(function_list, function (err, results) { //add results to pinnacle_data[]});
. (J'ai rencontré des problèmes en essayant de définir des fonctions uniques pour chaque élément du tableau).
De même, j'ai regardé les autres sujets connexes:
-
J'ai essayé d'imiter les solutions proposées à partir de Http asynchrone appels avec nodeJS avec aucun progrès.
-
Node.js - Async.js: comment fonctionne l'exécution en parallèle de travail?.
- Comment faire en parallèle asynchrone plusieurs demandes à la fois avec des Promesses de Nœud
EDIT - SOLUTION DE TRAVAIL
Le code suivant n'est désormais la tâche (en prenant ~80ms par la demande, y compris pour faire des demandes répétées à l'aide de npm requestretry
). De même, ce écailles très bien, en prenant une moyenne de temps de demande de ~80ms pour faire entre 5 demande au total, jusqu'à 1000.
var performance = require("performance-now");
var time_start = performance();
var async = require("async");
var request_retry = require('requestretry');
var lookup_list = [];
var total_requests = 50;
for (var i = 0; i < total_requests; i++) {
lookup_list.push(i);
}
var pinnacle_data = {};
async.map(lookup_list, function (item, callback) {
var file_index = Math.round(Math.random() * 495) + 1;
var pinnacle_file_index = 'http://localhost:3001/generate?file=' + file_index;
request_retry({
url: pinnacle_file_index,
maxAttempts: 20,
retryDelay: 20,
retryStrategy: request_retry.RetryStrategies.HTTPOrNetworkError
},
function (error, response, body) {
if (!error && response.statusCode == 200) {
body = JSON.parse(body);
var data_array = {};
data_array[file_index.toString()] = body;
callback(null, data_array);
} else {
console.log(error);
callback(error || response.statusCode);
}
});
},
function (err, results) {
var time_finish = performance();
console.log("It took " + (time_finish - time_start).toFixed(3) + "ms to complete " + total_requests + " requests.");
console.log("This gives an average rate of " + ((time_finish - time_start) / total_requests).toFixed(3) + " ms/request");
if (!err) {
for (var i = 0; i < results.length; i++) {
for (key in results[i]) {
pinnacle_data[key] = results[i][key];
}
}
var length_array = Object.keys(pinnacle_data).length.toString();
console.log("We've got all the data, totalling " + length_array + " unique entries.");
} else {
console.log("We had an error somewhere.");
}
});
Merci pour l'aide.
OriginalL'auteur oliversm | 2015-09-07
Vous devez vous connecter pour publier un commentaire.
Que vous avez découvert,
async.parallel()
ne peut paralléliser les opérations qui sont eux-mêmes asynchrone. Si les opérations sont synchrones, ensuite à cause de la mono-thread nature de node.js, les opérations s'exécutent l'une après l'autre, non pas en parallèle. Mais, si les opérations sont eux-mêmes asynchrone, puisasync.parallel()
(ou d'autres méthodes asynchrones) commencera tous à la fois et de coordonner les résultats pour vous.Voici une idée à l'aide de
async.map()
. J'ai utiliséasync.map()
parce que l'idée est qu'il prend un tableau en entrée et produit un tableau de résultats dans le même ordre que l'original, mais fonctionne à toutes les demandes en parallèle, ce qui semble à la ligne avec ce que vous voulez:Et, voici une version à l'aide de Bluebird promesses et un peu de la même manière à l'aide de
Promise.map()
d'itérer la table initiale:requestretry
au lieu derequest
comme mon autre serveur gardé en panne ou d'avoir un trop grand nombre de fichiers ouverts avec un tel bombardement de demandes, bien que la plupart de ces erreurs ont disparu après un léger retard et une autre tentative.Merci @jfriend00.si vous le faites parallèlement de nombreuses demandes qui vous sont accablants votre serveur, vous pouvez utiliser
async.mapLimit()
où vous pouvez spécifier combien de demandes sont autorisés à être en vol en même temps. Ceci est utile pour les demandes en cours d'exécution en parallèle, mais la protection contre les inondations le serveur et.mapLimit()
fera tout le travail pour vous. Vous venez de passer un argument supplémentaire pour combien de demandes simultanées pour permettre. Ce serait beaucoup mieux que d'inonder le serveur, à l'origine des erreurs, puis de réessayer.Merci pour les conseils, je suis actuellement hébergeant le serveur de données moi-même et il n'est pas très "heavy duty", de sorte que cette restriction avait réduit le nombre d'erreurs du serveur de données génère. Cependant, quand j'arrive à la pleine version de production, je vais être idéalement de bombarder quelqu'un d'autre serveur, qui devrait être beaucoup plus "heavy duty", et nous espérons être en mesure de faire face avec plus de 200 demandes simultanées facilement.
Vous devriez vraiment pas d'agression d'un autre serveur avec 200 demandes simultanées. De nombreux serveurs conçus pour les grandes échelle activement d'éviter qu'un seul point de terminaison de le faire par la limitation du débit pour protéger leur qualité de service pour les autres. Car il n'y a aucun moyen de vraiment tout seul serveur peut réellement traiter plus de 200 demandes à littéralement le même temps, il y a peu de point dans votre bombarder un serveur de cette façon que la plupart des demandes juste d'être mis en file d'attente tout de même ou vous pouvez déclencher la limitation du débit. Je vous suggère de vous ramener à 10 à 20 à l'heure.
Merci pour l'info, va le faire.
OriginalL'auteur jfriend00
Semble que vous êtes juste en train de télécharger un tas d'Url en parallèle. Ce qui fera:
ou encore plus simple, à l'aide de
async.map
:Pas de besoin. Vous venez de faire un plan de votre nouvelle Url à partir d'un nouveau tableau. L'asynchrone.carte juste crée un tableau de fonctions de s'exécuter de manière asynchrone. Vous répétez le processus pour ce que les Url que vous souhaitez ajouter. Donc, même si vous modifiez l'url de tableau, il n'aura pas d'impact sur les fonctions que vous avez déjà créé à l'aide asynchrone.carte.
Puis-je carte la réponse à sa réponse à l'aide de seconde?
OriginalL'auteur caasjj
Essayez ceci:
OriginalL'auteur cshion