Каков наилучший способ справиться с этим: большая загрузка через PHP + медленное соединение с клиента = время ожидания сценария до полной загрузки файла - PullRequest
11 голосов
/ 31 августа 2010

Мой клиент хотел предложить пользователям возможность загрузки, но только после того, как они заполнили регистрационную форму (в основном имя и адрес электронной почты). Пользователю отправляется электронное письмо со ссылками на загружаемый контент. Ссылки содержат регистрационный хеш, уникальный для пакета, файла и пользователя, и они фактически переходят на страницу PHP, которая регистрирует каждую загрузку и выталкивает файл, записывая его в стандартный вывод (вместе с соответствующими заголовками. Это решение имеет присущие ему недостатки , но это то, как они хотели это сделать. Надо сказать, что я сильно их подтолкнул к 1.) ограничить размеры загружаемых файлов и 2.) подумать об использовании CDN (у них есть международные клиенты, но они размещены в США на 2 зеркальных серверах и балансировщик нагрузки, который использует липкие IP). В любом случае, это «работает для меня», но некоторые из их международных клиентов используют очень медленные соединения (скорость передачи данных ~ 60 кБ / с), а некоторые из этих файлов довольно большие (150 МБ). Поскольку это PHP-скрипт, который обслуживает эти файлы, он ограничен настройкой тайм-аута скрипта. Сначала я установил это значение на 300 секунд (5 минут), но этого было недостаточно для некоторых пользователей бета-версии. Затем я попытался вычислить время ожидания сценария на основе размера файла, разделенного на соединение со скоростью 100 Кбит / с, но некоторые из этих пользователей работают даже медленнее.

Теперь клиент хочет просто увеличить значение тайм-аута. Я не хочу удалять тайм-аут все вместе на случай, если скрипт каким-то образом попадет в бесконечный цикл. Я также не хочу произвольно увеличивать время ожидания для некоторой общей скорости соединения с наименьшим общим знаменателем (большинство людей загружают намного быстрее, чем 100 Кбит / с). И я также хочу иметь возможность сказать клиенту в какой-то момент: «Посмотрите, эти файлы слишком велики для такой обработки. Вы влияете на производительность остальной части веб-сайта с помощью этих 40-минутных подключений. Нам либо нужно переосмыслите, как они доставляются, или используйте файлы меньшего размера. "

Я имею в виду пару решений:

  1. CDN - переместите файлы в службу CDN, например Amazon или Google. Мы все еще можем регистрировать попытки загрузки через файл PHP, но затем перенаправить браузер в настоящий файл. Одним из недостатков этого является то, что пользователь может обойти сценарий и загрузить непосредственно из CDN, как только у него будет URL-адрес (который можно найти, просмотрев заголовки HTTP). Это неплохо, но нежелательно.
  2. Расширение фермы серверов - Расширение фермы серверов от 2 до 4+ серверов и удаление правила закрепления IP из балансировщика нагрузки. Недостаток: это серверы Windows, поэтому они дорогие. Нет никаких причин, по которым они не могут быть блоками Linux, но настройка всех новых блоков может занять больше времени, чем клиент.
  3. Настройка 2 новых серверов строго для обслуживания этих загрузок - В основном те же преимущества и недостатки, что и у # 2, за исключением того, что мы могли бы по крайней мере изолировать остальную часть веб-сайта от (и точно настроить новые серверы для ) этот конкретный процесс. Мы также можем довольно легко сделать эти Linux-боксы.
  4. Определение скорости подключения пользователей - Я имел в виду способ определения текущей скорости пользователя с помощью AJAX на целевой странице загрузки, чтобы определить, сколько времени занимает загрузка статического файла с известный размер файла, затем отправка этой информации на сервер и вычисление времени ожидания на основе этой информации. Это не идеально, но лучше, чем оценивать скорость соединения слишком высокую или слишком низкую. Я не уверен, как получить информацию о скорости обратно на сервер, поскольку в настоящее время мы используем заголовок перенаправления, отправляемый с сервера.

Скорее всего, # 1-3 будут отклонены или, по крайней мере, отодвинуты. Так 4 хорошо ли это делать, или есть что-то еще, что я не учел?

(Не стесняйтесь оспаривать оригинальное решение.)

Ответы [ 6 ]

3 голосов
/ 31 августа 2010

Используйте X-SENDFILE.Большинство веб-серверов поддерживают его либо изначально, либо через плагин (apache).

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

1 голос
/ 31 августа 2010

Я немного сдержан по поводу # 4.Злоумышленник может подделать поддельный AJAX-запрос, чтобы установить для вашего тайм-аута очень высокое значение, после чего он может ввести вас в бесконечный цикл.(Если бы вы беспокоились об этом в первую очередь)

Я бы предложил решение, подобное @prodigitalson.Вы можете создавать каталоги, используя хэш-значения /downloads/389a002392ag02/myfile.zip, которые являются символическими ссылками на настоящий файл.Ваш PHP-скрипт перенаправляет на тот файл, который обслуживается HTTP-сервером.Символическая ссылка периодически удаляется.

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

1 голос
/ 31 августа 2010

Простым решением будет отключение тайм-аута. Вы можете сделать это для каждого запроса с помощью:

set_time_limit(0);

Если ваш скрипт не содержит ошибок, это не должно быть проблемой - если ваш сервер не может обрабатывать столько одновременных соединений из-за медленных клиентов.

В этом случае # 1, # 2 и # 3 - это два хороших решения, и я бы выбрал то, что дешевле. Ваши опасения по поводу # 1 могут быть смягчены путем создания токенов загрузки, которые можно использовать только один раз или в течение небольшого периода времени.

Вариант № 4, на мой взгляд, не очень хороший вариант. Скорость может сильно варьироваться во время загрузки, поэтому любая оценка, которую вы сделали бы на начальном этапе, была бы, со значительной вероятностью, неверной.

0 голосов
/ 31 августа 2010

Вы можете создать временный файл на диске или символическую ссылку, а затем перенаправить (используя header()) на этот временный файл.Тогда cronjob может прийти и удалить «просроченные» временные файлы.Ключевым моментом здесь является то, что с каждой загрузкой должен быть связан уникальный временный файл.

0 голосов
/ 31 августа 2010

Я думаю, вы могли бы сделать что-то вроде # 1, за исключением того, что держите его на своих серверах и обходите его через php напрямую. После того, как произойдет авторизация / утверждение с php, этот скрипт создаст временную ссылку на файл для загрузки через традиционный http. Если для идентификатора * nix сделать это через символическую ссылку на настоящий файл и запускать задание cron каждые n минут, чтобы очистить старые ссылки на файл.

0 голосов
/ 31 августа 2010

Я думаю, что основная проблема заключается в обслуживании файла через скрипт PHP. Не только у вас будет проблема тайм-аута. Кроме того, во время отправки файла клиенту выполняется процесс веб-сервера.

Я бы порекомендовал какой-то # 1. Это не обязательно должен быть CDN, но скрипт PHP должен перенаправлять непосредственно в файл. Вы можете проверить обход, используя правило перезаписи и параметр, который проверит, совпадают ли параметр и текущее время запроса.

...