在我們的WordPress伺服器中查找高CPU使用率的罪魁禍首

幾天前,我們收到了託管服務提供商(SiteGround)的電子郵件,通知我們我們的網站已經「每月允許使用的CPU的90%」,一旦超過100%,「網路服務將受到限制」而且我們可能「無法訪問它」。嚇壞了吧?我們必須儘快解決這絕對是不希望的情況。但是…從哪裡開始?

高CPU使用率期間我們網站的統計信息
高CPU使用率期間我們網站的統計信息。

今天,我們想與您分享我們在網站上遇到一個相當普遍的問題的經驗,解釋我們為找出罪魁禍首所做的工作以及我們如何解決該問題。這樣,如果您遇到類似的問題,您將對如何開始有一些想法……

CPU使用率高的原因

WordPress是用PHP編寫的內容管理系統。這意味著它所提供的內容是由一組PHP腳本動態生成的:每次訪問者到達您的網站時,WordPress都會處理請求(類似於「請給我發送您的主頁」之類的內容)並生成響應(在在這種情況下,它將發送主頁)。顯然,響應請求意味著一定程度地使用伺服器資源:必須查看請求本身,確定訪問者想要訪問的內容,從資料庫中獲取請求,生成HTML響應等等。

緩存系統可以加快網站載入時間的原因之一現在應該很明顯:基本上可以節省處理時間。當特定請求首次到達時(「將您的主頁發送給我」),WordPress開始啟動並生成響應。如果有緩存,它將在實際發送給訪問者之前存儲所述響應。這樣,將來對同一資源(在我們的示例中為主頁)的請求不再需要WordPress處理任何事情;緩存可以將其之前保存的副本發送回去,從而節省了時間和資源。

考慮到這種性能,不難想像為什麼在伺服器上看到高CPU使用率的原因是什麼:

  • 您收到太多請求。如果很多用戶在上班時間訪問您的網站,或者您收到許多不合法的請求(可能有人在攻擊您的伺服器),WordPress將不得不處理所有這些請求,因此,伺服器資源的使用將增加。
  • 請求的解決速度很慢。如果您安裝了許多插件,或者某些插件由於某種原因效率低下,那麼您收到的所有請求將花費比所需時間更長的時間,因為WordPress將運行許多效率低下的代碼。

因此,看起來高速緩存可以很好地防範這些問題,對嗎?確實是這樣。但是,請記住,緩存不會「解決」該問題;它只是「隱藏」它。記住這一點很重要,因為有些WordPress功能無法緩存,因此始終需要WordPress運行:

  • 使用WP-Cron計劃的任務。WP-Cron是一種WordPress機制,用於計劃將來運行的任務。例如,WordPress使用它來發布預定的帖子。
  • WordPress的REST APIREST API是一種介面,可以通過發送和接收JSON對象從第三方應用程序使用該介面與WordPress網站進行交互。某些REST API請求可能會被緩存(即GET請求),而其他的則不是(POST和PUT)。因此,您通常無法緩存該內容…
  • WordPress中的AJAX請求在WordPress中使用REST API之前,我們必須使用其AJAX API創建動態網站。該API與REST API非常相似,因為我們也可以使用它來向伺服器發送信息和從伺服器接收信息。這是一個不同的系統,但是它受到REST API的相同限制。

分析問題

首先,我們必須確定為什麼我們的網站上的CPU使用率增加了。對我們網站的請求數量是否增加了?現在滿足個人請求的速度變慢了嗎?為了回答這些問題,我們在伺服器上有一個非常有用的工具:訪問日誌

訪問日誌是一個文本文件,伺服器在其中記錄它收到的每個請求以及有關它們的有用信息。具體來說,訪問日誌告訴我們何時接收到請求(日期和時間),發出請求的人(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 ...這是瀏覽器的用戶代理,它向我們提供了有關發出請求的設備和操作系統的信息。

請注意,我們的伺服器66.249.83.82在第一個請求後如何收到來自同一IP()的多個請求。這可能看起來像是攻擊,但實際上並非如此:網頁通常包含多種資產(圖像,腳本,樣式),訪問您網站中某個網頁的訪問者會執行多個請求以全部檢索它們是完全正常的。 。

好吧,在我們的案例中,我們能夠驗證確實存在著異常多的請求。實際上非常高。我們知道,因為日誌文件比平時大得多。

可能的解釋是,由於某種原因,訪問量達到了高峰……但是根據Google Analytics(分析),事實並非如此。所以發生了一些不同的事情。

對訪問日誌的更詳細分析使我們能夠確定以下事實:我們收到的所有請求中,有15%以上來自同一IP。而且(鼓點)IP是我們自己的Web伺服器!

找出罪魁禍首

至此,我們終於知道這是我們自己的伺服器,它發出了如此多的請求,從而導致CPU使用率達到峰值。但為什麼?為什麼會這樣呢?誰在生成這些請求?這些是更難回答的問題。

首先,我們再次查看了日誌,按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"
...

我們找到了一個:所有這些異常請求的User-Agentphp-requests/1.7-3470169。有趣!

WordPress具有一些觸發請求的功能:wp_remote_request。如果您看一下這些函數的源代碼,就會發現它們基本上包裝了一個名為WP_Http的類中的一些方法。此類很有趣,因為默認情況下,它發出的所有請求都將User-Agent設置為「 WordPress / version」,因此這可能與我們的問題有關。但是還沒有……。

如果我們繼續檢查WordPress的源代碼,我們會看到它在內部WP_Http使用了另一個WordPress類來實際發出請求:Requests。而男孩是這個類有趣:在開始的時候它的定義,我們把它定義為恆定命名的VERSION,其值是1.7-3470169。稍後,它使用此常量來構建我們在日誌中找到的User-Agentphp-requests/1.7-3470169

輝煌!現在,我們已經確認,我們收到的所有這些奇怪請求均來自我們的WordPress網站。這可能意味著罪魁禍首是一個插件……但是,哪個插件呢?

我們必須弄清楚這一點的想法很簡單:如果我們修改User-Agent,使其包含使用Requests該類的插件的名稱,我們將在伺服器的訪問日誌中看到該插件的名稱。這實際上很容易實現。我們所做的就是用以下代碼片段編輯Requestsget_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)"
...

解決問題

一旦知道了有問題的插件的名稱,我們就簡單地聯繫了它的開發人員並尋求幫助。他們回答並告訴我們如何在解決正確的解決方案時解決該問題。對我們來說幸運的是,情況很快好起來了:

糾正CPU使用問題後來自我們網站的統計信息
尋求幫助後從我們的網站統計。

如您所見,關於自由軟體的一大優點是,我們可以探索我們使用的應用程序的源代碼並使其適應我們的需求。在這種情況下,我們能夠修改WordPress本身,以便它可以揭示我們需要的一些信息。

我希望您喜歡用它debug_backtrace來找出誰可以運行某個功能的技巧。當然,這不是一個正統的方法,但是它實現起來很快,並且到目前為止,它始終被證明是非常有用的。

相關文章