Za pomocą xhprof zauważyłem, że wykonanie file_scan_directory()
zajmuje więcej niż 10 sekund po załadowaniu pierwszej strony. Dlaczego miałoby to trwać tak długo?
To jest wynik działania xhprofile:
Za pomocą xhprof zauważyłem, że wykonanie file_scan_directory()
zajmuje więcej niż 10 sekund po załadowaniu pierwszej strony. Dlaczego miałoby to trwać tak długo?
To jest wynik działania xhprofile:
Odpowiedzi:
Wygląda na to, że znany ci problem z Drupal 7 dotyczy Ciebie .
Najprawdopodobniej uderzasz Unikaj ponownego skanowania katalogu modułów, gdy brakuje wielu modułów . Dzieje się tak, jeśli brakuje niektórych modułów w instalacji. Spróbuj sprawdzić tabelę systemową:
SELECT name, filename FROM system WHERE type = 'module' AND status = 1 ORDER BY filename
I oczyść wszystkie moduły, które są nadal włączone, ale brakuje ich w systemie plików.
Ogólnie rzecz biorąc, Drupal 7 jest znacznie bardziej przyjazny dla zasobów i skalowalny niż Drupal 6, poza kilkoma niefortunnymi regresjami takimi jak ten.
Patrząc na te funkcje, wygląda na to, że brakuje modułu lub może pojedynczego pliku modułu. Spójrz na drupal_get_filename () , wywołuje on drupal_system_listing (), który wywołuje tę funkcję, jeśli nie może znaleźć żądanego pliku. Dodaj dpm (func_get_args ()) tuż przed wywołaniem drupal_system_listing (), co powinno ci powiedzieć, którego pliku nie znajduje.
Jest kilka powodów, dla których ten problem może powstać, i ku mojemu wielkiemu przerażeniu, teraz mam dość wiedzy na temat tych przyczyn. Frustrujące jest to, że jeśli właśnie zauważyłeś ten problem po aktualizacji rdzenia Drupala do wersji 7.33+, może to być literówka w dowolnym module, nawet jeśli nie zaktualizowałeś tego modułu.
Możesz najpierw sprawdzić znany błąd, o którym wspomina @Berdir, szczególnie jeśli ostatnio usuwasz „nieużywane” moduły z bazy kodu. Aby dowiedzieć się, czy masz moduły, które są włączone, ale zostały usunięte z systemu plików, możesz uruchomić skrypt, taki jak wymieniony tutaj - lub użyć mojego, napisanego do instalacji w wielu witrynach w systemie z Drush, aby uruchomić z katalogu podstawowego 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
lub następujące:
while read -r file; do [ -f "$file" ] || echo "$file is missing."; done < <(drush sqlq "SELECT filename FROM system WHERE status = 1")
Jeśli znajdziesz moduł, który został usunięty z bazy kodu, postępuj zgodnie ze wskazówkami w kwestiach wspomnianych przez @Berdir.
Jeśli tak nie jest, Twoja sytuacja jest prawdopodobnie spowodowana błędem kodowania, takim jak plik, który został usunięty, ale nadal jest dodawany przez wywołanie drupal_add_js (od komentarza 19 w numerze # 1082892) lub niefortunną literówkę w module lub motywie , np. imagecache_actions
(patrz https://drupal.org/node/2381357 ).
W każdym razie, aby dokładnie zrozumieć, dlaczego tak się dzieje, musisz dokładnie wiedzieć, którego pliku Drupal nie może znaleźć. Tak więc, zgodnie z komentarzem Berdir użytkownika, można tymczasowo włamać drupal_get_filename
się bootstrap.inc
przez dodanie do dziennika lub wiadomość wezwanie tuż przed wywołaniem drupal_system_listing()
. Jeśli masz zainstalowany moduł Devel, to dpm
będzie działał; jeśli nie, możesz użyć drupal_set_message
lub syslog. Przykłady:
dpm(func_get_args());
drupal_set_message(implode(', ', func_get_args()));
syslog(LOG_WARNING, implode(', ', func_get_args()));
Gdy dowiesz się, czego szuka Drupal, dobrze jest założyć, że będziesz w stanie dowiedzieć się, dokąd się udać. Mój problem spowodowany był wywołaniem dołączenia pliku z nieistniejącego modułu imagcache_actions
(zwróć uwagę na literówkę). Tak więc szukałem imagecache_actions
w mojej bazie kodu (np. grep -r imagcache_actions .
) I znalazłem, że wersja 1.4 imagecache_canvasactions.module
używa modułu moduł_ładuj_włączenie poza jakimkolwiek wywołaniem funkcji, w zakresie plików, z literówką. Ponownie ten błąd został ujawniony dopiero po aktualizacji do Drupal 7.33+. Odkryłem, że problem został już utworzony imagecache_actions
, zastosowałem łatkę i wróciłem do pracy.
Miałem bardzo podobny problem - file_scan_directory()
zabijałem witrynę. Okazuje się, że ogromny node_modules
folder osadzony w moim niestandardowym motywie gulp
był skanowany przy każdym opróżnieniu pamięci podręcznej. Przeniesienie tych plików z folderu motywu (i zaktualizowanie niektórych ścieżek w moim pliku gulp) wydawało mi się to naprawić. Alternatywnie: Myślę, że możesz zhakować file.inc
:
'nomask' => '/(\.\.?|CVS|node_modules)$/', // https://www.drupal.org/node/2329453#comment-9360519
Jest file_scan_directory()
to funkcja rekurencyjna, która wszystkie pliki pasujące do danego katalogu. Wykorzystuje is_dir()
i opendir()
wywołuje PHP, które mogą być najdroższe pod względem wywołań systemowych We / Wy. Prosty bootstrap Drupal (np. time drush ev ""
) Może wywoływać file_scan_directory
kilka tysięcy razy (w zależności od złożoności hierarchii folderów Drupal, np. Liczby modułów i folderów).
W moim przypadku miałem ~ 1500 wywołań file_scan_directory
(24 sekund w sumie składających się z 2 połączeń drupal_system_listing
w common.inc
, a następnie pozostałe połączenia były podzielone przez rekurencyjnych wywołań file_scan_directory
to-ja.
Aby poprawić wydajność połączeń We / Wy, należy zaimplementować buforowanie plików. Można to osiągnąć, instalując i włączając OPCache ( opcache.enable=1
) oraz dostosowując jego ustawienia (patrz: Jak korzystać z OPCache PHP? ). Zalecane jest również stosowanie buforowania opartego na pamięci, takiego jak memcached / redis.
Korzystając z interfejsu wiersza polecenia (takiego jak drush
), należy również włączyć opcache.enable_cli=1
.
Po zmianie możesz sprawdzić bardziej wymagające połączenia systemowe za pomocą dostępnych debuggerów.
Na przykład
W systemie Linux przy użyciu strace
(naciśnij Ctrl- Caby zakończyć):
sudo strace -c -fp $(pgrep -n php)
W systemie Unix dtrace
(za pomocą statycznych sond DTrace PHP ), np
sudo dtrace -n 'inline string NAME = "php"; syscall:::entry /(NAME == strstr(NAME, execname)) || (execname == strstr(execname, NAME))/ { @num[probefunc] = count(); }'
Możesz dalej rozważyć optymalizację drupal_system_listing()
lub file_scan_directory()
wdrożenie statycznej pamięci podręcznej, np
--- 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);
Lub w celu buforowania file_scan_directory
wywołań z drupal_system_listing()
, a następnie sprawdź następującą łatkę dostępną pod adresem: katalog_kanału_pliku powinien być buforowany .