У меня есть общий ресурс 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-скрипт, работающий на 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()
сразу и обработать ошибку тогда. Я посмотрю, если это что-то изменит.
Вы можете попробовать использовать directio опция, позволяющая избежать кэширования данных inode для файлов, открытых на этом монтировании:
//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,directio 0 0
Это вряд ли является окончательным ответом на мою проблему, скорее это краткое изложение того, что я узнал и с чем я согласился.
В основе проблемы лежит то, что именно ОС сообщает, что файл не существует. Бег strace
иногда показывает
access("/Share/11/222/333.zip", F_OK) = -1 ENOENT (No such file or directory)
для файлов, которые существуют (и отображаются, когда перечислены с ls
).
Общий хост Windows иногда находился под большой загрузкой диска. Я переместил один из общих ресурсов на другой хост, чтобы теперь нагрузка распределялась между ними. Кроме того, общая нагрузка на систему в последнее время немного меньше. Всякий раз, когда я получаю сообщение об ошибке, что файл не существует, я повторяю запрос через некоторое время, и его больше нет.