У меня есть доля CIFS с Windows Server 2012 R2, установленный на Ubuntu 14.04.2 LTS (ядро 3.13.0-61-родовым), как этоPHP file_exists иногда возвращает ложь для файла на CIFS доли
/и т.д./Fstab
//10.1.2.3/Share /Share cifs credentials=/root/.smbcredentials/share_user,user=share_user,dirmode=0770,filemode=0660,uid=4000,gid=5000,forceuid,forcegid,noserverino,cache=none 0 0
gid=5000
соответствует группе www-data
которая проходит процесс PHP.
Файлы установлены правильно, когда я проверяю через консоль, зарегистрированную как пользователь www-data
, - они являются читаемыми и съемными (операции, которые используются скриптом PHP).
PHP-скрипт обрабатывает около 50-70 000 файлов в день. Файлы создаются на хост-компьютере Windows, и через некоторое время PHP-скрипт, запущенный на машине Linux, уведомляется о новом файле, проверяет, существует ли файл (file_exists
), читает его и удаляет. Обычно все работает нормально, но иногда (от нескольких сотен до 1-2 000 в день) скрипт PHP вызывает ошибку, что файл не существует. Это никогда не должно быть так, поскольку оно уведомляется только о фактически существующих файлах.
Когда я вручную проверяю те файлы, которые были указаны как не существующие, они корректно доступны на машине Ubuntu и имеют дату создания до того, как скрипт PHP проверял их существование.
Затем я запускаю скрипт PHP вручную, чтобы забрать этот файл, и он подхвачен без проблем.
То, что я уже пытался
Есть несколько подобных вопросов, но я, кажется, исчерпали все советы:
- Я добавил
clearstatcache()
перед проверкойfile_exists($f)
- права доступа файлов и каталогов ОК (точно такой же файл правильно подбирается позже)
- Путь, используемый для проверки
file_exists($f)
абсолютный путь без каких-либо специальных символов - пути файлов всегда формата/Share/11/222/333.zip
(с различными цифрами) - Я использовал
noserverino
доли параметра монтирования - Я использовал
cache=none
доли параметра монтирования
/proc/fs/cifs/Stats/
дисплеев как ниже, но я не знаю, есть ли здесь что-то подозрительное. Доля в вопросе 2) \\10.1.2.3\Share
Resources in use
CIFS Session: 1
Share (unique mount targets): 2
SMB Request/Response Buffer: 1 Pool size: 5
SMB Small Req/Resp Buffer: 1 Pool size: 30
Operations (MIDs): 0
6 session 2 share reconnects
Total vfs operations: 133925492 maximum at one time: 11
1) \\10.1.2.3\Share_Archive
SMBs: 53824700 Oplocks breaks: 12
Reads: 699 Bytes: 42507881
Writes: 49175075 Bytes: 801182924574
Flushes: 0
Locks: 12 HardLinks: 0 Symlinks: 0
Opens: 539845 Closes: 539844 Deletes: 156848
Posix Opens: 0 Posix Mkdirs: 0
Mkdirs: 133 Rmdirs: 0
Renames: 0 T2 Renames 0
FindFirst: 21 FNext 28 FClose 0
2) \\10.1.2.3\Share
SMBs: 50466376 Oplocks breaks: 1082284
Reads: 39430299 Bytes: 2255596161939
Writes: 2602 Bytes: 42507782
Flushes: 0
Locks: 1082284 HardLinks: 0 Symlinks: 0
Opens: 2705841 Closes: 2705841 Deletes: 539832
Posix Opens: 0 Posix Mkdirs: 0
Mkdirs: 0 Rmdirs: 0
Renames: 0 T2 Renames 0
FindFirst: 227401 FNext 1422 FClose 0
Один образец, который я думаю, что я вижу, что ошибка возникает только если файл в вопрос уже был обработан (чтение и удален) ранее PHP скрипт. Есть много файлов, которые были правильно обработаны, а затем обработаны позже, но я никогда не видел эту ошибку для файла, который обрабатывается в первый раз. Время между повторной обработкой варьируется от 1 до 20 дней. Для повторной обработки файл просто воссоздается по тому же пути на хосте Windows с обновленным контентом.
В чем может быть проблема? Как лучше исследовать? Как определить, лежит ли проблема на стороне PHP или ОС?
Update
Я переместил программное обеспечение, которое производит файлы в Ubuntu VM, которая монтирует же акции таким же образом. Этот компонент закодирован в Java. Я не вижу никаких проблем при чтении/записи файлов.
Update - подробности PHP
Точный код РНР:
$strFile = zipPath($intApplicationNumber);
clearstatcache();
if(!file_exists($strFile)){
return responseInternalError('ZIP file does not exist', $strFile);
}
intApplicationNumber
является параметр запроса (например, 12345678
.), Который просто преобразуется в путь, zipPath()
функция (например, \Share\12\345\678.zip
- всегда полный путь).
Сценарий может вызываться одновременно с разными номерами приложений, но не будет вызываться одновременно с тем же номером приложения.
Если сбой сценария (возвращает ошибку 'ZIP file does not exist'
), он будет вызываться снова через минуту. Если это не удастся, оно будет постоянно помечено как сбой. Затем, как правило, более чем через час, я могу вызвать скрипт вручную с помощью того же вызова (GET запрос), что это делается на производство и она отлично работает, файл найден и отправлен в ответ:
public static function ResponseRaw($strFile){
ob_end_clean();
self::ReadFileChunked($strFile, false);
exit;
}
protected static function ReadFileChunked($strFile, $blnReturnBytes=true) {
$intChunkSize = 1048576; // 1M
$strBuffer = '';
$intCount = 0;
$fh = fopen($strFile, 'rb');
if($fh === false){
return false;
}
while(!feof($fh)){
$strBuffer = fread($fh, $intChunkSize);
echo $strBuffer;
if($blnReturnBytes){
$intCount += strlen($strBuffer);
}
}
$blnStatus = fclose($fh);
if($blnReturnBytes && $blnStatus){
return $intCount;
}
return $blnStatus;
}
После того, как клиент получает файл, он уведомляет сервер PHP о том, что файл можно переместить в место архива (с помощью copy()
и unlink()
). Эта часть отлично работает.
результата Strace
После нескольких дней без каких-либо ошибок, вновь появилась ошибка. Я побежал strace
и сообщает
access("/Share/11/222/333.zip", F_OK) = -1 ENOENT (No such file or directory)
для некоторых файлов, которые существуют при запуске ls /Share/11/222/333.zip
из командной строки. Поэтому проблема находится на уровне ОС, PHP не должен быть обвинен.
Ошибки начались, когда загрузка на диске на хосте увеличилась (из-за других процессов), поэтому, как представляется, приведенное ниже предложение @ risyasin - это вопрос занятых ресурсов/тайм-аутов.
Я попробую совет @ miguel-svq пропустить тест на существование и сразу перейти на fopen()
и обработать ошибку. Я посмотрю, изменит ли он что-нибудь.
Хороший вопрос. Я уже не первый раз слышал, как что-то вроде этого ненадежно. Обходной путь, который поможет вам немного, заключается в повторном попытке файла file_exists и немедленном прекращении работы скрипта. – DanFromGermany
Спасибо @ DanFromGermany - да, это одна из грязных идей, которые у меня были - повторить (даже после паузы в N секунд), если это временная икота. Но я действительно хотел бы понять, почему это происходит, и исправить это в корне. –
Я действительно не думаю, что речь идет о php, но nfs. могут быть тайм-ауты или занятые ресурсы, поскольку они полагаются на сетевое взаимодействие. 'strace' и' tcpdump' с обеих сторон, чтобы увидеть, что на самом деле происходит, может дать вам подсказки. также попробуйте с пользователем php/webserver при его тестировании. – risyasin