本文目录导读:

在 PHP 中预留并发处理能力,主要取决于你的部署架构和业务场景,以下我按从简单到复杂、从单机到分布式的顺序,给出完整的预留方案和代码示例。
预留并发的基础认知
PHP 的并发能力受限于 PHP-FPM 进程数 和 数据库连接池,预留并发不是简单的加 Swoole,而是架构层面的设计。
按架构层级预留并发
传统 PHP-FPM 架构(最容易预留)
核心思想:进程隔离 + 队列削峰
// config/fpm_pool.conf 预留策略 pm = dynamic pm.max_children = 100 // 预留最大并发进程数 pm.start_servers = 20 // 初始进程数 pm.min_spare_servers = 10 // 最小空闲进程(预留响应能力) pm.max_spare_servers = 30 // 最大空闲进程 pm.max_requests = 500 // 防止内存泄漏
代码层面预留并发(使用 Redis 队列异步化)
<?php
// 1. 同步请求 → 转异步队列,预留并发处理能力
function handleHighConcurrency($requestData) {
// 将任务推入 Redis 队列,由 Worker 进程消费
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->lPush('task_queue', json_encode($requestData));
// 立即返回给用户,不阻塞等待
return response()->json(['status' => 'accepted']);
}
// 2. 独立的 Worker 进程(需要额外进程池)
// worker.php
while (true) {
$task = $redis->brPop('task_queue', 0);
processTask($task);
}
预留数据库并发(最常见的瓶颈)
方案:连接池 + 读写分离预分配
<?php
// 使用 PDO 预留连接池(单进程内复用)
class DBConnectionPool {
private array $pool = [];
private int $maxSize = 50; // 预留 50 个连接
public function getConnection(): PDO {
if (empty($this->pool)) {
$pdo = new PDO("mysql:host=localhost;dbname=test", "user", "pass");
$pdo->setAttribute(PDO::ATTR_PERSISTENT, true); // 持久连接
return $pdo;
}
return array_pop($this->pool);
}
public function release(PDO $pdo): void {
if (count($this->pool) < $this->maxSize) {
$this->pool[] = $pdo;
}
}
}
// 业务代码中预留并发处理
function getProductDetail($productId) {
try {
$pdo = DBConnectionPool::getInstance()->getConnection();
// 预留:将高频查询放入 Redis 缓存,减少数据库并发
$cacheKey = "product:{$productId}";
$redis = Redis::getInstance();
if ($cached = $redis->get($cacheKey)) {
return json_decode($cached, true);
}
// 数据库查询
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
$stmt->execute([$productId]);
$data = $stmt->fetch(PDO::FETCH_ASSOC);
// 回填缓存
$redis->setex($cacheKey, 3600, json_encode($data));
DBConnectionPool::getInstance()->release($pdo);
return $data;
} catch (PDOException $e) {
// 预留降级逻辑
return ['error' => 'system busy'];
}
}
高并发预留(使用 Swoole 常驻内存)
<?php
// server.php - 预留并发能力的关键
use Swoole\Http\Server;
use Swoole\Http\Request;
use Swoole\Http\Response;
// 预分配 Worker 进程数(建议 = CPU核心数 × 2)
$server = new Server("0.0.0.0", 9501);
$server->set([
'worker_num' => 32, // 预留 32 个 Worker 进程
'max_request' => 10000, // 内存清理
'task_worker_num' => 16, // 预留异步任务 Worker
'backlog' => 256 // 连接队列长度预留
]);
// 预留协程并发处理
$server->on('request', function (Request $req, Response $resp) {
// 使用协程处理,不阻塞 Worker
go(function () use ($req, $resp) {
// 业务逻辑
$data = ['status' => 'ok', 'time' => time()];
$resp->header('Content-Type', 'application/json');
$resp->end(json_encode($data));
});
});
// 预留 MySQL 连接池
$pool = new Swoole\Coroutine\Channel(100); // 预留 100 个协程连接
for ($i = 0; $i < 100; $i++) {
$mysql = new Swoole\Coroutine\MySQL();
$mysql->connect([
'host' => '127.0.0.1',
'user' => 'root',
'password' => 'pass',
'database' => 'test',
]);
$pool->push($mysql);
}
$server->start();
全链路限流预留(防止雪崩)
<?php
// 1. 令牌桶算法 - 预留稳定的处理能力
class TokenBucket {
private int $capacity; // 桶容量(最大预留值)
private float $tokens; // 当前令牌数
private float $refillRate; // 补充速率(每秒)
private float $lastRefill; // 上次补充时间
public function __construct(int $capacity = 1000, float $refillRate = 100) {
$this->capacity = $capacity;
$this->tokens = $capacity;
$this->refillRate = $refillRate;
$this->lastRefill = microtime(true);
}
public function tryAcquire(): bool {
$now = microtime(true);
$this->tokens = min($this->capacity,
$this->tokens + ($now - $this->lastRefill) * $this->refillRate);
$this->lastRefill = $now;
if ($this->tokens >= 1) {
$this->tokens -= 1;
return true;
}
return false;
}
}
// 2. 使用信号量预留(进程内并发控制)
$semaphore = sem_get(12345, 10); // 预留 10 个并发槽位
if (sem_acquire($semaphore)) {
try {
// 临界区业务
processRequest();
} finally {
sem_release($semaphore); // 释放槽位
}
}
预留并发的三种经典模式
消峰填谷(异步队列)
用户请求 → 快速写入消息队列 → 立即返回成功 → 后台 Worker 慢速处理
// 预留实现
$mq = new RabbitMQ();
$mq->declareQueue('order_queue');
$mq->publish(json_encode($orderData)); // 1ms 完成
// 立即返回,真正处理由 10 个 Worker 消费
流量控制(滑动窗口)
class SlidingWindowLimiter {
private Redis $redis;
private string $key;
private int $windowSize; // 窗口大小
private int $maxRequests; // 允许最大请求数
public function allow(): bool {
$now = microtime(true);
$key = "rate_limit:{$this->key}:window";
$this->redis->multi()
->zRemRangeByScore($key, 0, $now - $this->windowSize)
->zAdd($key, $now, uniqid())
->zCard($key)
->expire($key, $this->windowSize + 1);
$result = $this->redis->exec();
return $result[2] <= $this->maxRequests;
}
}
主备降级(超时中断)
// 设置超时时间,预留快速失败能力
set_time_limit(3); // 3秒超时
try {
$response = file_get_contents('http://external-api.com/data', false,
stream_context_create(['http' => ['timeout' => 2]]));
} catch (Exception $e) {
// 预留降级数据
$response = getFromCacheBackup();
}
预留并发的配置清单
PHP-FPM 配置(php.ini)
; 预留项目 memory_limit = 256M ; 数据量预留 max_execution_time = 30 ; 超时预留 max_input_vars = 3000 ; 参数预留 upload_max_filesize = 20M post_max_size = 25M
Nginx 代理配置
# 预留连接数 worker_connections 1024; # 预留超时设置 fastcgi_connect_timeout 30; fastcgi_send_timeout 300; fastcgi_read_timeout 300; # 预留缓冲 fastcgi_buffers 8 16k; fastcgi_buffer_size 32k;
数据库配置预留
-- 预留连接 SET GLOBAL max_connections = 500; SET GLOBAL wait_timeout = 60; SET GLOBAL interactive_timeout = 60; -- 预留复杂查询 SET SESSION sort_buffer_size = 2M; SET SESSION join_buffer_size = 2M;
预留并发的高级技巧
预生成数据缓冲
// 提前将热点数据放入内存
$hotData = apcu_fetch('hot_data');
if (false === $hotData) {
$hotData = databaseCalculateHotData();
apcu_store('hot_data', $hotData, 60); // 缓存 60 秒
}
// 直接使用,无需查询数据库
使用 Redis 分布式锁预留资源
$lock = new RedisLock($redis, 'user:123:update');
if ($lock->acquire(3)) { // 预留 3 秒锁
try {
// 业务处理
} finally {
$lock->release();
}
}
优雅降级(Circuit Breaker)
class CircuitBreaker {
public function call(callable $func) {
$circuit = apcu_fetch('circuit:key');
if ($circuit == 'open') {
return fallback(); // 快速失败
}
try {
$result = $func();
apcu_store('circuit:key', 'closed', 10);
return $result;
} catch (Exception $e) {
apcu_store('circuit:key', 'open', 5); // 熔断 5 秒
return fallback();
}
}
}
预留并发的监控指标
预留后必须监控这些指标来调整:
# 查看 FPM 进程 php-fpm -tt netstat -an | grep :9000 | grep ESTABLISHED | wc -l # 查看队列积压 redis-cli llen task_queue # 查看连接池 mysqladmin -u root -p status | grep Threads # PHP 错误日志 tail -f /var/log/php-fpm.log
预留并发的核心原则:
- 资源预分配:在部署时提前分配好进程、连接、内存,而不是等爆了才扩容
- 异步解耦:将重任务推入队列,让用户立即得到响应
- 限流降级:提前设定阈值,超过就快速失败返回错误
- 缓存兜底:核心数据必须有多级缓存
- 监控预留:提前埋点,每分钟查看哪些能力达到 80% 以上
根据你的实际架构选择:
- 小项目:PHP-FPM + Redis 队列 就够了
- 中型项目:Nginx + PHP-FPM + Swoole Worker
- 大型分布式:需要消息队列 (Kafka) + 分布式限流 (Redis + Lua)
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播