Sử dụng xhprof tôi nhận thấy rằng file_scan_directory()
phải mất hơn 10 giây để thực thi khi tải trang trước. Tại sao phải mất một thời gian dài như vậy?
Đây là đầu ra của xhprofile:
Sử dụng xhprof tôi nhận thấy rằng file_scan_directory()
phải mất hơn 10 giây để thực thi khi tải trang trước. Tại sao phải mất một thời gian dài như vậy?
Đây là đầu ra của xhprofile:
Câu trả lời:
Có vẻ như bạn bị ảnh hưởng bởi một vấn đề đã biết trong Drupal 7 .
Rất có thể, bạn đang nhấn Tránh quét lại thư mục mô-đun khi thiếu nhiều mô-đun . Nó xảy ra nếu bạn có một số mô-đun bị thiếu trong cài đặt của bạn. Hãy thử kiểm tra bảng hệ thống của bạn:
SELECT name, filename FROM system WHERE type = 'module' AND status = 1 ORDER BY filename
Và dọn sạch mọi mô-đun vẫn được bật nhưng thiếu trong hệ thống tập tin.
Nhìn chung, Drupal 7 thân thiện với tài nguyên hơn và có thể mở rộng hơn so với Drupal 6, ngoài một số hồi quy không may như thế này.
Nhìn vào các chức năng đó, có vẻ như nó thiếu một mô-đun hoặc có thể là một tệp duy nhất của mô-đun. Hãy xem drupal_get_filename () , nó gọi drupal_system_listing (), gọi hàm này nếu nó không thể tìm thấy tệp được yêu cầu. Thêm một dpm (func_get_args ()) ngay trước khi nó gọi drupal_system_listing (), nó sẽ cho bạn biết tập tin nào nó không tìm thấy.
Có một số lý do tại sao vấn đề này có thể phát sinh, và với sự thất vọng lớn của tôi, bây giờ tôi thấy mình có phần hiểu biết về những lý do đó. Thật khó chịu, nếu bạn vừa nhận thấy vấn đề này sau khi nâng cấp lõi Drupal lên 7.33+, đây có thể là một lỗi đánh máy trong bất kỳ mô-đun nào, ngay cả khi bạn chưa nâng cấp mô-đun đó.
Trước tiên, bạn có thể muốn kiểm tra lỗi đã biết mà @Berdir đề cập, đặc biệt nếu gần đây bạn đã xóa các mô-đun "không sử dụng" khỏi cơ sở mã. Để tìm hiểu xem bạn có các mô-đun được kích hoạt nhưng đã bị xóa khỏi hệ thống tệp, bạn có thể chạy một tập lệnh như tập lệnh được đề cập ở đây - hoặc sử dụng tập lệnh của tôi, được viết cho cài đặt nhiều trang trên hệ thống có drush, để chạy từ thư mục cơ sở 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
hoặc như sau:
while read -r file; do [ -f "$file" ] || echo "$file is missing."; done < <(drush sqlq "SELECT filename FROM system WHERE status = 1")
Nếu bạn tìm thấy một mô-đun đã bị xóa khỏi cơ sở mã, hãy làm theo các hướng dẫn trong các vấn đề mà @Berdir đã đề cập.
Nếu không phải như vậy, tình huống của bạn có thể do lỗi mã hóa, chẳng hạn như tệp đã bị xóa nhưng vẫn đang được thêm bởi một cuộc gọi drupal_add_js (từ nhận xét 19 trong số # 1082892) hoặc lỗi chính tả trong mô-đun hoặc chủ đề không may , ví dụ imagecache_actions
(xem https://drupal.org/node/2381357 ).
Trong mọi trường hợp, để tìm hiểu chính xác lý do tại sao điều này xảy ra, bạn cần biết chính xác tập tin mà Drupal không thể tìm thấy. Vì vậy, theo bình luận Berdir, bạn có thể tạm Hack drupal_get_filename
trong bootstrap.inc
bằng cách thêm một bản ghi hoặc tin nhắn cuộc gọi ngay trước khi cuộc gọi đến drupal_system_listing()
. Nếu bạn đã cài đặt mô-đun Devel thì dpm
sẽ hoạt động; nếu không, bạn có thể sử dụng drupal_set_message
hoặc syslog. Ví dụ:
dpm(func_get_args());
drupal_set_message(implode(', ', func_get_args()));
syslog(LOG_WARNING, implode(', ', func_get_args()));
Một khi bạn biết Drupal đang tìm kiếm điều gì, đó là một sự đánh cược tốt mà bạn sẽ có thể tìm ra nơi sẽ đi từ đó. Vấn đề của tôi là do một cuộc gọi bao gồm một tệp từ mô-đun không tồn tại imagcache_actions
(lưu ý lỗi chính tả). Vì vậy, tôi đã tìm kiếm imagecache_actions
trong cơ sở mã của mình (ví dụ grep -r imagcache_actions .
) và thấy rằng phiên bản 1.4 imagecache_canvasactions.module
sử dụng module_load_include bên ngoài bất kỳ lệnh gọi hàm nào, trong phạm vi tệp, với một lỗi đánh máy. Một lần nữa, lỗi này chỉ được phơi bày sau khi cập nhật lên Drupal 7.33+. Tôi thấy rằng một vấn đề đã được tạo ra imagecache_actions
, áp dụng bản vá và đã hoạt động trở lại.
Tôi đã có một vấn đề rất giống nhau - file_scan_directory()
đã giết chết trang web. Hóa ra một node_modules
thư mục huuge được nhúng trong chủ đề tùy chỉnh của tôi gulp
đã được quét mỗi lần xóa bộ đệm. Di chuyển các tệp này ra khỏi thư mục chủ đề (và cập nhật một số đường dẫn trong gulpfile của tôi) dường như để sửa nó cho tôi. Ngoài ra: Tôi nghĩ bạn có thể hack file.inc
:
'nomask' => '/(\.\.?|CVS|node_modules)$/', // https://www.drupal.org/node/2329453#comment-9360519
Đây file_scan_directory()
là một hàm đệ quy mà tất cả các tệp khớp với một thư mục đã cho. Nó sử dụng is_dir()
và opendir()
các cuộc gọi PHP có thể tốn kém nhất về thời gian đối với các cuộc gọi hệ thống I / O. Bootstrap đơn giản (ví dụ time drush ev ""
) có thể gọi file_scan_directory
vài nghìn lần (tùy theo mức độ phức tạp của hệ thống phân cấp thư mục Drupal của bạn, ví dụ: số lượng mô-đun và thư mục của nó).
Trong trường hợp của tôi, tôi đã có ~ 1500 cuộc gọi đến file_scan_directory
(24 giây trong tổng số bao gồm 2 cuộc gọi từ drupal_system_listing
trong common.inc
, sau đó các cuộc gọi khác là phân chia bởi các cuộc gọi đệ quy để file_scan_directory
nó ngã.
Để cải thiện hiệu suất của các cuộc gọi I / O, bạn cần triển khai bộ đệm ẩn tệp. Điều này có thể đạt được bằng cách cài đặt và bật OPCache ( opcache.enable=1
) và điều chỉnh cài đặt của nó (xem: Cách sử dụng PHP OPCache? ). Cũng nên sử dụng bộ nhớ đệm dựa trên bộ nhớ như memcached / redis.
Khi sử dụng giao diện dòng lệnh (chẳng hạn như drush
), bạn cũng nên bật opcache.enable_cli=1
.
Sau khi thay đổi, bạn có thể kiểm tra các tòa nhà tiêu thụ nhiều hơn bằng cách sử dụng một số trình gỡ lỗi có sẵn.
Ví dụ
Trên Linux bằng cách sử dụng strace
(nhấn Ctrl- Cđể kết thúc):
sudo strace -c -fp $(pgrep -n php)
Trên Unix sử dụng dtrace
(sử dụng các đầu dò tĩnh DTrace của PHP ), vd
sudo dtrace -n 'inline string NAME = "php"; syscall:::entry /(NAME == strstr(NAME, execname)) || (execname == strstr(execname, NAME))/ { @num[probefunc] = count(); }'
Bạn có thể xem xét thêm tối ưu hóa drupal_system_listing()
hoặc file_scan_directory()
bằng cách triển khai bộ đệm tĩnh, ví dụ:
--- 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);
Hoặc đối với file_scan_directory
các cuộc gọi bộ đệm từ drupal_system_listing()
, sau đó kiểm tra bản vá sau đây có sẵn tại: file_scan_directory sẽ được lưu vào bộ đệm .