--- description: "Swoole 协程与高并发编程规范 — 协程安全/连接池/Worker配置/内存管理" globs: - "Case-Database-Backend/app/**/*.php" - "Case-Database-Backend/config/**/*.php" - "Case-Database-Backend/bin/hyperf.php" alwaysApply: false --- # ⚡ Swoole Coroutine & High-Concurrency Standards ## 核心原则 Swoole 是常驻内存的协程服务器,与传统 PHP-FPM 有本质区别。 **每个请求共享 Worker 进程内存**,必须避免全局状态污染。 ## 协程安全规则 ### 绝对禁止 ```php // ❌ 全局变量存储请求数据(会被其他协程覆盖) global $currentUser; // ❌ 静态属性存储请求级数据 class UserContext { public static ?User $user = null; // WRONG: shared across coroutines } // ❌ 阻塞 I/O 函数(会阻塞整个 Worker) file_get_contents('https://api.example.com'); // use Guzzle coroutine client sleep(5); // use Coroutine::sleep() flock($fp, LOCK_EX); // use Redis distributed lock // ❌ 非协程安全的全局单例 $pdo = new PDO(...); // use connection pool instead ``` ### 正确做法 ```php // ✅ 使用 Hyperf Context 传递请求数据 use Hyperf\Context\Context; // 在中间件中设置 Context::set('current_user', $user); // 在任意位置读取(协程安全) $user = Context::get('current_user'); // ✅ 协程安全的延时 use Swoole\Coroutine; Coroutine::sleep(0.1); // ✅ 使用协程 HTTP 客户端 use Hyperf\Guzzle\ClientFactory; $client = $this->clientFactory->create(); $response = $client->get('https://api.example.com'); ``` ## 连接池配置 ### MySQL 连接池 ```php // config/autoload/databases.php 'pool' => [ // 公式: min = Worker数, max = Worker数 * 2 ~ 4 'min_connections' => (int) env('DB_POOL_MIN', 5), 'max_connections' => (int) env('DB_POOL_MAX', 50), 'connect_timeout' => 10.0, // seconds 'wait_timeout' => 3.0, // wait for available connection 'heartbeat' => -1, // disable heartbeat 'max_idle_time' => 60, // recycle idle connections ], ``` ### Redis 连接池 ```php // config/autoload/redis.php return [ 'default' => [ 'host' => env('REDIS_HOST', 'localhost'), 'port' => (int) env('REDIS_PORT', 6379), 'auth' => env('REDIS_AUTH', null), 'db' => (int) env('REDIS_DB', 0), 'pool' => [ 'min_connections' => 5, 'max_connections' => 30, 'connect_timeout' => 10.0, 'wait_timeout' => 3.0, 'heartbeat' => -1, 'max_idle_time' => 60, ], ], ]; ``` ### 连接池容量计算 ``` 单 Worker 最大协程数: max_coroutine (default: 100000) 实际并发协程数 ≈ QPS * avg_response_time DB Pool Max = ceil(concurrent_coroutines * db_query_ratio) Redis Pool Max = ceil(concurrent_coroutines * redis_query_ratio) 示例 (百万级): - 4 Worker, 每 Worker 1000 并发协程 - DB 查询占比 60% → DB Pool Max = 1000 * 0.6 = 600 (per worker) - 实际受 MySQL max_connections 限制,需配合读写分离 ``` ## Worker 进程配置 ```php // config/autoload/server.php 'settings' => [ 'worker_num' => (int) env('SWOOLE_WORKER_NUM', swoole_cpu_num() * 2), 'task_worker_num' => (int) env('SWOOLE_TASK_WORKER_NUM', 4), 'max_request' => 10000, // restart worker after N requests (prevent memory leak) 'max_coroutine' => 100000, // max coroutines per worker 'enable_coroutine' => true, 'open_tcp_nodelay' => true, 'socket_buffer_size' => 3 * 1024 * 1024, 'buffer_output_size' => 3 * 1024 * 1024, 'package_max_length' => 5 * 1024 * 1024, 'heartbeat_check_interval' => 60, 'heartbeat_idle_time' => 120, ], ``` ## 协程并发控制 ```php use Hyperf\Coroutine\Parallel; // ✅ 并行执行多个无依赖的 I/O 操作 $parallel = new Parallel(10); // max concurrency = 10 $parallel->add(function () { return $this->orderService->getStatistics($orderId); }); $parallel->add(function () { return $this->paymentService->getPayments($orderId); }); $parallel->add(function () { return $this->deliveryService->getDeliveries($orderId); }); [$stats, $payments, $deliveries] = $parallel->wait(); ``` ```php use Hyperf\Coroutine\WaitGroup; // ✅ WaitGroup: wait for all coroutines to complete $wg = new WaitGroup(); $results = []; $wg->add(1); go(function () use ($wg, &$results) { $results['users'] = $this->userService->getOnlineCount(); $wg->done(); }); $wg->add(1); go(function () use ($wg, &$results) { $results['orders'] = $this->orderService->getTodayCount(); $wg->done(); }); $wg->wait(5.0); // timeout 5s ``` ## 内存管理 - `max_request = 10000`: Worker 处理 N 个请求后自动重启,防止内存泄漏 - 避免在全局/静态变量中累积数据 - 大数组处理完后手动 `unset()` - 使用 `memory_get_usage()` 监控内存 - 对象生命周期与请求绑定,不跨请求持有引用 ```php // ✅ 大数据分批处理 ProductionOrder::query() ->where('status', 'pending') ->chunk(500, function ($orders) { foreach ($orders as $order) { $this->processOrder($order); } // chunk 结束后自动释放内存 }); ``` ## 定时任务 ```php use Hyperf\Crontab\Annotation\Crontab; #[Crontab( name: 'CheckPaymentTimeout', rule: '*/5 * * * *', memo: 'Check payment timeout orders', singleton: true, // prevent duplicate execution onOneServer: true, // only run on one server in cluster mutexPool: 'default', )] class CheckPaymentTimeoutTask { public function execute(): void { // batch processing with chunk } } ``` ## 性能监控 ```php // Swoole Server status $server = $this->container->get(ServerInterface::class); $stats = $server->stats(); // Returns: start_time, connection_num, request_count, worker_request_count, coroutine_num // Redis monitor $redis = $this->container->get(RedisFactory::class)->get('default'); $info = $redis->info(); ```