Назад | Перейти на главную страницу

Обратный прокси-сервер Nginx вызывает чрезмерное время загрузки файлов

Обзор

Мое веб-приложение позволяет пользователям загружать файлы, которые хранятся на s3, через мои серверы. Когда пользователь запрашивает файл, мой веб-сервер извлекает его из s3, а затем отправляет его клиенту.

Недавно я развернул балансировщик нагрузки, сделав мои текущие настройки следующим образом:

Обратите внимание, что в настоящее время у меня есть только один веб-сервер для упрощения отладки.

Первоначальная проблема

После того, как я развернул балансировщик нагрузки, я заметил, что загрузка больших файлов (размером более 4 МБ) завершалась ошибкой с таймаутом шлюза 504 через 60 секунд.

Я просмотрел журнал ошибок балансировщика нагрузки nginx для сайта и увидел несколько таких записей:

[error] 11770#11770: *40 upstream timed out (110: Connection timed out) while reading response header from upstream, client: XXXX, ...

Когда я посмотрел журнал ошибок nginx веб-сервера для сайта, я увидел похожие записи:

[error] 6632#6632: *41 upstream timed out (110: Connection timed out) while reading response header from upstream, client: ...
[error] 6632#6632: *85 upstream timed out (110: Connection timed out) while reading response header from upstream, client: ...
[error] 7163#7163: *41 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: ...
[error] 7505#7505: *41 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: ...
[error] 7505#7505: *91 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: ....

И глядя на журналы ошибок php-fpm на моем веб-сервере:

WARNING: [pool www] child 3011, script '/home/forge/XXX.com/public/index.php' (request: "GET /index.php") execution timed out (64.950545 sec), terminating
WARNING: [pool www] child 3011 exited on signal 15 (SIGTERM) after 1140.059968 seconds from start
WARNING: [pool www] server reached pm.max_children setting (5), consider raising it
WARNING: [pool www] child 4260, script '/home/forge/XXX.com/public/index.php' (request: "GET /index.php") execution timed out (68.171099 sec), terminating
WARNING: [pool www] child 4260 exited on signal 15 (SIGTERM) after 160.005837 seconds from start
NOTICE: [pool www] child 4271 started

Я объяснил это тем, что у моих тайм-аутов выполнения php и тайм-аутов соединения nginx слишком мало, поэтому я увеличил их, выполнив следующие действия:

Это в некоторой степени устранило мою первоначальную проблему, так как теперь я могу загружать файлы большего размера (проверено до 25 МБ).

Следующая проблема - медленные загрузки

После указанных выше изменений конфигурации я могу загружать файлы без тайм-аута, однако для начала загрузки требуется слишком много времени (~ 300 секунд), а сама загрузка идет медленно (небольшая проблема).

Процесс загрузки файла следующий:

Для справки, на веб-сервере выполняется следующая функция:

public function show($projectID, $documentID, $revisionID, $fileID)
{
    $fileEntry = File::find($fileID);

    $path = $fileEntry->path();
    $file = Storage::get($path);
    $size = Storage::size($path);

    return Response::make($file, 200)
            ->header('Content-Type', $fileEntry->mime)
            ->header('Content-Disposition', 'attachment; filename="' . $fileEntry->original_filename . '"')
            ->header('Content-Length:', $size);
}

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

Что могло быть причиной этой проблемы? Я не думаю, что когда-либо сталкивался с этой проблемой до развертывания балансировщика нагрузки.

Если я загружаю файл непосредственно из s3, время загрузки составляет лишь часть от общего времени при передаче через мой сервер, поэтому я не верю, что проблема заключается в двойной обработке. Это может быть связано с размером буфера или памяти?

Дополнительная информация:

Хорошая редакция, все намного яснее.

Похоже, это тайм-аут приложения в PHP. Я предполагаю, что PHP полностью загружает большие файлы во временное место, а затем возвращает их, а не передает их обратно напрямую. Этим объясняется задержка, но не столько низкая скорость. Я даже не знаю, практична ли прямая потоковая передача прямо с S3 обратно через ваш стек - исследуйте, если требуется (вами). Я бы также посмотрел, имеет ли значение PHP5, я нашел PHP7 менее надежным в нескольких крайних случаях.

Я бы отслеживал точное время, когда запросы приходят, попадают на каждый сервер и ответы возвращаются каждым сервером, чтобы вы могли полностью отслеживать запрос. Это особенно верно для уровня PHP: добавьте ведение журнала, когда сервер приложений получает запрос, когда он извлекается из S3 и когда он начинает отправлять его обратно клиенту.

Скорость загрузки немного озадачивает. Я бы нашел способ проверить скорость между вашим PHP-сервером и S3 - сделайте завиток или что-то в этом роде - это может быть простая проблема с пропускной способностью или задержка, уменьшающая доступную пропускную способность. Обходной путь может заключаться в использовании CloudFront, но, вероятно, нет, так как это ускорит загрузку только при втором запросе, а не при первом.

После того, как вы все это сделаете, если вы еще не разработали, опубликуйте информацию, которую вы обнаружите, особенно точное время, когда несколько запросов попадают на каждый уровень, и время, когда ответы возвращаются.