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

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

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

У меня есть несколько решений, которые следующие:

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

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

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

Ответ 1

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

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

Ответ 2

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

set_time_limit(0);

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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