2012-01-17 3 views
7

У меня возникла следующая проблема с моим VPS-сервером.Запуск параллельных PHP-скриптов

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

<?php 
header("Content-type: application/octet-stream"); 
readfile("really-big-file.zip"); 
exit(); 
?> 

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

Проблема в том, что во время такой загрузки сайт не отвечает на другие запросы.

+0

Не то, чтобы это проблема, но при обслуживании больших файлов вы всегда должны называть 'set_time_limit (0);'. На данный момент это не должно иметь никакого значения, но у вас возникнут потенциальные проблемы, которые могут возникнуть, если вы переместите это в какой-то момент на платформу Windows * *. – DaveRandom

+1

Как вы обнаружили проблему? Испытываете ли вы это, делая несколько запросов с одного и того же компьютера? И вы используете сеансы? – DaveRandom

+0

@DaveRandom Я заметил проблему, когда попытался загрузить несколько файлов (они были поставлены в очередь для загрузки). Я использую сеансы - просто пытался, и похоже, что это ограничение не влияет на другие сеансы. Спасибо за ваши мысли. Сейчас я буду исследовать. –

ответ

30

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

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

Решение на самом деле очень просто: позвоните session_write_close(), прежде чем вы начнете выводить файл. Это закроет файл сеанса, освободит блокировку и разрешит выполнение последующих параллельных запросов.

+0

Спасибо, в этом была проблема. –

+0

Хорошая точка и +1 – Lion

+0

Просто отлично. Ты сделал мой день! – Claudix

1

Каким образом сервер не отвечает на другие запросы? Это «Ожидание example.com ...» или это дает ошибку любого рода?

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

РЕДАКТИРОВАТЬ: пока не был ответом на этот вопрос, спрашивающий спросил о чтении файла, помеченного. Вот функция, которую я использую. Поставьте на него полный путь к файлу.

function readfile_chunked($file_path, $retbytes = true) 
{ 
$buffer = ''; 
$cnt = 0; 
$chunksize = 1 * (1024 * 1024); // 1 = 1MB chunk size 
$handle = fopen($file_path, 'rb'); 
if ($handle === false) { 
    return false; 
} 
while (!feof($handle)) { 
    $buffer = fread($handle, $chunksize); 
    echo $buffer; 
    ob_flush(); 
    flush(); 
    if ($retbytes) { 
     $cnt += strlen($buffer); 
    } 
} 
    $status = fclose($handle); 
    if ($retbytes && $status) { 
     return $cnt; // return num. bytes delivered like readfile() does. 
} 
    return $status; 
} 
+0

Новые запросы ждут завершения загрузки. Я не совсем уверен, как я могу реализовать загрузку chunk в PHP? –

+0

Привет, Эмиль М. Я добавил функцию, которую я использую, чтобы прочитать файл в кусках. –

+0

Я только что ответил на ваш ответ, поэтому позвольте мне объяснить. readfile не считывает содержимое файла в память, а затем записывает его в stdout. Нет, он вызывает внутреннюю функцию '_php_stream_passthru()', которая представляет собой петлю C, похожую на вашу, за исключением того, что она не выполняет флеши, поскольку настройка INI 'output_buffering' будет достигать этого в любом случае. И 'zlib.output_compression' и т. Д. Должны быть ложными для вывода уже сжатого содержимого. Ваше предложение просто добавляет сложности без каких-либо преимуществ. – TerryE

0

Я пробовал разные подходы (чтение и отправку файлов небольших порций [см комментариев на readfile в PHP документе], используя груши HTTP_Download), но я всегда столкнулся с проблемами производительности, когда файлы становятся большими.

Существует Apache mod , где вы можете сделать свою бизнес-логику, а затем делегировать загрузку в Apache. Загрузка не будет общедоступной. Я думаю, это самое элегантное решение проблемы.

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

2

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

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

Либо wget с той же машины или другого браузера на другой машине.

0

То же самое происходит со мной, и я не использую сеансы. session.auto_start установлен в 0 В моем примере скрипт запускается только «sleep (5)», и добавление «session_write_close()» вначале не решает проблему.

0

Проверьте свой файл httpd.conf. Возможно, у вас есть «KeepAlive On», и поэтому ваш второй запрос зависает до тех пор, пока первый не будет завершен. В общем, ваш PHP-скрипт не должен позволять посетителям долго ждать. Если вам нужно загрузить что-то большое, сделайте это в отдельном внутреннем запросе, который пользователь не имеет прямого контроля. До его завершения верните некоторый статус «выполнения» конечным пользователям и, когда это будет сделано, обработайте фактические результаты.

Смежные вопросы