Используя xhprof, я заметил, что file_scan_directory()
при загрузке главной страницы требуется более 10 секунд. Почему это займет так много времени?
Это вывод xhprofile:
Используя xhprof, я заметил, что file_scan_directory()
при загрузке главной страницы требуется более 10 секунд. Почему это займет так много времени?
Это вывод xhprofile:
Ответы:
Похоже, вы столкнулись с известной проблемой в 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 (), который должен сообщить вам, какой файл он не находит.
Есть несколько причин, по которым эта проблема может возникнуть, и, к моему великому разочарованию, я теперь немного разбираюсь в этих причинах. К сожалению, если вы только что заметили эту проблему после обновления ядра 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
, применила исправление и вернулась к работе.
У меня была очень похожая проблема - file_scan_directory()
убивал сайт. Оказывается, огромная node_modules
папка, встроенная в мою пользовательскую тему, проверялась при очистке gulp
кеша. Перемещение этих файлов из папки темы (и обновление некоторых путей в моем gulpfile), казалось, исправило это для меня. Альтернативно: я думаю, что вы можете взломать file.inc
:
'nomask' => '/(\.\.?|CVS|node_modules)$/', // https://www.drupal.org/node/2329453#comment-9360519
Это 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 должен быть кэширован .