การใช้ xhprof ฉันสังเกตว่าfile_scan_directory()
ใช้เวลานานกว่า 10 วินาทีในการดำเนินการเมื่อโหลดหน้าแรก ทำไมต้องใช้เวลานานเช่นนี้?
นี่คือผลลัพธ์ของ xhprofile:
การใช้ xhprof ฉันสังเกตว่าfile_scan_directory()
ใช้เวลานานกว่า 10 วินาทีในการดำเนินการเมื่อโหลดหน้าแรก ทำไมต้องใช้เวลานานเช่นนี้?
นี่คือผลลัพธ์ของ xhprofile:
คำตอบ:
ดูเหมือนว่าคุณกำลังได้รับผลกระทบจากปัญหาที่รู้จักกันใน Drupal 7
ส่วนใหญ่อาจจะตีคุณไดเรกทอรีโมดูลหลีกเลี่ยงการ re-สแกนเมื่อหลายโมดูลจะหายไป มันจะเกิดขึ้นหากคุณมีโมดูลที่ขาดหายไปในการติดตั้ง ลองตรวจสอบตารางระบบของคุณ:
SELECT name, filename FROM system WHERE type = 'module' AND status = 1 ORDER BY filename
และล้างโมดูลใด ๆ ที่ยังคงเปิดใช้งานอยู่ แต่หายไปจากระบบไฟล์
โดยรวมแล้ว Drupal 7 เป็นวิธีที่เป็นมิตรกับทรัพยากรและปรับขนาดได้แล้ว Drupal 6 นอกเหนือไปจากการถดถอยที่น่าเสียดายเช่นนี้
ดูฟังก์ชั่นเหล่านั้นดูเหมือนว่าจะไม่มีโมดูลหรืออาจเป็นไฟล์เดียวของโมดูล ลองดูที่drupal_get_filename ()มันเรียก drupal_system_listing () ซึ่งเรียกใช้ฟังก์ชันนี้หากไม่พบไฟล์ที่ร้องขอ เพิ่ม dpm (func_get_args ()) ก่อนที่จะเรียก drupal_system_listing () ที่ควรบอกไฟล์ที่ไม่พบ
มีสาเหตุหลายประการที่ทำให้ปัญหานี้เกิดขึ้นและเพื่อความผิดหวังที่ยิ่งใหญ่ของฉันตอนนี้ฉันพบว่าตัวเองค่อนข้างมีความรู้เกี่ยวกับเหตุผลเหล่านั้น ถ้าคุณเพิ่งสังเกตเห็นปัญหานี้หลังจากอัพเกรด Drupal core ไปที่ 7.33+ นี่อาจเป็นความผิดพลาดในโมดูลใด ๆ แม้ว่าคุณจะไม่ได้อัพเกรดโมดูลนั้นก็ตาม
คุณอาจต้องการตรวจสอบข้อผิดพลาดที่รู้จักที่ @Berdir กล่าวถึงโดยเฉพาะหากคุณเพิ่งลบโมดูล "ไม่ได้ใช้" ออกจากฐานรหัส หากต้องการทราบว่าคุณมีโมดูลที่เปิดใช้งาน แต่ถูกลบออกจากระบบไฟล์คุณสามารถเรียกใช้สคริปต์เช่นที่กล่าวถึงที่นี่หรือใช้ทุ่นระเบิดซึ่งเขียนขึ้นสำหรับการติดตั้งแบบหลายไซต์บนระบบที่มี drush เพื่อให้ทำงานได้ จากไดเรกทอรีฐาน 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
หรือต่อไปนี้:
while read -r file; do [ -f "$file" ] || echo "$file is missing."; done < <(drush sqlq "SELECT filename FROM system WHERE status = 1")
หากคุณพบโมดูลที่ถูกลบออกจากฐานรหัสให้ทำตามคำแนะนำในประเด็นที่ @Berdir พูดถึง
หากไม่ใช่สิ่งนั้นสถานการณ์ของคุณน่าจะเกิดจากข้อผิดพลาดในการเขียนโค้ดเช่นไฟล์ที่ถูกลบออก แต่ยังคงถูกเพิ่มโดยการเรียก drupal_add_js (จากความคิดเห็นที่ 19 ในปัญหา # 1082892) หรือพิมพ์ผิดที่โชคร้ายในโมดูลหรือธีม เช่นimagecache_actions
(ดูhttps://drupal.org/node/2381357 )
ไม่ว่าในกรณีใดหากต้องการทราบสาเหตุที่เกิดขึ้นคุณต้องทราบว่าไฟล์ใดที่ Drupal หาไม่เจอ ดังนั้นตามความคิดเห็น Berdir คุณสามารถตัดชั่วคราวdrupal_get_filename
ในโดยการเพิ่มบันทึกข้อความหรือโทรก่อนที่จะเรียกร้องให้bootstrap.inc
drupal_system_listing()
หากคุณติดตั้งโมดูล Devel ไว้dpm
จะสามารถใช้งานได้ ถ้าไม่คุณสามารถใช้drupal_set_message
หรือ syslog ตัวอย่าง:
dpm(func_get_args());
drupal_set_message(implode(', ', func_get_args()));
syslog(LOG_WARNING, implode(', ', func_get_args()));
เมื่อคุณรู้ว่า Drupal กำลังมองหาอะไรมันเป็นโอกาสที่ดีที่คุณจะสามารถรู้ได้ว่าจะไปที่ไหนจากที่นั่น ปัญหาของฉันเกิดจากการเรียกเพื่อรวมไฟล์จากโมดูลที่ไม่มีอยู่imagcache_actions
(หมายเหตุการพิมพ์ผิด) ดังนั้นฉันค้นหาimagecache_actions
ใน codebase ของฉัน (เช่นgrep -r imagcache_actions .
) และพบว่ารุ่น 1.4 ของการimagecache_canvasactions.module
ใช้งาน module_load_include นอกการเรียกใช้ฟังก์ชันใด ๆ ในขอบเขตไฟล์ด้วยการพิมพ์ผิด อีกครั้งข้อผิดพลาดนี้จะปรากฏเฉพาะหลังจากอัปเดตเป็น Drupal 7.33+ ฉันพบว่าปัญหาได้ถูกสร้างขึ้นเพื่อimagecache_actions
ใช้งานโปรแกรมแก้ไขและกลับมาทำธุรกิจอีกครั้ง
ฉันมีปัญหาที่คล้ายกันมาก - file_scan_directory()
กำลังฆ่าไซต์ เปิดnode_modules
โฟลเดอร์huuge ที่ฝังอยู่ในชุดรูปแบบที่ฉันกำหนดสำหรับgulp
การสแกนแคชแต่ละครั้ง การย้ายไฟล์เหล่านี้ออกจากโฟลเดอร์ธีม (และอัปเดตบางพา ธ ในไฟล์ gulpfile ของฉัน) ดูเหมือนจะแก้ไขให้ฉัน อีกวิธีหนึ่ง: ฉันคิดว่าคุณสามารถแฮ็คfile.inc
:
'nomask' => '/(\.\.?|CVS|node_modules)$/', // https://www.drupal.org/node/2329453#comment-9360519
file_scan_directory()
เป็นฟังก์ชันเวียนซึ่งไฟล์ทั้งหมดที่ตรงกับไดเรกทอรีที่กำหนด มันใช้is_dir()
และopendir()
โทร PHP ซึ่งอาจแพงที่สุดในแง่ของการโทรระบบ I / O Bootstrap แบบง่ายของ Drupal (เช่นtime drush ev ""
) อาจใช้file_scan_directory
เวลาสองสามพันครั้ง (ขึ้นอยู่กับความซับซ้อนของลำดับชั้นโฟลเดอร์ Drupal เช่นจำนวนโมดูลและโฟลเดอร์)
ในกรณีของฉันฉันมี ~ 1500 โทรไปfile_scan_directory
(24 วินาทีรวมประกอบด้วย 2 สายจากdrupal_system_listing
ในcommon.inc
นั้นสายอื่น ๆ ที่แยกจากสายเรียกซ้ำไปfile_scan_directory
มันด้วยตัวเอง
เพื่อปรับปรุงประสิทธิภาพการโทร I / O คุณจะต้องใช้การแคชไฟล์ สิ่งนี้สามารถทำได้โดยการติดตั้งและเปิดใช้งาน OPCache ( opcache.enable=1
) และปรับแต่งการตั้งค่า (ดู: วิธีการใช้ PHP OPCache? ) แนะนำให้ใช้การแคชตามหน่วยความจำเช่น memcached / Redis
เมื่อใช้ติดต่อบรรทัดคำสั่ง (เช่นdrush
) opcache.enable_cli=1
คุณควรเปิดใช้งาน
หลังจากการเปลี่ยนแปลงคุณสามารถตรวจสอบ syscalls ที่มากขึ้นโดยใช้ debuggers ที่มีอยู่
เช่น
บน Linux ใช้strace
(กดCtrl- Cเพื่อเสร็จสิ้น):
sudo strace -c -fp $(pgrep -n php)
ใน Unix ใช้dtrace
(ใช้โพรบคง DTrace ของ PHP ), เช่น
sudo dtrace -n 'inline string NAME = "php"; syscall:::entry /(NAME == strstr(NAME, execname)) || (execname == strstr(execname, NAME))/ { @num[probefunc] = count(); }'
คุณสามารถพิจารณาการเพิ่มประสิทธิภาพdrupal_system_listing()
หรือfile_scan_directory()
ดำเนินการแคชแบบคงที่เช่น
--- 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);
หรือแคชfile_scan_directory
โทรออกจากdrupal_system_listing()
นั้นตรวจสอบแพทช์ต่อไปนี้มีอยู่ที่: file_scan_directory ควรจะเก็บไว้