file_scan_directory () занимает около 10 секунд для выполнения

10

Используя xhprof, я заметил, что file_scan_directory()при загрузке главной страницы требуется более 10 секунд. Почему это займет так много времени?

Это вывод xhprofile:

Скриншот

hknik
источник
Вы не можете "file_scan_directory" на "первой странице", так как первая страница - это запись в таблице базы данных, а не путь к файловой системе.
Летарион
@ Летарион Я думаю, ты неправильно понял мой вопрос. Я имею в виду время, которое занимает эта функция, когда загружается первая страница. Я редактировал вопрос.
hknik
Действительно ли главная страница имеет какое-либо отношение к функции, занимающей определенное количество времени? Какой каталог вы на самом деле сканируете? Что в каталоге?
Летарион
Ага! Я думал, что вы вызвали функцию самостоятельно, и удивился, почему вы не предоставили больше подробностей. Ответ Бердира выглядит очень разумным. :)
Летарион

Ответы:

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 (), который вызывает эту функцию, если не может найти запрошенный файл. Добавьте dpm (func_get_args ()) прямо перед вызовом drupal_system_listing (), который должен сообщить вам, какой файл он не находит.

Berdir
источник
К сожалению (!) В файловой системе нет ни одного модуля
hknik
Затем вам нужно проследить, откуда поступил вызов, может быть, пользовательский модуль или модуль Contrib делает что-то неправильно Щелкните по родительским функциям file_scan_directory () и обновите исходный пост списком родительских функций.
Бердир
Глядя на эти функции, это действительно похоже , что это не хватает модуля или , может быть , один файл модуля. Посмотрите на drupal_get_filename: api.drupal.org/api/drupal/includes!bootstrap.inc/function/… . Он вызывает функцию, если не может найти запрошенный файл. Добавьте dpm (func_get_args ()) прямо перед вызовом drupal_system_listing (), который должен сказать вам, какую функцию он не находит.
Бердир
@Berdir Ваш последний комментарий должен быть в вашем ответе, так как он актуален.
kiamlaluno
Ссылки на «известные проблемы в Drupal 7» и «Избегать повторного сканирования каталога модулей» не работают. Оба являются прежними ответами на стек. У кого-нибудь есть другие ссылки?
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 (из комментария 19 в выпуске №1082892) или неудачной опечаткой в ​​модуле или теме Например imagecache_actions(см. https://drupal.org/node/2381357 ).

В любом случае, чтобы точно выяснить, почему это происходит, вам нужно точно знать, какой файл Drupal не может найти. Таким образом, в соответствии с комментарием Berdir, вы можете временно взломать drupal_get_filenameв bootstrap.incпутем добавления вызова журнала или сообщение непосредственно перед вызовом drupal_system_listing(). Если у вас установлен модуль Devel, то он dpmбудет работать; если нет, вы можете использовать drupal_set_messageили системный журнал. Примеры:

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папка, встроенная в мою пользовательскую тему, проверялась при очистке gulpкеша. Перемещение этих файлов из папки темы (и обновление некоторых путей в моем gulpfile), казалось, исправило это для меня. Альтернативно: я думаю, что вы можете взломать file.inc:

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

williamsowen
источник
0

Это file_scan_directory()рекурсивная функция для всех файлов, соответствующих данному каталогу. Он использует is_dir()и opendir()вызовы PHP, которые могут быть наиболее дорогостоящими с точки зрения системных вызовов ввода / вывода. Простой загрузчик Drupal (например time drush ev "") может вызывать file_scan_directoryнесколько тысяч раз (в зависимости от сложности вашей иерархии папок Drupal, например, количества модулей и их папок).

В моем случае у меня было ~ 1500 вызовов file_scan_directory(всего 24 секунды, состоящие из 2 вызовов от drupal_system_listingвхода common.inc, затем остальные вызовы были разделены на рекурсивные вызовы к file_scan_directoryсебе).

Чтобы повысить производительность при вызовах ввода / вывода, вам необходимо реализовать кэширование файлов. Это может быть достигнуто путем установки и включения 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 должен быть кэширован .

kenorb
источник