У меня есть общий ресурс CIFS из Windows Server 2012 R2, установленный на Ubuntu 14.04.2 LTS (ядро 3.13.0-61-generic), как это
/etc/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 или ОС?
Обновить
Я переместил программное обеспечение, которое создает файлы, на виртуальную машину Ubuntu, которая монтирует те же общие ресурсы таким же образом. Этот компонент написан на Java. Я не вижу никаких проблем при чтении/записи файлов.
Обновление – сведения о PHP
Точный код 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()
, а затем обработать ошибку. Я посмотрю, изменит ли это что-нибудь.
strace
иtcpdump
с обеих сторон, чтобы увидеть, что на самом деле происходит, могут дать вам подсказки. также попробуйте с пользователем php/webserver при тестировании. - person risyasin   schedule 18.03.2016file_exists(realpath($f))
, который является предложенным обходным путем в первой предоставленной вами ссылке. Второй пост посвящен 64-битным номерам инодов в 32-битных системах — я уже сталкивался с этим постом и смонтировал общий ресурс с опциейnoserverino
. Кроме того, Ubuntu 64-битная, так что это не должно быть проблемой... - person Adam Michalik   schedule 29.03.2016fopen
ing, чтобы увидеть, существует ли он, может быть, у него больше шансов на успех? - person toster-cx   schedule 31.03.2016