Usando xhprof ho notato che ci file_scan_directory()
vogliono più di 10 secondi per eseguire il caricamento della prima pagina. Perché dovrebbe impiegare così tanto tempo?
Questo è l'output di xhprofile:
Usando xhprof ho notato che ci file_scan_directory()
vogliono più di 10 secondi per eseguire il caricamento della prima pagina. Perché dovrebbe impiegare così tanto tempo?
Questo è l'output di xhprofile:
Risposte:
Sembra che tu sia interessato da un problema noto in Drupal 7 .
Molto probabilmente, stai colpendo Evita di ripetere la scansione della directory dei moduli quando mancano più moduli . Succede se hai alcuni moduli mancanti nella tua installazione. Prova a controllare la tabella di sistema:
SELECT name, filename FROM system WHERE type = 'module' AND status = 1 ORDER BY filename
E ripulisci tutti i moduli che sono ancora abilitati ma mancanti dal filesystem.
Nel complesso, Drupal 7 è molto più intuitivo e scalabile rispetto a Drupal 6, oltre ad alcune sfortunate regressioni come questa.
Guardando quelle funzioni, sembra che manchi un modulo o forse un singolo file di un modulo. Dai un'occhiata a drupal_get_filename () , chiama drupal_system_listing (), che chiama questa funzione se non riesce a trovare il file richiesto. Aggiungi un dpm (func_get_args ()) prima di chiamare drupal_system_listing (), che dovrebbe dirti quale file non sta trovando.
Ci sono diverse ragioni per cui questo problema può sorgere e, con mio grande sgomento, ora mi trovo in qualche modo ben informato su tali motivi. Frustrantemente, se hai appena notato questo problema dopo aver aggiornato Drupal core a 7.33+, questo potrebbe essere un errore di battitura in qualsiasi modulo, anche se non hai aggiornato quel modulo.
Potresti prima verificare il bug noto menzionato da @Berdir, specialmente se hai rimosso di recente moduli "non utilizzati" dalla base di codice. Per scoprire se hai moduli abilitati ma che sono stati rimossi dal file system puoi eseguire uno script come quello menzionato qui - o usare il mio, scritto per un'installazione multi-sito su un sistema con drush, da eseguire dalla directory di base 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
o il seguente:
while read -r file; do [ -f "$file" ] || echo "$file is missing."; done < <(drush sqlq "SELECT filename FROM system WHERE status = 1")
Se trovi un modulo che è stato rimosso dalla base di codice, segui le istruzioni nei problemi menzionati da @Berdir.
In caso contrario, la tua situazione è probabilmente causata da un errore di codifica, ad esempio un file che è stato rimosso ma che viene ancora aggiunto da una chiamata drupal_add_js (dal commento 19 nel numero # 1082892) o da un errore di battitura sfortunato in un modulo o tema , ad esempio imagecache_actions
(vedi https://drupal.org/node/2381357 ).
In ogni caso, per capire esattamente perché ciò sta accadendo, devi sapere esattamente quale file Drupal non riesce a trovare. Così, secondo il commento di Berdir, si può temporaneamente incidere drupal_get_filename
in bootstrap.inc
con l'aggiunta di una chiamata o un messaggio di log appena prima della chiamata a drupal_system_listing()
. Se hai installato il modulo Develop, allora dpm
funzionerà; in caso contrario, è possibile utilizzare drupal_set_message
o syslog. Esempi:
dpm(func_get_args());
drupal_set_message(implode(', ', func_get_args()));
syslog(LOG_WARNING, implode(', ', func_get_args()));
Una volta che sai cosa Drupal sta cercando, è una buona scommessa che sarai in grado di capire dove andare da lì. Il mio problema è stato causato da una chiamata per includere un file dal modulo inesistente imagcache_actions
(notare l'errore di battitura). Quindi, ho cercato imagecache_actions
nel mio codebase (ad esempio grep -r imagcache_actions .
) e ho scoperto che la versione 1.4 imagecache_canvasactions.module
utilizza module_load_include al di fuori di qualsiasi chiamata di funzione, nell'ambito del file, con un refuso. Ancora una volta, questo errore è stato esposto solo dopo l'aggiornamento a Drupal 7.33+. Ho scoperto che era già stato creato un problema imagecache_actions
, applicato la patch ed era tornato in attività.
Ho avuto un problema molto simile: file_scan_directory()
stavo uccidendo il sito. Si scopre che una node_modules
cartella enorme incorporata nel mio tema personalizzato per cui gulp
è stata scansionata ogni cache flush. Spostare questi file fuori dalla cartella del tema (e aggiornare alcuni percorsi nel mio gulpfile) sembrava risolverlo per me. In alternativa: penso che tu possa hackerare file.inc
:
'nomask' => '/(\.\.?|CVS|node_modules)$/', // https://www.drupal.org/node/2329453#comment-9360519
La file_scan_directory()
è una funzione ricorsiva che tutti i file che corrispondono a una determinata directory. Sono gli usi is_dir()
e le opendir()
chiamate PHP che possono essere più costosi in termini di chiamate di sistema I / O. Il bootstrap Drupal semplice (ad es. time drush ev ""
) Può chiamare file_scan_directory
alcune migliaia di volte (a seconda della complessità della gerarchia delle cartelle di Drupal, ad es. Il numero di moduli e le sue cartelle).
Nel mio caso ho avuto ~ 1500 chiamate a file_scan_directory
(24 secondi in totale consistenti in 2 chiamate da drupal_system_listing
dentro common.inc
, quindi le altre chiamate sono state divise da chiamate ricorsive a file_scan_directory
se stesso.
Per migliorare le prestazioni sulle chiamate I / O, è necessario implementare la memorizzazione nella cache dei file. Ciò può essere ottenuto installando e abilitando OPCache ( opcache.enable=1
) e modificandone le impostazioni (vedi: Come usare PHP OPCache? ). Si consiglia inoltre di utilizzare la cache basata sulla memoria come memcached / redis.
Quando si utilizza l'interfaccia della riga di comando (come drush
), è necessario abilitare anche opcache.enable_cli=1
.
Dopo la modifica è possibile controllare i syscall più consumati utilizzando alcuni debugger disponibili.
Per esempio
Su Linux usando strace
(premi Ctrl- Cper finire):
sudo strace -c -fp $(pgrep -n php)
Su Unix usando dtrace
(usando le sonde statiche DTrace di PHP ), ad es
sudo dtrace -n 'inline string NAME = "php"; syscall:::entry /(NAME == strstr(NAME, execname)) || (execname == strstr(execname, NAME))/ { @num[probefunc] = count(); }'
È inoltre possibile prendere in considerazione l'ottimizzazione drupal_system_listing()
o l' file_scan_directory()
implementazione della cache statica, ad es
--- 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);
O per la memorizzazione nella cache delle file_scan_directory
chiamate da drupal_system_listing()
, quindi controllare la seguente patch disponibile su: file_scan_directory deve essere memorizzata nella cache .