file_scan_directory()大约需要10秒才能执行


10

我注意到使用xhprof file_scan_directory()加载首页时需要花费10秒钟以上的时间来执行。为什么要花这么长时间?

这是xhprofile的输出:

屏幕截图


您不能在“首页”上“ file_scan_directory”,因为首页是数据库表中的条目,而不是文件系统路径。
Letharion'3

@Letharion我认为您误解了我的问题。我的意思是首页加载此功能所花费的时间。我编辑了问题。
hknik 2012年

首页确实与该功能在特定时间段内有关系吗?您实际上在扫描哪个目录?目录中有什么?
Letharion'3

啊哈!我以为您自己调用了该函数,并且想知道为什么不提供更多详细信息。贝尔迪尔的答案看起来很合理。:)
Letharion'3

Answers:


14

听起来您受Drupal 7中一个已知问题的影响。

最有可能的是,当缺少多个模块时,您点击避免重新扫描模块目录。如果您的安装中缺少一些模块,则会发生这种情况。尝试检查您的系统表:

SELECT name, filename FROM system WHERE type = 'module' AND status = 1 ORDER BY filename

并清除文件系统中仍然启用但缺少的所有模块。

总体而言,Drupal 7比Drupal 6在资源上更加友好且可扩展,除了一些不幸的回归之类。

查看这些功能,看起来确实缺少模块或模块的单个文件。看一下drupal_get_filename(),它会调用drupal_system_listing(),如果找不到所请求的文件,则会调用此函数。在调用drupal_system_listing()之前添加一个dpm(func_get_args()),它应该告诉您找不到的文件。


否。不幸的是(!)文件系统中没有模块缺失
hknik 2012年

然后,您需要跟踪调用的来源,可能是自定义或contrib模块做错了什么。单击file_scan_directory()的父函数,然后使用父函数列表更新初始帖子。
贝迪尔(Berdir)2012年

查看这些功能,看起来确实缺少模块或模块的单个文件。看一下drupal_get_filename:api.drupal.org/api/drupal/includes ! bootstrap.inc/function/…。如果找不到请求的文件,它将调用该函数。在调用drupal_system_listing()之前添加一个dpm(func_get_args()),它应该告诉您它找不到哪个函数。
贝迪尔2012年

@Berdir您的最新评论应该是在您的答案中,因为它是相关的。
kiamlaluno

指向“ Drupal 7中的已知问题”和“避免重新扫描模块目录”的链接已断开。两者都是以前的stackexchange答案。有人还有其他参考吗?
rfay

4

可能会出现此问题的原因有很多,令我非常沮丧的是,我现在对这些原因有些了解。令人沮丧的是,如果您在将Drupal核心升级到7.33+之后刚刚注意到此问题,那么即使您没有升级该模块,这也可能是任何模块的错字。

从代码库中删除的模块

您可能希望首先检查@Berdir提到的已知错误,尤其是如果您最近正在从代码库中删除“未使用”的模块时,尤其如此。要查明是否有已启用但已从文件系统中删除的模块,可以运行一个脚本(例如此处提到的脚本)-或使用为在带有drush的系统上进行多站点安装而编写的我的脚本来运行从Drupal基本目录:

find sites -maxdepth 1 -iname '*.*' -type d | sed -rne 's:sites/(.+):echo \1; drush @\1 sqlq "select filename from system where status = 1" | grep "/" | sed -rne "s_(.+)_test -f \\1 || echo \\1_p" | bash:p' | bash

或以下内容:

while read -r file; do [ -f "$file" ] || echo "$file is missing."; done < <(drush sqlq "SELECT filename FROM system WHERE status = 1")

如果找到已从代码库中删除的模块,请按照@Berdir提到的问题中的说明进行操作。

编码错误

如果不是那样,您的情况很可能是由编码错误引起的,例如文件已被删除但仍通过drupal_add_js调用(来自问题#1082892的注释19)添加,或者模块或主题中的拼写错误,例如imagecache_actions(请参阅https://drupal.org/node/2381357)。

无论如何,要弄清楚为什么会这样,您需要确切知道Drupal找不到哪个文件。因此,根据Berdir的评论,您可以drupal_get_filenamebootstrap.inc调用之前添加日志或消息调用来暂时破解drupal_system_listing()。如果您已安装Devel模块,则dpm可以使用;如果没有,则可以使用drupal_set_message或syslog。例子:

dpm(func_get_args());
drupal_set_message(implode(', ', func_get_args()));
syslog(LOG_WARNING, implode(', ', func_get_args()));

一旦知道了Drupal的需求,就可以确定从那里去哪儿了。我的问题是由调用包含不存在的模块中的文件引起的imagcache_actions(请注意拼写错误)。因此,我imagecache_actions在我的代码库中进行了搜索(例如grep -r imagcache_actions .),发现1.4版本的版本imagecache_canvasactions.module在任何函数调用之外都在文件范围内使用错字来使用module_load_include。同样,仅在更新到Drupal 7.33+之后才暴露此错误。我发现已经为创建了一个问题imagecache_actions,应用了该补丁程序,然后又恢复了正常工作。


2

我有一个非常类似的问题- file_scan_directory()正在杀死该网站。事实证明,node_modules嵌入到我的自定义主题中的huuge 文件夹在gulp每次刷新缓存时都会被扫描。将这些文件移出主题文件夹(并更新我的gulpfile中的某些路径)似乎已为我解决了。或者:我认为您可以破解file.inc

'nomask' => '/(\.\.?|CVS|node_modules)$/', // https://www.drupal.org/node/2329453#comment-9360519


0

file_scan_directory()是所有的文件匹配一个给定的目录,递归函数。它的使用is_dir()opendir()PHP调用就I / O系统调用而言可能是最耗时的。简单的Drupal引导程序(例如time drush ev "")可能调用file_scan_directory数千次(具体取决于您Drupal文件夹层次结构的复杂性,例如模块及其文件夹的数量)。

在我的情况下,我有约1500次调用file_scan_directory(总共24秒,其中包含来自drupal_system_listingin的2次调用common.inc,然后其他调用被递归调用file_scan_directory自身分解)。

为了提高I / O调用的性能,您需要实现文件缓存。这可以通过安装和启用OPCache(opcache.enable=1)并对其设置进行调整来实现(请参阅:如何使用PHP OPCache?)。还建议使用基于内存的缓存,例如memcached / redis。

使用命令行界面(例如drush)时,还应该启用opcache.enable_cli=1

更改之后,您可以使用一些可用的调试器检查更耗时的系统调用。

例如

  • 在Linux上使用strace(按Ctrl- C完成):

    sudo strace -c -fp $(pgrep -n php)
  • 在Unix上使用dtrace(使用PHP的DTrace静态探针),例如

    sudo dtrace -n 'inline string NAME = "php"; syscall:::entry /(NAME == strstr(NAME, execname)) || (execname == strstr(execname, NAME))/ { @num[probefunc] = count(); }'

您可以进一步考虑优化drupal_system_listing()file_scan_directory()通过实现静态缓存,例如

--- a/includes/file.inc
+++ b/includes/file.inc
@@ -2104,6 +2104,8 @@ function file_download_access($uri) {
  *   'filename', and 'name' members corresponding to the matching files.
  */
 function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
+  static $dirs = array();
+
   // Merge in defaults.
   $options += array(
     'nomask' => '/(\.\.?|CVS)$/',
@@ -2120,7 +2122,12 @@ function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
       if (!preg_match($options['nomask'], $filename) && $filename[0] != '.') {
         $uri = "$dir/$filename";
         $uri = file_stream_wrapper_uri_normalize($uri);
-        if (is_dir($uri) && $options['recurse']) {
+
+        if (empty($dirs[$uri])) {
+          $dirs[$uri] = is_dir($uri);
+        }
+
+        if ($dirs[$uri] && $options['recurse']) {
           // Give priority to files in this folder by merging them in after any subdirectory files.
           $files = array_merge(file_scan_directory($uri, $mask, $options, $depth + 1), $files);

或者,要缓存file_scan_directory来自的调用drupal_system_listing(),请检查以下修补程序:file_scan_directory应该被缓存

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.