对于CIFS共享上的文件,PHP file_exists有时会返回false

Ada*_*lik 27 php linux share mount cifs

我有一个安装在Ubuntu 14.04.2 LTS(内核3.13.0-61-generic)上的Windows Server 2012 R2的CIFS共享

/ 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
Run Code Online (Sandbox Code Playgroud)

gid=5000对应于组www-data运行在PHP过程.

当我通过以www-data用户身份登录的控制台进行检查时,文件正确安装- 它们是可读和可移动的(PHP脚本使用的操作).

PHP脚本每天处理大约50-70,000个文件.这些文件是在主机Windows机器上创建的,一段时间后,Linux机器上运行的PHP脚本会收到有关新文件的通知,检查文件是否存在(file_exists),读取和删除.通常一切正常,但有时(每天几百到1-2 000)PHP脚本会引发文件不存在的错误.绝不应该这样,因为只通知实际存在的文件.

当我手动检查那些报告为不存在的文件时,它们可以在Ubuntu机器上正确访问,并且具有从PHP脚本检查它们存在之前的创建日期.

然后我手动触发PHP脚本来获取该文件,并且它可以毫无问题地被拾取.

我已经尝试过了什么

有许多类似的问题,但我似乎已经用尽了所有的建议:

  • clearstatcache()在检查前添加了file_exists($f)
  • 文件和目录权限都正常(以后正确选取完全相同的文件)
  • 用于检查的路径file_exists($f)是绝对路径,没有特殊字符 - 文件路径始终是格式/Share/11/222/333.zip(具有各种数字)
  • 我使用了noserverinoshare mount参数
  • 我使用了cache=noneshare mount参数

/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
Run Code Online (Sandbox Code Playgroud)

我认为我看到的一种模式是,只有在PHP脚本之前已经处理(读取和删除)有问题的文件时才会引发错误.有许多文件已经正确处理,然后再次处理,但我从未见过第一次处理的文件的错误.重新处理之间的时间从1天到大约20天不等.对于重新处理,只需在Windows主机上具有更新内容的相同路径下重新创建该文件.

可能是什么问题?我怎样才能更好地调查?如何确定问题出在PHP或OS端?


更新

我已经将生成文件的软件移动到以相同方式安装相同共享的Ubuntu VM.该组件使用Java编码.在读取/写入文件时,我没有看到任何问题.


更新 - PHP详细信息

确切的PHP代码是:

$strFile = zipPath($intApplicationNumber);

clearstatcache();

if(!file_exists($strFile)){
    return responseInternalError('ZIP file does not exist', $strFile);
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

在客户端收到文件后,他通知PHP服务器该文件可以移动到存档位置(通过copy()unlink()).那部分工作正常.


STRACE结果

几天没有错误后,错误再次出现.我跑了strace,它报告

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

对于从命令行运行时存在的某些文件ls /Share/11/222/333.zip.因此问题出在操作系统级别,PHP不应该受到指责.

当主机磁盘上的负载增加时(由于其他进程),错误开始出现,因此@ risyasin的建议似乎最有可能 - 这是资源忙/超时的问题.

我将尝试@ miguel-svq建议跳过存在测试,fopen()然后立即去处理错误.我会看看它是否会改变任何东西.

jav*_*ezg 1

您可以尝试使用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
Run Code Online (Sandbox Code Playgroud)