LAMP服务器中PHP请求负载平衡。

5

在AWS 1.7GB服务器中一次性接收到2000个请求。应用程序试图处理它们,但最终出现了内存耗尽错误。我优化了PHP脚本和MySQL数据库,达到了我所知道的最优状态。

因此,我的决定是:

我想在服务器上处理200个请求,并拒绝前1800个请求。下一次将处理下一个200个请求并拒绝1600个请求。通过这种方式,我可以处理所有请求。

问题1:如何实现?

我计划像下面这样实现:

  1. 获取Apache进程计数,如果超过120,则拒绝来自服务器的请求。

  2. 基于监视服务器RAM可用内存,我计划拒绝请求。

需要建议:哪种实现方法最佳?

欢迎提出其他建议。

问题2:如何使用PHP获取Apache进程计数?

问题3:如何使用PHP获取可用RAM内存大小?

注意:拒绝请求不是问题,我可以再次获取。如果我从服务器拒绝请求,那么就没有问题,服务器正常。一旦我处理了这2000个请求,之后负载就会降低。


你有对拒绝请求的具体要求吗?比如通过响应5XX HTTP状态码来拒绝请求? - jdhildeb
我在计划部分提到了我的决定。你可以分享你的想法,这个好吗?如果好的话,如何实现呢? - Sundar
我的建议是:1. 检查是否启用了apache [ExpiresActive from mod_expires.c]; 2. 使用php-cgi,例如:php-fpm或hhvm(HipHop Virtual Machine for PHP)。它确实能够带来很大的差异。3. 不要处理部分请求,使用缓存服务,例如:varnish,除非所有请求都需要不同的内容;4. 将静态内容移动至cdn服务器;5. 使用AWS平衡服务器-有点昂贵,我并不是真正建议这样做。仅逐一执行以上步骤,您将很快发现差异。 - Zac
4个回答

2

我已经使用APC制作了一个PHP进程限制器的原型。

<?php

   $processes = apc_fetch('processes');
   if(!$processes) { // Initial Status
        $processes=1;
   }
   if ($processes > 3) {
        echo "Reject: ". $processes;
        // Return HTTP/403 ...
        exit -1;
   }

   $processes ++;
   apc_store('processes', $processes);

   // Long memory hunger code
   sleep(10);
   // .... your code   .....//

   // Implement global MUTEX??
   $processes = apc_fetch('processes');
   echo "Pending process: ". ($processes -1);
   $processes --;
   apc_store('processes', $processes);
?>

问题在于内存。如果我们使用APC缓存来处理数据,内存不足以进行处理,我必须分配额外的内存。但在这种情况下,这种方法行不通。这个逻辑很好,我可以在其他问题中使用它。 - Sundar
你只需要使用APC存储一个变量。 - KikoV
是的,但是该模块需要一些内存来安装。 - Sundar
那使用这个呢?http://www.php.net/manual/en/book.shmop.php 和互斥量? - KikoV

2
首先,我建议不要使用系统调用,特别是在您有许多请求时。运行外部进程可能会导致性能问题,而且在您的情况下,进程数/内存使用量会快速变化(您说过一次有2000个请求),因此您不能使用cronjob缓存这些值(即使您每秒运行一个cron,也无法确保这些值100%真实)。您可以获取脚本的内存使用情况,估算您可以同时处理的进程数,这样就可以了。
现在,据我所知,您想按特定顺序处理请求:处理1-200个请求,然后是201-400个请求,以此类推?如果是这种情况,您需要跟踪已经处理过的请求。
实现这一点的简单方法是在数据库中保持请求队列 - 如果您可以使用memcached或类似的东西,那就更好了:
- 每次收到请求时,您都会检查队列,并确保您没有超过200个活动请求; - 下一步是检查请求是否应该运行(这意味着您可以通过检查GET / POST中的某个值来唯一标识每个请求)- 这样可以确保如果请求#200在上一分钟内被处理,您将忽略它并允许请求#201运行; - 如果请求检查通过,则将其添加到队列中作为活动请求,并在完成后将其标记为已完成/从队列中删除;
但是,如果请求顺序对您不重要,那么您可以只保留请求计数,并确保您永远不会超过某个限制。

这是一个不错的想法。在我不知道为什么之前应该实施这个想法。现在在生产服务器上,我无法修改代码。 我没有拒绝呼叫,而是将200个作业请求转储到表中,这对数据库来说是较小的负载。我们可以管理这个问题。对于这种类型的请求队列,我们不需要拒绝任何东西。 但是,这个逻辑已经在当前版本的代码中实现了。尽管如此,服务器仍然需要进行升级。 - Sundar

1
我认为最好的方式是尝试以可扩展的方式实现您的目标,而不是拒绝请求并依赖系统指标。我过去曾经使用过相同的设置来处理视频。
如果是我,我会这样设置:
  1. 从弹性负载均衡器开始

  2. 在负载均衡器内创建一个按需小型 ec2 实例的自动扩展组(如果预算非常紧张,甚至可以使用微型实例)。该组的大小将根据您的工作负载类型而变化。确保使用 CloudWatch 根据负载均衡器的工作负载扩展该组。

  3. 这些实例将负责接收处理请求并将其传递到 SQS 队列。这些实例不需要很努力地工作,因为它们所做的只是将请求转发到 SQS 队列。

    NB:如果您的客户能够直接将请求推送到 SQS 队列,则实际上可以完全跳过步骤 1-3。

  4. 现在让我们来制造你的劳动力。设置另一个自动缩放组,其中包含更多的小实例,但该组将由 spot 实例组成。将该组的最小大小设置为 0,最大大小设置为 10。还要将 spot 实例价格设置为可让实例大部分时间启动但不会在 spot 价格飙升时花费大量资金的价格。

  5. 使用 CloudWatch 监视 SQS 队列,并在队列中有一些项目时触发工作自动缩放组扩展。

  6. 应将您的工作力量实例设置为开始轮询 SQS 队列并在它们被启动后立即处理请求。由您确定它们消耗此队列的速度。

额外阅读:

是的,我可以使用这个,但公司不想花任何钱解决这个问题,因为这个问题是由我们自己的错误造成的,我们必须利用现有的资源来解决它。感谢您提供的信息。 - Sundar

1
根据您对服务器的访问权限,通过读取两个命令的输出可以实现您想要的操作。我假设您在Linux服务器上,如果不是这种情况,则必须使用另一个命令/选项。 ps H -U apache(获取所有apache线程)
cat /proc/meminfo
例如,我会使用cron作业将该信息写入PHP可以读取的文件中,然后在脚本中使用该信息。
对于进程数,只需计算文件中的行数即可。
对于可用内存,您需要进行一些计算。 meminfo的输出很长且详细,但您只需要考虑memfree和swapfree两个值。如果系统是专用的,没有其他类型的进程正在运行,您还可以包括缓存值,因为这些值很可能已被apache使用。
如果您不能/不想在系统上使用cron作业,但可以从PHP执行命令,则可以执行这些命令,但我认为最好将工作的每个部分分开处理。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接