使用技巧

在我们的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来找出谁可以运行某个功能的技巧。当然,这不是一个正统的方法,但是它实现起来很快,并且到目前为止,它始终被证明是非常有用的。