file_scan_directory () mất khoảng 10 giây để thực thi


10

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:

ảnh chụp màn hình


Bạn không thể "file_scan_directory" "trang trước", vì trang trước là một mục trong bảng cơ sở dữ liệu, không phải là đường dẫn hệ thống tệp.
Letharion

@Letharion Tôi nghĩ bạn đã hiểu nhầm câu hỏi của tôi. Ý tôi là thời gian mà chức năng này mất khi tải trang trước. Tôi đã chỉnh sửa câu hỏi.
hknik

Trang đầu có thực sự có liên quan gì đến chức năng chiếm một lượng thời gian cụ thể không? Thư mục nào bạn thực sự quét? Có gì trong thư mục?
Letharion

Aha! Tôi nghĩ bạn đã tự gọi chức năng này và tự hỏi tại sao bạn không cung cấp thêm chi tiết. Câu trả lời của Berdir có vẻ rất hợp lý. :)
Letharion

Câu trả lời:


14

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.


Thật không may (!) Không có mô-đun nào bị thiếu trong hệ thống tập tin
hknik

Sau đó, bạn cần theo dõi cuộc gọi đến từ đâu, có thể một mô-đun tùy chỉnh hoặc đóng góp đang làm gì đó sai. Nhấp qua các hàm cha của file_scan_directory () và cập nhật bài đăng ban đầu với danh sách các hàm cha.
Berdir

Nhìn vào các chức năng đó, 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: api.drupal.org/api/drupal/includes!bootstrap.inc/feft/ trên . Nó gọi hàm 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 chức năng nào nó không tìm thấy.
Berdir

@Berdir Nhận xét cuối cùng của bạn nên có trong câu trả lời của bạn, vì nó có liên quan.
kiamlaluno

Các liên kết đến "sự cố đã biết trong Drupal 7" và "Tránh quét lại thư mục mô-đun" bị hỏng. Cả hai đều là câu trả lời stackexchange trước đây. Có ai có tài liệu tham khảo khác?
rfay

4

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 đó.

Các mô-đun loại bỏ khỏi cơ sở mã

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.

Lỗi mã hóa

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_filenametrong bootstrap.incbằ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ì dpmsẽ hoạt động; nếu không, bạn có thể sử dụng drupal_set_messagehoặ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_actionstrong 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.modulesử 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.


2

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_modulesthư 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


0

Đâ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()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_directoryvà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_listingtrong 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_directorynó 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_directorycá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 .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.