本文目录导读:

在PHP项目中动态调整并发数量,主要取决于你的运行模式(FPM、Swoole、Workerman等),下面是几种常见场景的解决方案:
PHP-FPM 模式(最传统)
FPM是进程池模型,并发数由 pm.max_children 控制,动态调整需要重载配置:
实现方式:
// 保存PID文件路径
$pidFile = '/var/run/php-fpm.pid';
$configFile = '/etc/php/8.2/fpm/pool.d/www.conf';
// 1. 读取当前配置
$config = file_get_contents($configFile);
// 2. 计算新的并发数(基于系统负载)
$loadAvg = sys_getloadavg()[0]; // 当前1分钟负载
$cpuCores = 4; // CPU核心数
if ($loadAvg > $cpuCores * 0.8) {
$newMax = 20; // 降低
} else {
$newMax = 50; // 升高
}
// 3. 修改配置
$config = preg_replace(
'/pm\.max_children\s*=\s*\d+/',
"pm.max_children = {$newMax}",
$config
);
file_put_contents($configFile, $config);
// 4. 优雅重载
exec("sudo /usr/sbin/php-fpm8.2 --reload");
问题:
- 需要root权限
- 重载需要时间(秒级)
- 无法实时调整
Swoole/Workerman 常驻内存模式
这种模式可以真正实现毫秒级动态调整。
Swoole 实现:
$server = new Swoole\Http\Server('0.0.0.0', 9501);
// 动态调整Worker数量
$server->on('Start', function($server) {
// 启动动态调整线程
Swoole\Timer::tick(5000, function() use ($server) {
$loadAvg = sys_getloadavg()[0];
$cpuCores = swoole_cpu_num();
$currentWorkers = $server->setting['worker_num'];
// 计算目标worker数
if ($loadAvg > $cpuCores * 0.7 && $currentWorkers > 4) {
// 负载高,减少Worker
$newWorkers = max(4, $currentWorkers - 2);
} elseif ($loadAvg < $cpuCores * 0.3 && $currentWorkers < 32) {
// 负载低,增加Worker
$newWorkers = min(32, $currentWorkers + 2);
} else {
return; // 不需要调整
}
// 设置新的Worker数
$server->set([
'worker_num' => $newWorkers
]);
echo "Worker数调整为: {$newWorkers}\n";
});
});
Workerman 实现:
$worker = new Workerman\Worker('http://0.0.0.0:9501');
$worker->count = 16; // 初始Worker数
// 动态调整
$timerId = Workerman\Lib\Timer::add(10, function() use ($worker, &$timerId) {
$loadAvg = sys_getloadavg()[0];
$cpuCores = shell_exec('nproc');
// 获取当前连接数
$connectionCount = count($worker->connections);
// 根据连接数调整Worker
if ($connectionCount > 1000 && $worker->count < 64) {
$worker->count = min(64, $worker->count + 4);
echo "增加Worker到: {$worker->count}\n";
} elseif ($connectionCount < 100 && $worker->count > 4) {
$worker->count = max(4, $worker->count - 2);
echo "减少Worker到: {$worker->count}\n";
}
});
基于消息队列的消费速度控制
如果需要控制消费者进程数量(如RabbitMQ/Kafka):
class DynamicConsumerManager
{
private $maxConsumers = 10;
private $minConsumers = 1;
private $currentConsumers = 1;
private $queue;
public function adjustConsumers()
{
// 获取队列积压情况
$queueSize = $this->queue->getQueueSize();
$processingRate = $this->getCurrentProcessingRate();
// 目标:保持队列大小稳定
$targetQueueSize = 1000;
// PID控制算法
$error = $queueSize - $targetQueueSize;
$derivative = $error - $this->lastError;
$this->lastError = $error;
// 调整量
$adjust = 0.1 * $error + 0.05 * $derivative;
$newConsumers = round($this->currentConsumers + $adjust);
// 限制范围
$newConsumers = max($this->minConsumers, min($this->maxConsumers, $newConsumers));
if ($newConsumers != $this->currentConsumers) {
$this->updateConsumerCount($newConsumers);
$this->currentConsumers = $newConsumers;
}
}
private function getCurrentProcessingRate()
{
// 计算每秒处理的消息数
return $this->processedCount / max(1, time() - $this->startTime);
}
private function updateConsumerCount($count)
{
// 实际调整消费者数(可能通过Supervisor或其他进程管理器)
$cmd = "supervisorctl update consumer_num={$count}";
exec($cmd);
}
}
使用配置中心 + 热加载
适用于微服务架构,通过配置中心动态调整:
class ConfigDrivenWorkerManager
{
private $consulClient;
private $server;
public function watchConfig()
{
while (true) {
// 从配置中心获取最新配置
$config = $this->consulClient->get('service/worker_config');
if ($config['worker_num'] != $this->currentWorkers) {
// 热更新Worker数
$this->server->set(['worker_num' => $config['worker_num']]);
// 记录日志
$this->log("Worker数已调整: {$this->currentWorkers} -> {$config['worker_num']}");
}
sleep(30); // 每30秒检查一次
}
}
}
最佳实践建议
指标选择:
- CPU负载:适合CPU密集型任务
- 队列积压:适合消息消费场景
- 请求响应时间:适合Web服务
- 内存使用率:避免OOM
调整策略:
class AdaptiveConcurrencyControl
{
private $metrics = [];
public function decideAdjustment()
{
// 综合多个指标
$cpuScore = $this->normalize($this->getCpuLoad(), 0, 100);
$memoryScore = $this->normalize($this->getMemoryUsage(), 0, 80);
$latencyScore = $this->normalize($this->getAvgLatency(), 0, 200);
$overallScore = ($cpuScore * 0.4 + $memoryScore * 0.3 + $latencyScore * 0.3);
if ($overallScore > 80) {
return 'decrease'; // 负载过高
} elseif ($overallScore < 30) {
return 'increase'; // 资源空闲
} else {
return 'stable';
}
}
private function normalize($value, $min, $max) {
return min(100, max(0, ($value - $min) / ($max - $min) * 100));
}
}
安全机制:
class SafeAdjuster
{
private $lastAdjustTime = 0;
private $minAdjustInterval = 30; // 最小调整间隔(秒)
public function safeAdjust($newValue)
{
// 1. 检查调整频率
if (time() - $this->lastAdjustTime < $this->minAdjustInterval) {
return false;
}
// 2. 渐进式调整(每次最多调整20%)
$maxChange = $this->currentValue * 0.2;
if (abs($newValue - $this->currentValue) > $maxChange) {
$newValue = $this->currentValue + ($newValue > $this->currentValue ? 1 : -1) * $maxChange;
}
// 3. 执行调整
$this->performAdjustment($newValue);
$this->lastAdjustTime = time();
$this->currentValue = $newValue;
return true;
}
}
| 运行模式 | 调整方式 | 响应速度 | 适用场景 |
|---|---|---|---|
| PHP-FPM | 重载配置 | 秒级 | 传统Web应用 |
| Swoole/Workerman | API动态设置 | 毫秒级 | 高性能服务 |
| 消息队列 | 增减消费者 | 秒级 | 异步处理 |
| 配置中心 | 热加载 | 分钟级 | 微服务架构 |
推荐方案:
- 新项目:使用 Swoole,原生支持动态调整
- 老旧项目:使用 FPM + 配置管理,配合Supervisor控制进程数
- 队列消费:使用 PID控制算法 平滑调整消费者数量