2016-02-18 2 views
27

У меня есть доля 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() и обработать ошибку. Я посмотрю, изменит ли он что-нибудь.

+1

Хороший вопрос. Я уже не первый раз слышал, как что-то вроде этого ненадежно. Обходной путь, который поможет вам немного, заключается в повторном попытке файла file_exists и немедленном прекращении работы скрипта. – DanFromGermany

+0

Спасибо @ DanFromGermany - да, это одна из грязных идей, которые у меня были - повторить (даже после паузы в N секунд), если это временная икота. Но я действительно хотел бы понять, почему это происходит, и исправить это в корне. –

+3

Я действительно не думаю, что речь идет о php, но nfs. могут быть тайм-ауты или занятые ресурсы, поскольку они полагаются на сетевое взаимодействие. 'strace' и' tcpdump' с обеих сторон, чтобы увидеть, что на самом деле происходит, может дать вам подсказки. также попробуйте с пользователем php/webserver при его тестировании. – risyasin

ответ

1

Вы можете попытаться использовать directio варианта делать, чтобы избежать кэширования данных инода на файлы, открытые на этом горе:

//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 
+0

[man page] (http://linux.die.net/man/8/mount.cifs) говорит: «Этот параметр будет устаревшим в 3.7. Пользователи должны использовать cache = none вместо более поздних ядер». Мое ядро ​​3.13, и у меня уже есть 'cache = none'. Есть ли смысл использовать 'directio'? –

0

Это вряд ли окончательный ответ на мою проблему, а краткое изложение того, что Я узнал и с чем согласился.

В основе проблемы лежит то, что ОС сообщает, что файл не существует. Запуск strace показывает иногда

access("/Share/11/222/333.zip", F_OK) = -1 ENOENT (No such file or directory) 

для файлов, которые существуют (и появляются, когда перечисленные с ls).

Хост-узел Windows часто находился под нагрузкой на жесткий диск. То, что я сделал, - это переместить одну из долей на другой хост, чтобы теперь распределялась загрузка между ними. Кроме того, общая нагрузка на систему в последнее время немного легче. Всякий раз, когда я получаю ошибку о том, что файл не существует, я несколько раз повторю запрос, и его больше нет.

Смежные вопросы