Die Ausführung von file_scan_directory () dauert ca. 10 Sekunden

10

Bei Verwendung von xhprof ist mir aufgefallen, dass file_scan_directory()die Ausführung beim Laden der Startseite mehr als 10 Sekunden dauert. Warum sollte es so lange dauern?

Dies ist die Ausgabe von xhprofile:

Bildschirmfoto

hknik
quelle
Sie können die "Startseite" nicht "file_scan_directory" verwenden, da die Startseite ein Eintrag in einer Datenbanktabelle und kein Dateisystempfad ist.
Letharion
@ Letharion Ich denke du hast meine Frage falsch verstanden. Ich meine die Zeit, die diese Funktion benötigt, wenn die Startseite geladen wird. Ich habe die Frage bearbeitet.
Hknik
Hat die Titelseite wirklich etwas damit zu tun, dass die Funktion eine bestimmte Zeit in Anspruch nimmt? Welches Verzeichnis scannen Sie tatsächlich? Was ist im Verzeichnis?
Letharion
Aha! Ich dachte, Sie hätten die Funktion selbst aufgerufen und mich gefragt, warum Sie nicht mehr Details angegeben haben. Berdirs Antwort sieht sehr vernünftig aus. :)
Letharion

Antworten:

14

Es hört sich so an, als wären Sie von einem bekannten Problem in Drupal 7 betroffen .

Höchstwahrscheinlich treffen Sie auf Vermeiden Sie das erneute Scannen des Modulverzeichnisses, wenn mehrere Module fehlen . Es passiert, wenn in Ihrer Installation einige Module fehlen. Versuchen Sie, Ihre Systemtabelle zu überprüfen:

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

Bereinigen Sie alle Module, die noch aktiviert sind, aber im Dateisystem fehlen.

Insgesamt ist Drupal 7 viel ressourcenschonender und skalierbarer als Drupal 6, abgesehen von einigen unglücklichen Regressionen wie dieser.

Wenn man sich diese Funktionen ansieht, sieht es so aus, als würde ein Modul oder eine einzelne Datei eines Moduls fehlen. Werfen Sie einen Blick auf drupal_get_filename () , es ruft das drupal_system_listing () auf, das diese Funktion aufruft, wenn es die angeforderte Datei nicht finden kann. Fügen Sie eine dpm (func_get_args ()) hinzu, bevor drupal_system_listing () aufgerufen wird. Diese sollte Ihnen mitteilen, welche Datei nicht gefunden wird.

Berdir
quelle
Nein. Leider (!)
Fehlt
Dann müssen Sie nachverfolgen, woher der Anruf kommt. Möglicherweise macht ein benutzerdefiniertes Modul oder ein Contrib-Modul etwas falsch. Klicken Sie sich durch die übergeordneten Funktionen von file_scan_directory () und aktualisieren Sie den ersten Beitrag mit der Liste der übergeordneten Funktionen.
Berdir
Wenn man sich diese Funktionen ansieht, sieht es so aus, als würde ein Modul oder eine einzelne Datei eines Moduls fehlen. Schauen Sie sich drupal_get_filename an: api.drupal.org/api/drupal/includes!bootstrap.inc/function/… . Die Funktion wird aufgerufen, wenn die angeforderte Datei nicht gefunden werden kann. Fügen Sie kurz vor dem Aufruf von drupal_system_listing () eine dpm (func_get_args ()) hinzu, die Ihnen mitteilen soll, welche Funktion nicht gefunden wird.
Berdir
@Berdir Dein letzter Kommentar sollte in deiner Antwort sein, da er relevant ist.
Kiamlaluno
Die Links zu "Bekanntes Problem in Drupal 7" und "Vermeiden Sie das erneute Scannen des Modulverzeichnisses" sind fehlerhaft. Beides sind frühere Antworten zum Stapelaustausch. Hat jemand andere Referenzen?
Rfay
4

Es gibt mehrere Gründe, warum dieses Problem auftreten kann, und zu meiner großen Bestürzung bin ich jetzt etwas über diese Gründe informiert. Wenn Sie dieses Problem nach dem Upgrade von Drupal Core auf 7.33+ gerade bemerkt haben, kann dies frustrierend sein, dass dies in jedem Modul ein Tippfehler sein kann, selbst wenn Sie dieses Modul nicht aktualisiert haben.

Module aus der Codebasis entfernt

Möglicherweise möchten Sie zuerst nach dem bekannten Fehler suchen, den @Berdir erwähnt, insbesondere wenn Sie kürzlich "nicht verwendete" Module aus der Codebasis entfernt haben. Um herauszufinden, ob Module aktiviert sind, aber aus dem Dateisystem entfernt wurden, können Sie ein Skript wie das hier erwähnte ausführen oder mein Skript verwenden, das für eine Installation mit mehreren Standorten auf einem System mit Drush geschrieben wurde aus dem Drupal-Basisverzeichnis:

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

oder Folgendes:

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

Wenn Sie ein Modul finden, das aus der Codebasis entfernt wurde, befolgen Sie die Anweisungen in den von @Berdir genannten Problemen.

Codierungsfehler

Wenn dies nicht der Fall ist, wird Ihre Situation wahrscheinlich durch einen Codierungsfehler verursacht, z. B. durch eine Datei, die entfernt wurde, aber noch durch einen Aufruf von drupal_add_js (aus Kommentar 19 in Ausgabe # 1082892) oder einen unglücklichen Tippfehler in einem Modul oder Thema hinzugefügt wird zB imagecache_actions(siehe https://drupal.org/node/2381357 ).

Um genau herauszufinden, warum dies geschieht, müssen Sie in jedem Fall genau wissen, welche Datei Drupal nicht finden kann. So, wie pro Berdir Kommentar, können Sie vorübergehend hacken drupal_get_filenamein bootstrap.incdurch ein Protokoll oder eine Nachricht Gespräch hinzugefügt wird kurz vor dem Aufruf drupal_system_listing(). Wenn Sie das Devel-Modul installiert dpmhaben, funktioniert es. Wenn nicht, können Sie drupal_set_messageoder syslog verwenden. Beispiele:

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

Sobald Sie wissen, wonach Drupal sucht, ist es eine gute Wette, dass Sie herausfinden können, wohin Sie von dort aus gehen müssen. Mein Problem wurde durch einen Aufruf verursacht, eine Datei aus dem nicht vorhandenen Modul aufzunehmen imagcache_actions(Tippfehler beachten). Also habe ich imagecache_actionsin meiner Codebasis gesucht (z. B. grep -r imagcache_actions .) und festgestellt, dass Version 1.4 von imagecache_canvasactions.modulemodule_load_include außerhalb eines Funktionsaufrufs im Dateibereich mit einem Tippfehler verwendet. Auch dieser Fehler wurde erst nach dem Update auf Drupal 7.33+ angezeigt. Ich stellte fest, dass bereits ein Problem für erstellt wurde imagecache_actions, wendete den Patch an und war wieder im Geschäft.

David Hunt
quelle
2

Ich hatte ein sehr ähnliches Problem - ich habe file_scan_directory()die Seite getötet. Es stellte sich heraus, dass ein riesiger node_modulesOrdner, der in mein benutzerdefiniertes Design eingebettet gulpwar, bei jedem Cache-Flush gescannt wurde. Das Verschieben dieser Dateien aus dem Themenordner (und das Aktualisieren einiger Pfade in meiner Gulpfile) schien das Problem für mich zu beheben. Alternativ: Ich denke du kannst hacken file.inc:

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

williamsowen
quelle
0

Dies file_scan_directory()ist eine rekursive Funktion, die alle Dateien enthält, die für ein bestimmtes Verzeichnis übereinstimmen. Es sind Verwendungen is_dir()und opendir()PHP-Aufrufe, die im Hinblick auf E / A-Systemaufrufe am zeitaufwändigsten sind. Einfacher Drupal-Bootstrap (z. B. time drush ev "") kann file_scan_directoryeinige tausend Mal aufgerufen werden (abhängig von der Komplexität Ihrer Drupal-Ordnerhierarchie, z. B. Anzahl der Module und ihrer Ordner).

In meinem Fall hatte ich ~ 1500 Anrufe an file_scan_directory(insgesamt 24 Sekunden, bestehend aus 2 Anrufen von drupal_system_listingin common.inc, dann wurden die anderen Anrufe durch rekursive Anrufe an sich file_scan_directoryselbst aufgeteilt.

Um die Leistung bei E / A-Aufrufen zu verbessern, müssen Sie das Datei-Caching implementieren. Dies kann erreicht werden, indem OPCache ( opcache.enable=1) installiert und aktiviert und seine Einstellungen angepasst werden (siehe: Verwendung von PHP OPCache? ). Es wird auch empfohlen, speicherbasiertes Caching wie memcached / redis zu verwenden.

Wenn Sie eine Befehlszeilenschnittstelle (z. B. drush) verwenden, sollten Sie diese auch aktivieren opcache.enable_cli=1.

Nach der Änderung können Sie die aufwendigeren Systemaufrufe mit einigen verfügbaren Debuggern überprüfen.

Z.B

  • Unter Linux mit strace(hit Ctrl- Cto finish):

    sudo strace -c -fp $(pgrep -n php)
  • Unter Unix dtrace(unter Verwendung der statischen DTrace-Sonden von PHP ), z

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

Sie können die Optimierung drupal_system_listing()oder file_scan_directory()Implementierung eines statischen Caches in Betracht ziehen , z

--- 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);

Oder für das Caching von file_scan_directoryAnrufen aus drupal_system_listing(), dann überprüfen Sie den folgenden Patch verfügbar unter: file_scan_directory zwischengespeichert werden sollen .

Kenorb
quelle