У меня есть общий ресурс CIFS от Windows Server 2012 R2, установленный на Ubuntu 14.04.2 LTS (ядро 3.13.0-61-generic), подобное этому
/и т.д. /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 script, запущенный на машине Linux, уведомляется о новом файле, проверяет, существует ли файл (file_exists
), читает его и удаляет. Обычно все работает нормально, но иногда (от нескольких сотен до 1-2 000 в день) PHP скрипт вызывает ошибку, что файл не существует. Это никогда не должно быть так, поскольку оно уведомляется только о фактически существующих файлах.
Когда я вручную проверяю те файлы, которые были указаны как не существующие, они корректно доступны на машине Ubuntu и имеют дату создания до того, как PHP script проверил их существование.
Затем я запускаю PHP script вручную, чтобы забрать этот файл, и он подбирается без проблем.
Что я уже пробовал
Есть несколько похожих вопросов, но я, кажется, исчерпал все советы:
- Я добавил
clearstatcache()
перед проверкойfile_exists($f)
- Разрешения для файлов и каталогов в порядке (точно такой же файл будет получен позже)
- Путь, используемый для проверки
file_exists($f)
, - это абсолютный путь без специальных символов - пути к файлам всегда имеют формат/Share/11/222/333.zip
(с различными цифрами) - Я использовал параметр
- Я использовал параметр
/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 script. Есть много файлов, которые были правильно обработаны, а затем обработаны позже, но я никогда не видел эту ошибку для файла, который обрабатывается в первый раз. Время между повторной обработкой варьируется от 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
- всегда полный путь).
script может быть вызван одновременно с разными номерами приложений, но не будет вызываться одновременно с тем же номером приложения.
Если сбой script (возвращает ошибку 'ZIP file does not exist'
), он будет вызываться снова через минуту. Если это не удастся, оно будет постоянно помечено как сбой. Затем, как правило, более часа спустя, я могу вызвать script вручную с тем же вызовом (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()
и обработать ошибку. Я посмотрю, изменит ли он что-нибудь.