初始化

This commit is contained in:
2026-03-05 21:27:11 +08:00
commit 130de0fd5d
140 changed files with 21972 additions and 0 deletions

View File

@@ -0,0 +1,232 @@
---
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();
```