PHP readfile() никогда не заканчивается и заставляет сервер Apache зависать
У меня большая проблема с сервером Apache, приложением php.
Сервер обслуживает сайт с довольно высоким трафиком, который работает с php.
Каждые 24 часа или 48 часов зависает apache, и мне приходится перезагружать его, чтобы снова получить доступ к веб-сайту. Я должен перезагрузить его, потому что apache достигает максимального количества разрешенных процессов / серверов (16000 для меня), и он не может освободить другие процессы, потому что все другие процессы активны.
Веб-сайт, размещенный на этом сервере, является приложением php, которое в конце концов обслуживает файл: допустим, это сервер загрузки.
Файлы запрашиваются браузером через форму, которая отправляет запрос POST.
Проблема в том, что этот почтовый запрос никогда не заканчивается (я вижу, что почти все 16000 процессов на моем сервере являются запросами POST).
Файлы, которые обслуживаются, являются большими файлами (от 10M до 2G), и я передаю их с помощью функции php readfile (я не хочу предоставлять им ссылку href, поэтому я использую запрос POST формы, чтобы пользователь никогда не видел, где находится файл находится в моей файловой системе).
Функция, которая использует php readfile, кажется, никогда не завершится, даже если я использую exit() в конце (см. Фрагмент кода ниже).
Я прошу здесь, чтобы избежать этого бесконечного POST-запроса, вызванного моим php-кодом. Я хочу сохранить POST способ подачи файлов.
Сначала мой конф:
- Ubuntu сервер 14.04
- Apache 2.4 с MPM Prefork
- php 5.5.9 (мод php)
- аппаратное обеспечение: 128G RAM
мой файл mpm_prefork.conf:
<IfModule mpm_prefork_module>
StartServers 512
MinSpareServers 512
MaxSpareServers 1024
ServerLimit 16000 # no problem with my server ram
MaxRequestWorkers 16000
MaxConnectionsPerChild 10000
</IfModule>
мой файл apache2.conf:
...
Timeout 300
KeepAlive On
MaxKeepAliveRequests 500
KeepAliveTimeout 5
...
Мой файл php.ini:
max_execution_time = 7200
мои файлы журнала apache: ничего интересного для моей проблемы
Мунинский график, показывающий, когда возникает проблема:
Состояние моего сервера Apache выглядит следующим образом:
И мой класс сервера (код, который вызывает проблему):
class Server
{
/* the file is served from a remote url source */
public function serveFileFromUrl()
{
if (empty($_POST)) {
return;
}
$url = $_POST['file_url'];
$mime = $_POST['mime'];
$name = sanitizeFileName($_POST['name']) . uniqid() . '.' . $mime;
$size = $_POST['size'];
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) {
// for Internet Explorer
if ($mime == 'mp3') {
header('Content-Type: "audio/' . $mime . '"');
} else {
header('Content-Type: "video/' . $mime . '"');
}
header('Content-disposition: attachment; filename="' . $name . '"');
header('Expires: 0');
if ($size !== '') {
header('Content-Length: ' . $size);
}
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-Transfer-Encoding: binary");
header('Pragma: public');
} else {
// not for internet Explorer
if ($mime == 'mp3') {
header('Content-Type: "audio/' . $mime . '"');
} else {
header('Content-Type: "video/' . $mime . '"');
}
header('Content-disposition: attachment; filename="' . $name . '"');
header('Expires: 0');
if ($size !== '') {
header('Content-Length: ' . $size);
}
header("Content-Transfer-Encoding: Binary");
header('Pragma: no-cache');
}
ob_end_clean(); // fix memory problems with readfile (http://heap.tumblr.com/post/119127049/a-note-about-phps-output-buffer-and-readfile)
flush(); // fix memory problems with readfile
readfile($url);
@ob_end_flush();
exit();
}
/* file is served from my filesystem */
public function serveFileFromPath()
{
if (empty($_POST)) {
return;
}
$url = APP_PATH . '/download/' . $_POST['file_name'];
$mime = $_POST['mime'];
$name = sanitizeFileName($_POST['name']) . '-' . uniqid() . '.' . $mime;
$size = $_POST['size'];
if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) {
// for Internet Explorer
if ($mime == 'mp3') {
header('Content-Type: "audio/' . $mime . '"');
} else {
header('Content-Type: "video/' . $mime . '"');
}
header('Content-disposition: attachment; filename="' . $name . '"');
header('Expires: 0');
if ($size !== '') {
header('Content-Length: ' . $size);
}
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header("Content-Transfer-Encoding: binary");
header('Pragma: public');
} else {
// not for internet Explorer
if ($mime == 'mp3') {
header('Content-Type: "audio/' . $mime . '"');
} else {
header('Content-Type: "video/' . $mime . '"');
}
header('Content-disposition: attachment; filename="' . $name . '"');
header('Expires: 0');
if ($size !== '') {
header('Content-Length: ' . $size);
}
header("Content-Transfer-Encoding: Binary");
header('Pragma: no-cache');
}
ob_end_clean(); // fix memory problems with readfile (http://heap.tumblr.com/post/119127049/a-note-about-phps-output-buffer-and-readfile)
flush(); // fix memory problems with readfile
readfile($url);
@ob_end_flush();
exit();
}
}
Есть ли у кого-нибудь решение, чтобы избежать бесконечных POST-запросов? Я в порядке, чтобы обслуживать файлы с помощью чего-то другого, чем PHP, если это может решить проблему.
Пожалуйста, не дублируйте, я добавил достаточно кода, фрагмент кода и картинки, чтобы конкретизировать этот вопрос:)
1 ответ
mod_xsendfile - хорошая альтернатива доставке файлов на PHP.
https://tn123.org/mod_xsendfile/
В противном случае вы можете просто добавить ограничение времени к вашему PHP-скрипту, чтобы он не мог работать вечно.