البحث عن السبب وراء الاستخدام العالي لوحدة المعالجة المركزية في خادم WordPress الخاص بنا
نشرت: 2020-05-07قبل بضعة أيام ، تلقينا بريدًا إلكترونيًا من مزود الاستضافة (SiteGround) لإعلامنا بأن موقعنا يحتوي بالفعل على "90٪ من الاستخدام الشهري المسموح به لوحدة المعالجة المركزية" وأنه بمجرد تجاوزنا لـ 100٪ ، "ستكون خدمة الويب محدودة" وقد نواجه "مشكلة في الوصول إليه". مخيف جدا ، أليس كذلك؟ لقد كان بالتأكيد موقفًا غير مرغوب فيه تمامًا وكان علينا إصلاحه في أسرع وقت ممكن. لكن ... من أين نبدأ؟

أردنا اليوم أن نشارككم تجربتنا في مواجهة مشكلة شائعة إلى حد ما في مواقع الويب ، وشرح ما فعلناه لتحديد الجاني وكيف حللنا المشكلة. بهذه الطريقة ، إذا واجهت مشكلة مماثلة ، سيكون لديك بعض الأفكار حول كيفية البدء ...
أسباب ارتفاع استخدام وحدة المعالجة المركزية
WordPress هو نظام إدارة محتوى مكتوب بلغة PHP. هذا يعني أن المحتوى الذي يقدمه يتم إنشاؤه ديناميكيًا بواسطة مجموعة من نصوص PHP النصية: في كل مرة يصل زائر إلى موقع الويب الخاص بك ، يقوم WordPress بمعالجة الطلب (وهو شيء مثل "الرجاء إرسال صفحتك الرئيسية إلي") ويقوم بإنشاء استجابة (في في هذه الحالة ، يتم إرسال الصفحة الرئيسية). من الواضح أن الاستجابة للطلب تعني استخدامًا معينًا لموارد الخادم: على المرء أن ينظر إلى الطلب نفسه ، وتحديد ما يريد الزائر الوصول إليه ، وجلبه من قاعدة البيانات ، وإنشاء استجابة HTML ، وما إلى ذلك.
يجب أن يكون أحد أسباب تسريع نظام ذاكرة التخزين المؤقت لوقت تحميل موقع الويب الخاص بك واضحًا تمامًا الآن: فهو يوفر وقت المعالجة هذا بشكل أساسي. عندما يصل طلب محدد لأول مرة ("أرسل لي صفحتك الرئيسية") ، يبدأ WordPress ويولد الاستجابة. إذا كانت ذاكرة التخزين المؤقت في مكانها الصحيح ، فإنها تخزن الاستجابة المذكورة قبل إرسالها فعليًا إلى الزائر. بهذه الطريقة ، الطلبات المستقبلية إلى نفس المورد (في مثالنا ، الصفحة الرئيسية) لم تعد تتطلب WordPress لمعالجة أي شيء ؛ يمكن لذاكرة التخزين المؤقت إعادة النسخة المحفوظة من قبل ، وبالتالي توفير الوقت والموارد.
مع وضع هذا الأداء في الاعتبار ، ليس من الصعب تخيل الأسباب التي تجعلنا نرى استخدامًا عاليًا لوحدة المعالجة المركزية على خادمنا:
- لقد تلقيت طلبات كثيرة جدًا. إذا جاء الكثير من المستخدمين إلى موقع الويب الخاص بك في نفس الوقت ، أو تلقيت العديد من الطلبات غير المشروعة (ربما يهاجم شخص ما خادمك) ، فسيتعين على WordPress معالجة كل هذه الطلبات ، وبالتالي ، سيزداد استخدام موارد الخادم.
- الطلبات بطيئة في الحل. إذا كان لديك الكثير من المكونات الإضافية المثبتة أو أن بعض المكونات الإضافية الخاصة بك غير فعالة لأي سبب من الأسباب ، فإن جميع الطلبات التي تحصل عليها ستستغرق وقتًا أطول من اللازم ، لأن WordPress سيشغل الكثير من التعليمات البرمجية غير الفعالة.
لذلك يبدو أن ذاكرة التخزين المؤقت هي وسيلة حماية جيدة ضد هذه المشكلات ، أليس كذلك؟ وبالفعل هو كذلك. ومع ذلك ، يرجى أن تضع في اعتبارك أن ذاكرة التخزين المؤقت لا "تصلح" المشكلة ؛ إنه ببساطة "يخفيه". ومن المهم تذكر ذلك لأن هناك وظائف WordPress لا يمكن تخزينها مؤقتًا ، وبالتالي ، ستتطلب دائمًا تشغيل WordPress:
- المهام المجدولة باستخدام WP-Cron. WP-Cron هي آلية WordPress لجدولة المهام التي سيتم تشغيلها في المستقبل. على سبيل المثال ، يستخدمه WordPress لنشر المنشورات المجدولة.
- WordPress 'REST API. واجهة برمجة تطبيقات REST هي واجهة يمكن للمرء استخدامها من تطبيقات الطرف الثالث للتفاعل مع موقع WordPress عن طريق إرسال واستقبال كائنات JSON. قد يتم تخزين بعض طلبات REST API مؤقتًا (أي طلبات GET) ، لكن البعض الآخر ليس كذلك (POST و PUT). لذلك ، هذا شيء لا يمكنك تخزينه مؤقتًا بشكل عام ...
- طلبات AJAX في WordPress. قبل أن يكون لدينا واجهة برمجة تطبيقات REST في WordPress ، كان علينا استخدام AJAX API الخاص بها لإنشاء مواقع ويب ديناميكية. تشبه واجهة برمجة التطبيقات هذه تمامًا واجهة برمجة تطبيقات REST ، لأنه يمكننا أيضًا استخدامها لإرسال المعلومات واستلامها من الخادم. إنه نظام مختلف ، لكنه يخضع لنفس القيود التي تخضع لها REST API.
تحليل المشكلة
أولاً ، يتعين علينا تحديد سبب زيادة استخدام وحدة المعالجة المركزية على موقعنا. هل زاد عدد الطلبات على موقعنا؟ هل هو الآن أبطأ في خدمة الطلبات الفردية؟ للإجابة على هذه الأسئلة ، لدينا أداة مفيدة للغاية على خادمنا: سجل الوصول .
سجل الوصول هو ملف نصي يسجل فيه الخادم كل طلب يتلقاها مع معلومات مفيدة عنها. على وجه التحديد ، يخبرنا سجل الوصول بوقت استلام الطلب (التاريخ والوقت) ، ومن الذي قدمه (عنوان IP) ، والمورد الذي طلبه (عنوان URL) ، وما إذا كان الطلب ناجحًا ، وما إلى ذلك. إليك مثال من الخادم الخاص بنا :
66.249.83.82 - - [22/Apr/2020:14:04:59 +0200] "GET /es/blog/imagenes-gratuitas-para-tu-blog/ HTTP/1.0" 200 22325 "-" "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko; googleweblight) Chrome/38.0.1025.166 Mobile Safari/535.19" 66.249.83.84 - - [22/Apr/2020:14:05:02 +0200] "GET /es/wp-content/uploads/sites/3/2018/07/aziz-acharki-549137-unsplash-540x350.jpg HTTP/1.0" 200 10566 "https://neliosoftware.com/es/blog/imagenes-gratuitas-para-tu-blog/" "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko; googleweblight) Chrome/38.0.1025.166 Mobile Safari/535.19" 66.249.83.82 - - [22/Apr/2020:14:05:02 +0200] "GET /es/wp-content/uploads/sites/3/2018/07/Screenshot-of-Unsplah-website-768x520.png HTTP/1.0" 200 399577 "https://neliosoftware.com/es/blog/imagenes-gratuitas-para-tu-blog/" "Mozilla/5.0 (Linux; Android 4.2.1; en-us; Nexus 5 Build/JOP40D) AppleWebKit/535.19 (KHTML, like Gecko; googleweblight) Chrome/38.0.1025.166 Mobile Safari/535.19" ... 188.79.17.218 - - [22/Apr/2020:14:06:14 +0200] "GET /es/blog/problemas-mas-comunes-de-wordpress/ HTTP/1.0" 200 110741 "https://www.google.com/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15" 188.79.17.218 - - [22/Apr/2020:14:06:16 +0200] "GET /es/wp-content/plugins/nelio-ab-testing/assets/dist/js/alternative-loader.js?version=52b0ff65c68ab39896d47d6ff673fd59 HTTP/1.0" 200 2763 "https://neliosoftware.com/es/blog/problemas-mas-comunes-de-wordpress/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15" 188.79.17.218 - - [22/Apr/2020:14:06:16 +0200] "GET /es/wp-includes/css/dist/block-library/style.min.css?ver=5.4 HTTP/1.0" 200 7627 "https://neliosoftware.com/es/blog/problemas-mas-comunes-de-wordpress/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Safari/605.1.15" ...دعنا نلقي نظرة فاحصة عليها:
-
66.249.83.82. هذا هو عنوان IP للجهاز الذي قدم الطلب. -
22/Apr/2020:14:04:59 +0200. هذا هو التاريخ والوقت المحددان للطلب. -
GET /es/blog/imagenes-gratuitas-para-tu-blog/ HTTP/1.0. ثم نرى الزائر طلب (GET) منشورًا من مدونتنا (باللغة الإسبانية). -
Mozilla/5.0 (Linux; Android ...هذا هو وكيل مستخدم المتصفح ويعطينا بعض المعلومات عن الجهاز ونظام التشغيل الذي قدم الطلب.
لاحظ كيف تلقى خادمنا طلبات متعددة من نفس IP ( 66.249.83.82 ) بعد الأول. قد يبدو هذا هجومًا ، لكنه في الواقع ليس كذلك: عادةً ما تتضمن صفحات الويب عدة أصول (صور ونصوص وأنماط) ومن الطبيعي تمامًا أن يقوم الزائر الذي يصل إلى صفحة ويب معينة في موقعك بتنفيذ طلبات متعددة لاستردادها جميعًا .

حسنًا ، في حالتنا ، تمكنا من التحقق من أن لدينا بالفعل عددًا كبيرًا غير عادي من الطلبات. مرتفع للغاية ، في الواقع. وعرفنا ذلك لأن ملف السجل كان أكبر بكثير من المعتاد.
قد يكون التفسير المحتمل هو أنه كانت هناك ذروة في الزيارات لسبب ما ... ولكن وفقًا لـ Google Analytics ، لم يكن هذا هو الحال. لذلك كان هناك شيء مختلف يحدث.
سمح لنا التحليل الأكثر تفصيلاً لسجل الوصول بتحديد الحقيقة التالية: أكثر من 15٪ من جميع الطلبات التي تلقيناها جاءت من نفس عنوان IP. و (لفة الأسطوانة) هذا IP كان خادم الويب الخاص بنا!

تحديد الجاني
في هذه المرحلة ، عرفنا أخيرًا أن الخادم الخاص بنا هو الخادم الذي يقدم العديد من الطلبات لدرجة أنه حقق ذروة في استخدام وحدة المعالجة المركزية الخاصة به. لكن لماذا؟ لماذا حدث هذا؟ من كان يولد تلك الطلبات؟ هذه أسئلة أصعب للإجابة عليها.
أولاً ، نظرنا إلى سجلاتنا مرة أخرى ، وقمنا بالتصفية حسب عنوان IP وحاولنا تحديد نمط يمكن أن يلقي بعض الضوء على المشكلة قيد البحث:
35.214.244.124 - - [22/Apr/2020:14:06:08 +0200] "GET /es HTTP/1.0" 301 - "https://neliosoftware.com/es" "php-requests/1.7-3470169" 35.214.244.124 - - [22/Apr/2020:14:06:08 +0200] "GET /es?..." "php-requests/1.7-3470169" 35.214.244.124 - - [22/Apr/2020:14:06:18 +0200] "GET /es?..." "php-requests/1.7-3470169" 35.214.244.124 - - [22/Apr/2020:14:07:21 +0200] "GET /es?..." "php-requests/1.7-3470169" 35.214.244.124 - - [22/Apr/2020:14:07:24 +0200] "GET /es?..." "php-requests/1.7-3470169" ... ووجدنا واحدًا: وكيل المستخدم لجميع هذه الطلبات الشاذة كان طلبات php-requests/1.7-3470169 . مثير للانتباه!
يحتوي WordPress على العديد من الوظائف التي يمكن من خلالها تشغيل الطلبات: wp_remote_request . إذا ألقيت نظرة على الكود المصدري لهذه الوظائف ، فسترى أنها تلتف بشكل أساسي عدة طرق من فئة تسمى WP_Http . هذا الفصل مثير للاهتمام لأنه ، بشكل افتراضي ، جميع الطلبات التي يقدمها تقوم بتعيين User-Agent على "WordPress / version" ، لذلك كان من الممكن أن يكون لهذا علاقة بمشكلتنا. لكنها لم ... بعد.
إذا واصلنا فحص شفرة مصدر WordPress ، فسنرى أن WP_Http يستخدم فئة WordPress أخرى داخليًا لتقديم الطلبات بالفعل: Requests . وهذه الفئة مثيرة للاهتمام: في بداية تعريفها نرى أنها تحدد ثابتًا اسمه VERSION تبلغ قيمته 1.7-3470169 . وبعد ذلك بقليل ، يستخدم هذا الثابت لبناء User-Agent الذي وجدناه في سجلاتنا: php-requests/1.7-3470169 .
متألق! لقد أكدنا الآن أن كل هذه الطلبات الغريبة التي نتلقاها تأتي من موقع WordPress الخاص بنا. ربما يعني هذا أن الجاني هو مكون إضافي ... ولكن أيهما؟
كانت الفكرة التي كان علينا أن نفهمها بسيطة للغاية: إذا قمنا بتعديل User-Agent بحيث يتضمن اسم المكون الإضافي الذي يستخدم فئة Requests ، فسنرى اسم المكون الإضافي في سجل الوصول الخاص بخادمنا. وهذا في الواقع سهل التحقيق. كل ما فعلناه هو تعديل وظيفة Requests get_default_options باستخدام المقتطف التالي:
$trace = debug_backtrace(); $files = []; foreach ( $trace as $log ) { if ( false !== strpos( $log['file'], '/wp-content/plugins/' ) ) { $files[] = $log['file']; } } if ( empty( $files ) ) { $debug = 'no-plugin'; } else { $plugins = array_map( function( $x ) { return preg_replace( '/.*\/wp-content\/plugins\/([^\/]+)\/.*/', '$1', $x ); }, $files ); $plugins = array_unique( $plugins ); $debug = implode( ' ', $plugins ); } $defaults['useragent'] .= " (NelioDebug {$debug})";دعونا نرى ما يفعله خطوة بخطوة:
- أولاً نحصل على مكدس التنفيذ مع
debug_backtrace. هذه وظيفة PHP تنشئ تتبعًا عكسيًا ، وتكشف عن جميع الوظائف التي تم استدعاؤها للوصول إلى الوظيفة الحالية. - لكل عنصر في مكدس التنفيذ ، لدينا معلومات مثل الوظيفة التي تم استدعاؤها ، والملف والسطر الذي تم تعريفه فيه ، والوسيطات التي تم استدعاؤها معها ، وما إلى ذلك ، ولكن ما نريد التركيز عليه هو الملف حيث تم تعريف الوظيفة: إذا كانت موجودة في
wp-content/plugins، فنحن نعرف بالتأكيد أنها وظيفة محددة في مكون إضافي. - بمجرد الانتهاء من معالجة جميع العناصر الموجودة في المكدس ، نحتاج ببساطة إلى الحصول على الأسماء (إن وجدت) لجميع المكونات الإضافية التي وجدناها وإدراجها في متغير
useragent.
بعد تمديد WordPress كما هو موضح ، سرعان ما بدأنا في معرفة من هو الجاني :
35.214.244.124 - - [23/Apr/2020:10:59:08 +0200] "GET /es HTTP/1.0" 301 - "https://neliosoftware.com/es" "php-requests/1.7-3470169 (NelioDebug culprit)" 35.214.244.124 - - [23/Apr/2020:10:59:20 +0200] "GET /?..." "php-requests/1.7-3470169 (NelioDebug culprit)" 35.214.244.124 - - [23/Apr/2020:10:59:36 +0200] "GET /?..." "php-requests/1.7-3470169 (NelioDebug culprit)" 35.214.244.124 - - [23/Apr/2020:11:00:01 +0200] "GET /es?..." "php-requests/1.7-3470169 (NelioDebug culprit)" 35.214.244.124 - - [23/Apr/2020:11:05:21 +0200] "GET /es?..." "php-requests/1.7-3470169 (NelioDebug culprit)" ...حل مشكلة
بمجرد أن عرفنا اسم المكون الإضافي المخالف ، اتصلنا ببساطة بمطوره وطلبنا المساعدة. ردوا وقالوا لنا كيف نتغلب على المشكلة بينما هم يعملون على حل مناسب. لحسن الحظ بالنسبة لنا ، تحسنت الأمور بسرعة:

كما ترى ، فإن أحد الأشياء العظيمة حول البرمجيات الحرة هو أنه يمكننا استكشاف الكود المصدري للتطبيقات التي نستخدمها وتكييفها مع احتياجاتنا. في هذه الحالة ، تمكنا من تعديل WordPress نفسه بحيث يمكنه الكشف عن بعض المعلومات التي نحتاجها.
آمل أن تكون قد أحببت خدعة استخدام debug_backtrace لمعرفة من يقوم بتشغيل وظيفة معينة. بالتأكيد ، إنها ليست طريقة تقليدية ، لكنها سريعة في التنفيذ ، وحتى الآن ، ثبت دائمًا أنها مفيدة للغاية.
صورة مميزة بواسطة Michal Mancewicz على Unsplash.
