Параллельный wget в Bash

Я получаю кучу относительно небольших страниц с сайта и задаюсь вопросом, могу ли я как-то сделать это параллельно в Bash. В настоящее время мой код выглядит так, но для выполнения требуется некоторое время (я думаю, что замедляет меня - это латентность в соединении).

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

Я слышал об использовании xargs, но я ничего не знаю об этом, и страница man очень запутанна. Есть идеи? Возможно ли это сделать параллельно? Есть ли другой способ, чтобы я мог атаковать это?

Ответ 1

В большинстве случаев предпочтительнее нажать wget в фоновом режиме с помощью & или -b, вы можете использовать xargs для того же эффекта и лучше.

Преимущество состоит в том, что xargs будет правильно синхронизироваться без дополнительной работы. Это означает, что вы можете безопасно получать доступ к загруженным файлам (при условии, что ошибка не возникает). Все загрузки будут завершены (или не удалены) после выхода xargs, и по коду выхода вы знаете, все ли прошло хорошо. Это гораздо предпочтительнее ожидание с sleep и тестирование для завершения вручную.

Предполагая, что URL_LIST - это переменная, содержащая все URL-адреса (может быть построена с помощью цикла в примере OP, но также может быть списком, созданным вручную), запустив это:

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

будет передавать один аргумент за раз (-n 1) до wget и выполнять не более 8 параллельных процессов wget за раз (-P 8). xarg возвращается после завершения последнего порожденного процесса, что мы хотели знать. Никаких дополнительных обманщиков не требуется.

"Магическое число" из 8 параллельных загрузок, которые я выбрал, не задано в камне, но это, вероятно, хороший компромисс. В "максимизации" серии загрузок есть два фактора:

Один заполняет "кабель", то есть использует доступную полосу пропускания. Предполагая, что "нормальные" условия (сервер имеет большую пропускную способность, чем клиент), это уже происходит с одной или двумя загрузками. Выбросы большего количества подключений при этой проблеме приведут только к удалению пакетов и управлению нагрузкой на TCP-загрузку, а N загружает с асимптотически шириной 1/N каждый к одному и тому же сетевому эффекту (минус упавшие пакеты, минус восстановление размера окна). Упавшие пакеты - это обычная вещь, которая должна произойти в сети IP, так как предполагается, что управление перегрузкой должно работать (даже с одним соединением), и обычно воздействие практически равно нулю. Однако наличие неоправданно большого числа соединений усиливает этот эффект, поэтому это может стать заметным. В любом случае, это не делает ничего быстрее.

Второй фактор - создание соединения и обработка запросов. Здесь действительно помогает несколько дополнительных соединений в полете. Одной из проблем является латентность двух круглых поездок (обычно 20-40 мс в одной и той же географической области, 200-300 мс между континентами) плюс нечетные 1-2 миллисекунды, которые серверу действительно нужно обработать запрос и нажать ответ к гнезду. Это не так много времени, но умножается на несколько сотен тысяч запросов, это быстро складывается.
Имея что-то от полудюжины до десятка запросов в полете, скрывает большую или всю эту задержку (она все еще там, но поскольку она перекрывается, она не суммируется!). В то же время наличие нескольких параллельных соединений не оказывает неблагоприятного воздействия, например, вызывает чрезмерную перегрузку или принуждает сервер к открытию новых процессов.

Ответ 2

Просто выполнение заданий в фоновом режиме не является масштабируемым решением: если вы извлекаете 10000 URL-адресов, вы, вероятно, хотите только получить несколько (скажем, 100) параллельно. GNU Parallel для этого:

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

Дополнительную информацию см. на странице руководства: http://www.gnu.org/software/parallel/man.html#example__download_10_images_for_each_of_the_past_30_days

Ответ 3

Вы можете использовать опцию -b:

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

Если вы не хотите файлы журнала, добавьте опцию -o /dev/null.

-o FILE  log messages to FILE.

Ответ 4

Добавление амперсанда в команду заставляет его работать в фоновом режиме

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

Ответ 5

В версии 2 wget реализовано несколько соединений. Ссылка проекта в github: https://github.com/rockdaboot/wget2