Каков наилучший способ справиться с этим: большая загрузка через PHP + медленное соединение с клиента = время ожидания скрипта перед загрузкой файла полностью

Мой клиент хотел, чтобы пользователи предлагали загрузки, но только после того, как они заполнили регистрационную форму (в основном имя и адрес электронной почты). Электронная почта отправляется пользователю со ссылками на загружаемый контент. Ссылки содержат хеш регистрации, уникальный для пакета, файла и пользователя, и они фактически переходят на страницу PHP, которая регистрирует каждую загрузку и выталкивает файл, записывая его в stdout (вместе с соответствующими заголовками. Это решение имеет присущие недостатки , но это то, как они хотели это сделать. Надо сказать, что я сильно затруднил их: 1.) ограничил размеры загружаемых файлов и 2.) подумал об использовании CDN (у них есть международные клиенты, но они размещены в США на 2 зеркальных серверах и балансировщик нагрузки, который использует липкие IP-адреса). Во всяком случае, это «работает для меня», но некоторые из их международных клиентов находятся на очень медленных соединениях (скорость d / l ~ 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 будут отклонены или, по крайней мере, оттолкнуты. Итак, хороший способ сделать это, или есть что-то еще, что я не рассматривал?

(Не стесняйтесь оспорить исходное решение.)

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

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

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

set_time_limit(0); 

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

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

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

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

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

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

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

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

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

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