web-dev-qa-db-fra.com

Wget parallèle dans Bash

Je reçois un tas de pages relativement petites d'un site Web et je me demandais si je pouvais le faire en parallèle dans Bash. Actuellement, mon code ressemble à ceci, mais il faut un certain temps pour l'exécuter (je pense que ce qui me ralentit est la latence de la connexion).

for i in {1..42}
do
    wget "https://www.example.com/page$i.html"
done

J'ai entendu parler de l'utilisation de xargs, mais je n'en sais rien et la page de manuel est très déroutante. Des idées? Est-il même possible de le faire en parallèle? Y a-t-il une autre façon de s'attaquer à cela?

68
Jonathon Vandezande

Il est préférable de pousser wget en arrière-plan en utilisant & ou -b, vous pouvez utiliser xargs dans le même sens, et mieux.

L'avantage est que xargs va se synchroniser correctement sans travail supplémentaire. Ce qui signifie que vous pouvez accéder en toute sécurité aux fichiers téléchargés (en supposant qu'aucune erreur ne se produise). Tous les téléchargements auront terminé (ou échoué) une fois que xargs se sera terminé, et vous savez par le code de sortie si tout s'est bien passé. Cela est préférable à une attente occupée avec sleep et à un test de fin manuel.

En admettant que URL_LIST est une variable contenant toutes les URL (peut être construite avec une boucle dans l'exemple de l'OP, mais peut également être une liste générée manuellement), exécutant ceci:

echo $URL_LIST | xargs -n 1 -P 8 wget -q

passera un argument à la fois (-n 1) à wget et exécutez au plus 8 processus parallèles wget à la fois (-P 8). xarg revient après la fin du dernier processus généré, ce que nous voulions savoir. Pas de supercherie supplémentaire nécessaire.

Le "nombre magique" de 8 téléchargements parallèles que j'ai choisi n'est pas figé, mais c'est probablement un bon compromis. Il existe deux facteurs pour "maximiser" une série de téléchargements:

L'une consiste à remplir "le câble", c'est-à-dire à utiliser la bande passante disponible. En supposant des conditions "normales" (le serveur a plus de bande passante que le client), c'est déjà le cas avec un ou au plus deux téléchargements. Le fait de lancer plus de connexions au problème entraînera uniquement la perte de paquets et TCP contrôle de congestion se déclenche, et [~ # ~] n [~ # ~] téléchargements avec une bande passante asymptotique 1/N chacun, avec le même effet net (moins les paquets perdus, moins la taille de la fenêtre La perte de paquets est une chose normale qui se produit dans un réseau IP, c'est ainsi que le contrôle de la congestion est censé fonctionner (même avec une seule connexion), et normalement l'impact est pratiquement nul. Cependant, ayant un nombre déraisonnablement élevé de les connexions amplifient cet effet, il peut donc être perceptible. En tout cas, il ne fait rien de plus rapide.

Le deuxième facteur est l'établissement de la connexion et le traitement des demandes. Ici, avoir quelques connexions supplémentaires en vol aide vraiment . Le problème auquel on est confronté est la latence de deux allers-retours (généralement 20 à 40 ms dans la même zone géographique, 200 à 300 ms intercontinentaux) plus les 1 à 2 millisecondes impaires dont le serveur a réellement besoin pour traiter la demande et envoyer une réponse. à la prise. Ce n'est pas beaucoup de temps en soi , mais multiplié par quelques centaines/milliers de demandes, cela s'additionne rapidement.
Le fait d'avoir une demi-douzaine à une douzaine de demandes en vol masque la majeure partie ou la totalité de cette latence (elle est toujours là, mais puisqu'elle se chevauche, elle ne se résume pas!). Dans le même temps, le fait de n'avoir que quelques connexions simultanées n'a pas d'effets néfastes, comme provoquer une congestion excessive ou forcer un serveur à bifurquer de nouveaux processus.

172
Damon

L'exécution des tâches en arrière-plan n'est pas une solution évolutive: si vous récupérez 10 000 URL, vous ne voudrez probablement en récupérer que quelques-unes (disons 100) en parallèle. GNU Parallel est fait pour ça:

seq 10000 | parallel -j100 wget https://www.example.com/page{}.html

Voir la page de manuel pour plus d'exemples: http://www.gnu.org/software/parallel/man.html#example__download_10_images_for_each_of_the_past_30_days

57
Ole Tange

Vous pouvez utiliser -b option:

wget -b "https://www.example.com/page$i.html"

Si vous ne voulez pas de fichiers journaux, ajoutez l'option -o /dev/null.

 - o Messages du journal FILE vers FILE. 
9
uzsolt

L'ajout d'une esperluette à une commande la fait fonctionner en arrière-plan

for i in {1..42}
do
    wget "https://www.example.com/page$i.html" &
done
7
Jack Edmonds

La version 2 de wget semble implémenter plusieurs connexions. Le lien du projet dans github: https://github.com/rockdaboot/wget2

1
user9869932