PHP 异步编程进阶面试手册

38 阅读9分钟

PHP 异步编程进阶手册

技术栈: Swoole + Hyperf
文档类型: 进阶实战 + 性能优化
更新时间: 2025-10-26


目录

  1. 异步编程基础
  2. 协程vs线程vs进程
  3. 并发模型对比
  4. 高性能架构设计
  5. 性能优化实战
  6. 常见陷阱与解决方案
  7. 分布式系统设计
  8. 监控与调试
  9. 实战案例
  10. 必考题

1. 异步编程基础

1.1 同步 vs 异步

同步阻塞模型(传统PHP-FPM)
// 传统同步阻塞代码
function getUserOrders(int $userId)
{
    // 查询用户信息(阻塞100ms)
    $user = $db->query("SELECT * FROM users WHERE id = {$userId}");
    
    // 查询订单列表(阻塞200ms)
    $orders = $db->query("SELECT * FROM orders WHERE user_id = {$userId}");
    
    // 查询商品信息(阻塞150ms)
    $products = $db->query("SELECT * FROM products WHERE id IN (...)");
    
    return [
        'user' => $user,
        'orders' => $orders,
        'products' => $products
    ];
}

// 总耗时: 100ms + 200ms + 150ms = 450ms
异步非阻塞模型(Swoole协程)
use Swoole\Coroutine;

function getUserOrders(int $userId)
{
    $result = [];
    
    // 并发执行3个查询
    Coroutine\run(function () use ($userId, &$result) {
        // 创建3个协程并发执行
        go(function () use ($userId, &$result) {
            $result['user'] = $db->query("SELECT * FROM users WHERE id = {$userId}");
        });
        
        go(function () use ($userId, &$result) {
            $result['orders'] = $db->query("SELECT * FROM orders WHERE user_id = {$userId}");
        });
        
        go(function () use ($userId, &$result) {
            $result['products'] = $db->query("SELECT * FROM products WHERE id IN (...)");
        });
    });
    
    return $result;
}

// 总耗时: max(100ms, 200ms, 150ms) = 200ms
// 性能提升: 450ms → 200ms (2.25倍)

1.2 异步编程模型

回调模型(Callback)
// Node.js风格的回调
asyncOperation(function ($result) {
    // 回调地狱
    anotherOperation($result, function ($result2) {
        yetAnotherOperation($result2, function ($result3) {
            finalOperation($result3, function ($final) {
                echo $final;
            });
        });
    });
});

缺点: 回调地狱、错误处理困难、代码可读性差

Promise模型
// JavaScript Promise风格
asyncOperation()
    ->then(function ($result) {
        return anotherOperation($result);
    })
    ->then(function ($result2) {
        return yetAnotherOperation($result2);
    })
    ->catch(function ($error) {
        handleError($error);
    });
协程模型(推荐)
use Swoole\Coroutine;

// 同步代码风格,异步执行效果
function asyncFlow()
{
    try {
        $result1 = asyncOperation();        // 自动挂起/恢复
        $result2 = anotherOperation($result1);
        $result3 = yetAnotherOperation($result2);
        return finalOperation($result3);
    } catch (Exception $e) {
        handleError($e);
    }
}

优点:

  • 代码可读性强(同步写法)
  • 性能高(异步执行)
  • 错误处理简单(try-catch)

2. 协程vs线程vs进程

2.1 底层对比

维度进程线程协程
调度者操作系统操作系统用户程序
切换开销最大(1-2μs)中等(0.5-1μs)最小(0.2μs)
内存占用大(10-30MB)中(1-2MB)小(2-8KB)
并发数量几十到几百几百到几千几万到几十万
通信方式IPC(管道/共享内存)共享内存Channel/Context
创建销毁较快极快
适用场景多核CPU计算IO密集型高并发IO

2.2 代码对比

多进程实现(1000并发)
use Swoole\Process;

$processPool = [];

// 创建1000个子进程(不现实)
for ($i = 0; $i < 1000; $i++) {
    $process = new Process(function () use ($i) {
        // 执行任务
        echo "进程{$i}执行\n";
        sleep(1);
    });
    
    $processPool[] = $process->start();
}

// 等待所有进程结束
foreach ($processPool as $pid) {
    Process::wait(true);
}

// 资源消耗: 1000 * 20MB = 20GB 内存 ❌
多线程实现(1000并发)
// PHP没有原生线程支持,需要pthreads扩展
class WorkerThread extends Thread
{
    private $taskId;
    
    public function __construct(int $taskId)
    {
        $this->taskId = $taskId;
    }
    
    public function run()
    {
        echo "线程{$this->taskId}执行\n";
        sleep(1);
    }
}

$threads = [];
for ($i = 0; $i < 1000; $i++) {
    $threads[] = new WorkerThread($i);
    $threads[$i]->start();
}

foreach ($threads as $thread) {
    $thread->join();
}

// 资源消耗: 1000 * 2MB = 2GB 内存 ⚠️
协程实现(1000并发)✅
use Swoole\Coroutine;

Coroutine\run(function () {
    for ($i = 0; $i < 1000; $i++) {
        go(function () use ($i) {
            echo "协程{$i}执行\n";
            Coroutine::sleep(1);
        });
    }
});

// 资源消耗: 1000 * 4KB = 4MB 内存 ✅
// 创建速度: 0.01秒 ✅

2.3 并发性能测试

use Swoole\Coroutine;
use Swoole\Runtime;

// 启用协程运行时Hook
Runtime::enableCoroutine();

/**
 * 性能测试: 10000次HTTP请求
 */

// 测试1: 同步阻塞(PHP-FPM)
$start = microtime(true);
for ($i = 0; $i < 10000; $i++) {
    file_get_contents('http://api.example.com/data');
}
$time1 = microtime(true) - $start;
echo "同步阻塞耗时: {$time1}秒\n";
// 预计: 10000 * 0.1s = 1000秒 (16分钟)

// 测试2: 协程并发(Swoole)
$start = microtime(true);
Coroutine\run(function () {
    for ($i = 0; $i < 10000; $i++) {
        go(function () {
            file_get_contents('http://api.example.com/data');
        });
    }
});
$time2 = microtime(true) - $start;
echo "协程并发耗时: {$time2}秒\n";
// 实际: ~5秒 (200倍性能提升)

echo "性能提升: " . round($time1 / $time2) . "倍\n";

3. 并发模型对比

3.1 Apache MPM (多进程模型)

                ┌─────────────┐
                │   Apache    │
                └──────┬──────┘
                       │
         ┌─────────────┼─────────────┐
         │             │             │
    ┌────▼────┐   ┌───▼────┐   ┌───▼────┐
    │Process 1│   │Process2│   │Process3│
    │PHP-FPM  │   │PHP-FPM │   │PHP-FPM │
    └─────────┘   └────────┘   └────────┘

并发能力: 100-200 (受内存限制)
内存占用: 30-50MB/进程
响应时间: 50-200ms
适用场景: 低并发、简单应用

3.2 Nginx + PHP-FPM (进程池模型)

    ┌─────────┐
    │  Nginx  │ (事件驱动)
    └────┬────┘
         │ FastCGI
    ┌────▼──────────────┐
    │   PHP-FPM Pool    │
    ├───────────────────┤
    │ Worker 1 (Idle)   │
    │ Worker 2 (Busy)   │
    │ Worker 3 (Busy)   │
    │ Worker 4 (Idle)   │
    └───────────────────┘

并发能力: 500-1000
内存占用: 20-40MB/进程
响应时间: 20-100ms
适用场景: 中等并发、传统Web应用

3.3 Node.js (单线程事件循环)

    ┌────────────────────────┐
    │   Event Loop (单线程)   │
    │  ┌──────────────────┐  │
    │  │  Callback Queue  │  │
    │  └──────────────────┘  │
    └────────┬───────────────┘
             │
    ┌────────▼────────┐
    │  Thread Pool    │
    │  (libuv)        │
    │  IO Operations  │
    └─────────────────┘

并发能力: 10000+
内存占用: 10-50MB (总计)
响应时间: 5-50ms
适用场景: 高并发IO密集型

3.4 Swoole (多进程+协程)

    ┌─────────────────────────────┐
    │      Master Process         │
    └──────────────┬──────────────┘
                   │
    ┌──────────────▼──────────────┐
    │      Manager Process        │
    └──────────────┬──────────────┘
                   │
         ┌─────────┼─────────┐
         │         │         │
    ┌────▼───┐ ┌──▼────┐ ┌──▼────┐
    │Worker 1│ │Worker2│ │Worker3│
    │        │ │       │ │       │
    │ ┌────┐ │ │┌────┐│ │┌────┐ │
    │ │Co 1│ │ ││Co 1││ ││Co 1│ │
    │ │Co 2│ │ ││Co 2││ ││Co 2│ │
    │ │...││ │ ││...│││ ││...││ │
    │ │Co N│ │ ││Co N││ ││Co N│ │
    │ └────┘ │ │└────┘│ │└────┘ │
    └────────┘ └──────┘ └───────┘

并发能力: 100万+
内存占用: 50-200MB (总计)
响应时间: 1-10ms
适用场景: 超高并发、实时应用

3.5 性能对比表

框架/模式QPS并发连接内存占用CPU使用率响应时间
Apache+PHP5002008GB80%200ms
Nginx+FPM200010002GB60%50ms
Node.js1000010000100MB70%10ms
Swoole50000100000200MB80%2ms
Hyperf60000100000300MB85%1.5ms

测试环境: 4核8G服务器,简单JSON接口


4. 高性能架构设计

4.1 微服务架构

┌─────────────────────────────────────────────────┐
│                  API Gateway                    │
│            (Hyperf + Swoole HTTP)               │
│  - 路由转发                                      │
│  - 负载均衡                                      │
│  - 限流熔断                                      │
│  - 认证鉴权                                      │
└───────────────────┬─────────────────────────────┘
                    │
        ┌───────────┼───────────┐
        │           │           │
┌───────▼──────┐ ┌─▼─────────┐ ┌▼──────────────┐
│ User Service │ │Order Srv  │ │Product Srv    │
│ (Hyperf)     │ │(Hyperf)   │ │(Hyperf)       │
└───────┬──────┘ └─┬─────────┘ └┬──────────────┘
        │           │            │
        └───────────┼────────────┘
                    │
        ┌───────────▼───────────┐
        │   Service Registry    │
        │   (Consul/Nacos)      │
        └───────────────────────┘
API网关实现
namespace App\Gateway;

use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\RequestMapping;
use Hyperf\LoadBalancer\LoadBalancerInterface;
use Hyperf\ServiceGovernance\DriverInterface;

#[Controller(prefix: '/gateway')]
class ApiGateway
{
    #[Inject]
    private LoadBalancerInterface $loadBalancer;
    
    #[Inject]
    private DriverInterface $serviceRegistry;
    
    #[RequestMapping(path: 'user/{action}', methods: 'get,post')]
    public function user(string $action)
    {
        // 1. 服务发现
        $services = $this->serviceRegistry->getNodes('user-service');
        
        // 2. 负载均衡
        $node = $this->loadBalancer->select($services);
        
        // 3. 限流检查
        if (!$this->rateLimiter->attempt($node['host'])) {
            return ['error' => 'Too many requests'];
        }
        
        // 4. 熔断检查
        if ($this->circuitBreaker->isOpen($node['host'])) {
            return ['error' => 'Service unavailable'];
        }
        
        // 5. 转发请求
        try {
            $response = $this->forwardRequest($node, $action);
            $this->circuitBreaker->recordSuccess($node['host']);
            return $response;
        } catch (\Exception $e) {
            $this->circuitBreaker->recordFailure($node['host']);
            throw $e;
        }
    }
    
    private function forwardRequest(array $node, string $action)
    {
        $client = new \Hyperf\Guzzle\ClientFactory();
        return $client->create()->get("{$node['host']}:{$node['port']}/{$action}");
    }
}

4.2 RPC架构

JSON-RPC实现
namespace App\JsonRpc;

use Hyperf\RpcServer\Annotation\RpcService;

/**
 * 服务端
 */
#[RpcService(name: 'UserService', protocol: 'jsonrpc-http', server: 'jsonrpc-http')]
class UserService implements UserServiceInterface
{
    public function getUserInfo(int $userId): array
    {
        // 查询数据库
        return [
            'id' => $userId,
            'name' => 'John Doe',
            'email' => 'john@example.com'
        ];
    }
    
    public function createUser(array $data): int
    {
        // 创建用户
        return $db->insert('users', $data);
    }
}

/**
 * 客户端调用
 */
class OrderService
{
    #[Inject]
    private UserServiceInterface $userService;
    
    public function createOrder(int $userId, array $products)
    {
        // RPC调用获取用户信息
        $user = $this->userService->getUserInfo($userId);
        
        if (!$user) {
            throw new \Exception('User not found');
        }
        
        // 创建订单逻辑
        // ...
    }
}
gRPC实现(高性能)
// user.proto
syntax = "proto3";

package user;

service UserService {
    rpc GetUser(GetUserRequest) returns (UserResponse) {}
    rpc CreateUser(CreateUserRequest) returns (UserResponse) {}
}

message GetUserRequest {
    int32 user_id = 1;
}

message UserResponse {
    int32 id = 1;
    string name = 2;
    string email = 3;
}
namespace App\Grpc;

use Hyperf\GrpcServer\Annotation\GrpcService;

#[GrpcService(name: 'user.UserService')]
class UserService
{
    public function GetUser(GetUserRequest $request): UserResponse
    {
        $userId = $request->getUserId();
        
        // 查询用户
        $user = $this->findUser($userId);
        
        $response = new UserResponse();
        $response->setId($user['id']);
        $response->setName($user['name']);
        $response->setEmail($user['email']);
        
        return $response;
    }
}

4.3 消息队列架构

namespace App\Queue;

use Hyperf\AsyncQueue\Annotation\AsyncQueueMessage;

/**
 * 异步任务
 */
#[AsyncQueueMessage]
class SendEmailJob
{
    public $to;
    public $subject;
    public $body;
    
    public function __construct(string $to, string $subject, string $body)
    {
        $this->to = $to;
        $this->subject = $subject;
        $this->body = $body;
    }
}

/**
 * 消费者
 */
class SendEmailConsumer extends ConsumerMessage
{
    public function consume($data): string
    {
        // 发送邮件
        $this->mailer->send($data->to, $data->subject, $data->body);
        
        return Result::ACK;
    }
}

/**
 * 生产者
 */
class OrderService
{
    #[Inject]
    private AsyncQueueDriver $queue;
    
    public function createOrder(array $orderData)
    {
        // 创建订单
        $orderId = $this->saveOrder($orderData);
        
        // 异步发送邮件
        $this->queue->push(new SendEmailJob(
            $orderData['email'],
            '订单创建成功',
            "您的订单#{$orderId}已创建"
        ));
        
        return $orderId;
    }
}

5. 性能优化实战

5.1 数据库优化

连接池配置
// config/autoload/databases.php
return [
    'default' => [
        'driver' => 'mysql',
        'host' => 'localhost',
        'port' => 3306,
        'database' => 'test',
        'username' => 'root',
        'password' => '',
        'charset' => 'utf8mb4',
        'pool' => [
            'min_connections' => 10,     // 最小连接数
            'max_connections' => 100,    // 最大连接数
            'connect_timeout' => 10.0,   // 连接超时
            'wait_timeout' => 3.0,       // 等待超时
            'heartbeat' => 60,           // 心跳间隔
            'max_idle_time' => 60,       // 最大空闲时间
        ],
    ],
];
批量查询优化
namespace App\Service;

class ProductService
{
    /**
     * ❌ 错误:N+1查询问题
     */
    public function getBadProducts(array $ids): array
    {
        $products = [];
        foreach ($ids as $id) {
            // 每次循环都查询数据库
            $product = Product::find($id); // 100个ID = 100次查询
            $products[] = $product;
        }
        return $products;
    }
    
    /**
     * ✅ 正确:批量查询
     */
    public function getGoodProducts(array $ids): array
    {
        // 一次查询获取所有数据
        return Product::whereIn('id', $ids)->get(); // 1次查询
    }
    
    /**
     * ✅ 更好:并发查询(数据量大时)
     */
    public function getBestProducts(array $ids): array
    {
        // 分片并发查询
        $chunks = array_chunk($ids, 100); // 每100个一组
        $results = [];
        
        $wg = new \Swoole\Coroutine\WaitGroup();
        
        foreach ($chunks as $chunk) {
            $wg->add();
            go(function () use ($chunk, &$results, $wg) {
                $products = Product::whereIn('id', $chunk)->get();
                $results = array_merge($results, $products->toArray());
                $wg->done();
            });
        }
        
        $wg->wait();
        return $results;
    }
}

5.2 缓存优化

多级缓存架构
namespace App\Service;

use Hyperf\Di\Annotation\Inject;
use Hyperf\Cache\Annotation\Cacheable;
use Psr\SimpleCache\CacheInterface;

class UserService
{
    #[Inject]
    private CacheInterface $cache;
    
    private $localCache = []; // L1: 进程内缓存
    
    /**
     * 三级缓存策略
     */
    public function getUser(int $userId): ?array
    {
        // L1: 进程内缓存(最快)
        if (isset($this->localCache[$userId])) {
            return $this->localCache[$userId];
        }
        
        // L2: Redis缓存(快)
        $cacheKey = "user:{$userId}";
        $user = $this->cache->get($cacheKey);
        
        if ($user !== null) {
            $this->localCache[$userId] = $user; // 回写L1
            return $user;
        }
        
        // L3: 数据库(慢)
        $user = User::find($userId);
        
        if ($user) {
            $userData = $user->toArray();
            
            // 写入L2缓存
            $this->cache->set($cacheKey, $userData, 3600);
            
            // 写入L1缓存
            $this->localCache[$userId] = $userData;
            
            return $userData;
        }
        
        return null;
    }
    
    /**
     * 使用注解自动缓存
     */
    #[Cacheable(prefix: 'user', ttl: 3600, value: '#{userId}')]
    public function getUserWithAnnotation(int $userId): ?array
    {
        return User::find($userId)?->toArray();
    }
}
缓存击穿/雪崩/穿透解决方案
namespace App\Service;

class CacheService
{
    /**
     * 解决缓存击穿:互斥锁
     */
    public function getWithMutex(string $key, callable $callback, int $ttl = 3600)
    {
        $data = $this->cache->get($key);
        
        if ($data !== null) {
            return $data;
        }
        
        // 获取分布式锁
        $lockKey = "lock:{$key}";
        $lock = $this->redis->set($lockKey, 1, ['NX', 'EX' => 10]);
        
        if ($lock) {
            try {
                // 双重检查
                $data = $this->cache->get($key);
                if ($data !== null) {
                    return $data;
                }
                
                // 查询数据库
                $data = $callback();
                
                // 设置缓存
                $this->cache->set($key, $data, $ttl);
                
                return $data;
            } finally {
                $this->redis->del($lockKey);
            }
        } else {
            // 等待锁释放后重试
            usleep(50000); // 等待50ms
            return $this->getWithMutex($key, $callback, $ttl);
        }
    }
    
    /**
     * 解决缓存雪崩:随机过期时间
     */
    public function setWithRandomTTL(string $key, $value, int $baseTTL = 3600)
    {
        // 在基础TTL上增加随机时间(±10%)
        $randomTTL = $baseTTL + rand(-$baseTTL * 0.1, $baseTTL * 0.1);
        $this->cache->set($key, $value, $randomTTL);
    }
    
    /**
     * 解决缓存穿透:布隆过滤器
     */
    public function getWithBloomFilter(string $key, callable $callback)
    {
        // 检查布隆过滤器
        if (!$this->bloomFilter->exists($key)) {
            return null; // 数据不存在,直接返回
        }
        
        $data = $this->cache->get($key);
        
        if ($data === null) {
            $data = $callback();
            
            if ($data === null) {
                // 缓存空值,防止穿透
                $this->cache->set($key, 'NULL', 60);
            } else {
                $this->cache->set($key, $data, 3600);
            }
        }
        
        return $data === 'NULL' ? null : $data;
    }
}

5.3 并发限流

namespace App\Middleware;

use Hyperf\RateLimit\Annotation\RateLimit;

class RateLimitMiddleware
{
    /**
     * 令牌桶算法
     */
    public function tokenBucket(string $key, int $capacity, int $rate)
    {
        $now = time();
        $bucket = $this->redis->hGetAll("bucket:{$key}");
        
        if (empty($bucket)) {
            $bucket = [
                'tokens' => $capacity,
                'last_time' => $now,
            ];
        }
        
        // 计算新增令牌数
        $elapsed = $now - $bucket['last_time'];
        $bucket['tokens'] = min($capacity, $bucket['tokens'] + $elapsed * $rate);
        $bucket['last_time'] = $now;
        
        if ($bucket['tokens'] >= 1) {
            $bucket['tokens'] -= 1;
            $this->redis->hMSet("bucket:{$key}", $bucket);
            return true; // 允许请求
        }
        
        return false; // 拒绝请求
    }
    
    /**
     * 滑动窗口算法
     */
    public function slidingWindow(string $key, int $limit, int $window = 60)
    {
        $now = time();
        $windowStart = $now - $window;
        
        // 删除窗口外的记录
        $this->redis->zRemRangeByScore("window:{$key}", 0, $windowStart);
        
        // 统计窗口内请求数
        $count = $this->redis->zCard("window:{$key}");
        
        if ($count < $limit) {
            // 记录当前请求
            $this->redis->zAdd("window:{$key}", $now, uniqid());
            $this->redis->expire("window:{$key}", $window);
            return true;
        }
        
        return false;
    }
    
    /**
     * 漏桶算法
     */
    public function leakyBucket(string $key, int $capacity, int $rate)
    {
        $now = microtime(true);
        $bucket = $this->redis->hGetAll("leaky:{$key}");
        
        if (empty($bucket)) {
            $bucket = [
                'water' => 0,
                'last_leak' => $now,
            ];
        }
        
        // 计算漏出的水量
        $elapsed = $now - $bucket['last_leak'];
        $bucket['water'] = max(0, $bucket['water'] - $elapsed * $rate);
        $bucket['last_leak'] = $now;
        
        if ($bucket['water'] < $capacity) {
            $bucket['water'] += 1;
            $this->redis->hMSet("leaky:{$key}", $bucket);
            return true;
        }
        
        return false;
    }
}

5.4 性能监控

namespace App\Aspect;

use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;

#[Aspect]
class PerformanceMonitorAspect extends AbstractAspect
{
    public $annotations = [
        \App\Annotation\Monitor::class
    ];
    
    public function process(ProceedingJoinPoint $proceedingJoinPoint)
    {
        $start = microtime(true);
        $startMemory = memory_get_usage();
        
        // 执行方法
        $result = $proceedingJoinPoint->process();
        
        $time = round((microtime(true) - $start) * 1000, 2);
        $memory = round((memory_get_usage() - $startMemory) / 1024 / 1024, 2);
        
        $class = $proceedingJoinPoint->className;
        $method = $proceedingJoinPoint->methodName;
        
        // 记录性能数据
        $this->logger->info("Performance Monitor", [
            'class' => $class,
            'method' => $method,
            'time' => "{$time}ms",
            'memory' => "{$memory}MB",
        ]);
        
        // 慢查询告警
        if ($time > 1000) {
            $this->alert("Slow method: {$class}::{$method} took {$time}ms");
        }
        
        return $result;
    }
}

6. 常见陷阱与解决方案

6.1 协程数据污染

问题代码
// ❌ 错误:全局变量在协程间共享
class UserController
{
    private static $currentUser;
    
    public function action()
    {
        self::$currentUser = $this->auth->user(); // 协程A设置
        
        co::sleep(1); // 协程切换
        
        // 此时$currentUser可能被协程B修改
        $this->logger->info('User: ' . self::$currentUser->name);
    }
}
解决方案
// ✅ 正确:使用协程上下文
use Hyperf\Context\Context;

class UserController
{
    public function action()
    {
        $user = $this->auth->user();
        Context::set('user', $user); // 存储到协程上下文
        
        co::sleep(1);
        
        $user = Context::get('user'); // 从协程上下文获取
        $this->logger->info('User: ' . $user->name);
    }
}

6.2 死锁问题

问题代码
// ❌ 错误:死锁
$channel = new Channel(1);

go(function () use ($channel) {
    $data1 = $channel->pop(); // 等待数据
    $data2 = $channel->pop(); // 永远等待
});

go(function () use ($channel) {
    $channel->push('data1');
    // 没有第二次push,导致死锁
});
解决方案
// ✅ 正确:使用超时
$channel = new Channel(1);

go(function () use ($channel) {
    $data1 = $channel->pop(1.0); // 超时1秒
    if ($data1 === false) {
        echo "超时\n";
        return;
    }
    
    $data2 = $channel->pop(1.0);
    if ($data2 === false) {
        echo "超时\n";
        return;
    }
});

6.3 内存泄漏

问题代码
// ❌ 错误:闭包引用导致内存泄漏
class EventManager
{
    private $listeners = [];
    
    public function on(string $event, callable $callback)
    {
        $this->listeners[$event][] = $callback;
    }
}

// 使用
$manager = new EventManager();
for ($i = 0; $i < 10000; $i++) {
    $largeData = str_repeat('x', 1024 * 1024); // 1MB
    
    $manager->on('test', function () use ($largeData) {
        // 闭包持有$largeData引用,无法释放
    });
}
// 内存占用: 10GB
解决方案
// ✅ 正确:及时释放引用
class EventManager
{
    private $listeners = [];
    
    public function on(string $event, callable $callback)
    {
        $this->listeners[$event][] = $callback;
    }
    
    public function off(string $event)
    {
        unset($this->listeners[$event]); // 及时释放
    }
}

// 或使用WeakMap(PHP 8.0+)
$map = new WeakMap();

6.4 阻塞操作

问题代码
// ❌ 错误:阻塞整个进程
go(function () {
    $result = file_get_contents('large_file.txt'); // 阻塞IO
    sleep(10); // 阻塞sleep
    $data = mysql_query('SELECT ...'); // 阻塞查询
});
解决方案
// ✅ 正确:使用协程化API
use Swoole\Coroutine\System;
use Swoole\Coroutine;

go(function () {
    // 协程文件IO
    $result = System::readFile('large_file.txt');
    
    // 协程sleep
    Coroutine::sleep(10);
    
    // 协程MySQL客户端
    $db = new Coroutine\MySQL();
    $db->connect([...]);
    $data = $db->query('SELECT ...');
});

7. 分布式系统设计

7.1 服务注册与发现

namespace App\Service;

use Hyperf\Consul\Agent;

class ServiceRegistry
{
    private $consul;
    
    public function __construct(Agent $consul)
    {
        $this->consul = $consul;
    }
    
    /**
     * 注册服务
     */
    public function register()
    {
        $this->consul->registerService(
            'user-service',
            [
                'ID' => 'user-service-' . gethostname(),
                'Name' => 'user-service',
                'Tags' => ['api', 'v1'],
                'Address' => '192.168.1.100',
                'Port' => 9501,
                'Check' => [
                    'HTTP' => 'http://192.168.1.100:9501/health',
                    'Interval' => '10s',
                    'Timeout' => '1s',
                ],
            ]
        );
    }
    
    /**
     * 服务发现
     */
    public function discover(string $serviceName): array
    {
        $services = $this->consul->services();
        $nodes = [];
        
        foreach ($services as $service) {
            if ($service['Service'] === $serviceName) {
                $nodes[] = [
                    'host' => $service['Address'],
                    'port' => $service['Port'],
                ];
            }
        }
        
        return $nodes;
    }
    
    /**
     * 注销服务
     */
    public function deregister()
    {
        $this->consul->deregisterService('user-service-' . gethostname());
    }
}

7.2 分布式锁

namespace App\Lock;

class RedisLock
{
    private $redis;
    
    /**
     * 获取锁
     */
    public function acquire(string $key, int $ttl = 10): bool
    {
        $token = uniqid();
        $result = $this->redis->set(
            "lock:{$key}",
            $token,
            ['NX', 'EX' => $ttl]
        );
        
        if ($result) {
            // 存储token用于释放
            Context::set("lock_token:{$key}", $token);
            return true;
        }
        
        return false;
    }
    
    /**
     * 释放锁(Lua脚本保证原子性)
     */
    public function release(string $key): bool
    {
        $token = Context::get("lock_token:{$key}");
        
        $script = <<<LUA
        if redis.call("get", KEYS[1]) == ARGV[1] then
            return redis.call("del", KEYS[1])
        else
            return 0
        end
        LUA;
        
        return $this->redis->eval($script, ["lock:{$key}", $token], 1) === 1;
    }
    
    /**
     * 自动续期(看门狗)
     */
    public function renewLock(string $key, int $ttl = 10)
    {
        go(function () use ($key, $ttl) {
            while (true) {
                co::sleep($ttl / 2);
                
                $token = Context::get("lock_token:{$key}");
                if (!$token) {
                    break; // 锁已释放
                }
                
                // 续期
                $script = <<<LUA
                if redis.call("get", KEYS[1]) == ARGV[1] then
                    return redis.call("expire", KEYS[1], ARGV[2])
                else
                    return 0
                end
                LUA;
                
                $this->redis->eval($script, ["lock:{$key}", $token, $ttl], 1);
            }
        });
    }
}

7.3 分布式事务(SAGA模式)

namespace App\Transaction;

class SagaOrchestrator
{
    private $steps = [];
    private $compensations = [];
    
    /**
     * 添加事务步骤
     */
    public function addStep(callable $action, callable $compensation)
    {
        $this->steps[] = $action;
        $this->compensations[] = $compensation;
    }
    
    /**
     * 执行SAGA事务
     */
    public function execute(): bool
    {
        $executedSteps = [];
        
        try {
            foreach ($this->steps as $index => $step) {
                $result = $step();
                $executedSteps[] = $index;
                
                if (!$result) {
                    throw new \Exception("Step {$index} failed");
                }
            }
            
            return true;
        } catch (\Exception $e) {
            // 补偿(回滚)
            $this->compensate($executedSteps);
            return false;
        }
    }
    
    /**
     * 补偿操作
     */
    private function compensate(array $executedSteps)
    {
        // 逆序执行补偿
        foreach (array_reverse($executedSteps) as $index) {
            try {
                $this->compensations[$index]();
            } catch (\Exception $e) {
                // 记录补偿失败
                $this->logger->error("Compensation {$index} failed: " . $e->getMessage());
            }
        }
    }
}

// 使用示例:订单创建
$saga = new SagaOrchestrator();

// 步骤1: 扣减库存
$saga->addStep(
    function () {
        return $this->inventoryService->decrease($productId, $quantity);
    },
    function () {
        $this->inventoryService->increase($productId, $quantity);
    }
);

// 步骤2: 扣减余额
$saga->addStep(
    function () {
        return $this->accountService->deduct($userId, $amount);
    },
    function () {
        $this->accountService->refund($userId, $amount);
    }
);

// 步骤3: 创建订单
$saga->addStep(
    function () {
        return $this->orderService->create($orderData);
    },
    function () {
        $this->orderService->cancel($orderId);
    }
);

$success = $saga->execute();

8. 监控与调试

8.1 性能分析

namespace App\Command;

use Hyperf\Command\Command;
use Swoole\Coroutine;

class ProfileCommand extends Command
{
    protected $signature = 'profile:run';
    
    public function handle()
    {
        // 开启协程追踪
        Coroutine::set(['trace_flags' => SWOOLE_TRACE_ALL]);
        
        // 记录开始状态
        $startTime = microtime(true);
        $startMemory = memory_get_usage();
        $startCoroutines = Coroutine::stats()['coroutine_num'];
        
        // 执行测试
        $this->runTest();
        
        // 输出统计
        $this->info('执行时间: ' . round((microtime(true) - $startTime) * 1000, 2) . 'ms');
        $this->info('内存占用: ' . round((memory_get_usage() - $startMemory) / 1024 / 1024, 2) . 'MB');
        $this->info('协程数量: ' . (Coroutine::stats()['coroutine_num'] - $startCoroutines));
        
        // 协程统计
        $stats = Coroutine::stats();
        $this->table(['指标', '值'], [
            ['当前协程数', $stats['coroutine_num']],
            ['协程峰值', $stats['coroutine_peak_num']],
            ['事件数', $stats['event_num']],
            ['信号监听', $stats['signal_listener_num']],
        ]);
    }
    
    private function runTest()
    {
        // 测试代码
    }
}

8.2 链路追踪

namespace App\Middleware;

use Hyperf\Di\Annotation\Inject;
use Zipkin\Tracer;
use Zipkin\Propagation\TraceContext;

class TracingMiddleware
{
    #[Inject]
    private Tracer $tracer;
    
    public function process($request, $handler)
    {
        // 创建Span
        $span = $this->tracer->nextSpan();
        $span->setName($request->getUri()->getPath());
        $span->start();
        
        // 添加标签
        $span->tag('http.method', $request->getMethod());
        $span->tag('http.url', (string) $request->getUri());
        
        try {
            $response = $handler->handle($request);
            $span->tag('http.status_code', $response->getStatusCode());
            return $response;
        } catch (\Exception $e) {
            $span->tag('error', $e->getMessage());
            throw $e;
        } finally {
            $span->finish();
        }
    }
}

8.3 日志系统

namespace App\Kernel;

use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Formatter\JsonFormatter;

class LoggerFactory
{
    public static function create(string $name = 'app'): Logger
    {
        $logger = new Logger($name);
        
        // 控制台输出
        $consoleHandler = new StreamHandler('php://stdout', Logger::DEBUG);
        $logger->pushHandler($consoleHandler);
        
        // 文件日志(按天轮转)
        $fileHandler = new RotatingFileHandler(
            BASE_PATH . '/runtime/logs/app.log',
            30, // 保留30天
            Logger::INFO
        );
        $fileHandler->setFormatter(new JsonFormatter());
        $logger->pushHandler($fileHandler);
        
        // 错误日志
        $errorHandler = new StreamHandler(
            BASE_PATH . '/runtime/logs/error.log',
            Logger::ERROR
        );
        $logger->pushHandler($errorHandler);
        
        // 处理器:添加上下文信息
        $logger->pushProcessor(function ($record) {
            $record['extra']['request_id'] = Context::get('request_id');
            $record['extra']['user_id'] = Context::get('user_id');
            $record['extra']['coroutine_id'] = Coroutine::getCid();
            return $record;
        });
        
        return $logger;
    }
}

9. 实战案例

9.1 高并发秒杀系统

namespace App\Service;

use Hyperf\Di\Annotation\Inject;
use Hyperf\Redis\Redis;

class SeckillService
{
    #[Inject]
    private Redis $redis;
    
    /**
     * 秒杀抢购
     */
    public function seckill(int $userId, int $productId): bool
    {
        $stockKey = "seckill:stock:{$productId}";
        $orderKey = "seckill:order:{$productId}";
        
        // Lua脚本保证原子性
        $script = <<<LUA
        -- 检查库存
        local stock = redis.call('get', KEYS[1])
        if not stock or tonumber(stock) <= 0 then
            return 0
        end
        
        -- 检查是否已购买
        if redis.call('sismember', KEYS[2], ARGV[1]) == 1 then
            return -1
        end
        
        -- 扣减库存
        redis.call('decr', KEYS[1])
        
        -- 记录订单
        redis.call('sadd', KEYS[2], ARGV[1])
        
        return 1
        LUA;
        
        $result = $this->redis->eval(
            $script,
            [$stockKey, $orderKey, $userId],
            2
        );
        
        if ($result === 1) {
            // 异步创建订单
            $this->queue->push(new CreateOrderJob($userId, $productId));
            return true;
        } elseif ($result === 0) {
            throw new \Exception('库存不足');
        } else {
            throw new \Exception('已经购买过了');
        }
    }
    
    /**
     * 初始化秒杀
     */
    public function initSeckill(int $productId, int $stock)
    {
        $stockKey = "seckill:stock:{$productId}";
        $this->redis->set($stockKey, $stock);
        $this->redis->expire($stockKey, 3600);
    }
}

9.2 实时数据推送

namespace App\Controller;

use Hyperf\WebSocketServer\Annotation\WebSocketController;
use Hyperf\WebSocketServer\Annotation\OnOpen;
use Hyperf\WebSocketServer\Annotation\OnMessage;
use Hyperf\WebSocketServer\Annotation\OnClose;

#[WebSocketController]
class PushController
{
    private $connections = [];
    
    #[OnOpen]
    public function onOpen(int $fd, Swoole\Http\Request $request)
    {
        // 解析token
        $token = $request->get['token'] ?? '';
        $userId = $this->auth->parseToken($token);
        
        if (!$userId) {
            $this->server->close($fd);
            return;
        }
        
        // 保存连接
        $this->connections[$fd] = $userId;
        
        // 订阅用户频道
        $this->redis->subscribe(["user:{$userId}"], function ($redis, $channel, $message) use ($fd) {
            $this->server->push($fd, $message);
        });
    }
    
    #[OnMessage]
    public function onMessage(int $fd, Swoole\WebSocket\Frame $frame)
    {
        $data = json_decode($frame->data, true);
        
        switch ($data['type']) {
            case 'ping':
                $this->server->push($fd, json_encode(['type' => 'pong']));
                break;
                
            case 'subscribe':
                // 订阅频道
                $channel = $data['channel'];
                $this->redis->subscribe([$channel], function ($redis, $chan, $msg) use ($fd) {
                    $this->server->push($fd, $msg);
                });
                break;
        }
    }
    
    #[OnClose]
    public function onClose(int $fd)
    {
        unset($this->connections[$fd]);
    }
    
    /**
     * 推送消息给指定用户
     */
    public function pushToUser(int $userId, array $data)
    {
        $this->redis->publish("user:{$userId}", json_encode($data));
    }
    
    /**
     * 广播消息
     */
    public function broadcast(array $data)
    {
        foreach ($this->connections as $fd => $userId) {
            $this->server->push($fd, json_encode($data));
        }
    }
}

9.3 分布式爬虫

namespace App\Service;

use Hyperf\Guzzle\ClientFactory;
use Symfony\Component\DomCrawler\Crawler;

class DistributedSpider
{
    private $queue;
    private $visited = [];
    private $maxConcurrency = 100;
    
    public function __construct()
    {
        $this->queue = new \Swoole\Coroutine\Channel(10000);
    }
    
    /**
     * 启动爬虫
     */
    public function start(string $seedUrl)
    {
        $this->queue->push($seedUrl);
        
        // 启动消费者协程
        for ($i = 0; $i < $this->maxConcurrency; $i++) {
            go(function () {
                $this->consume();
            });
        }
        
        // 等待队列清空
        while ($this->queue->length() > 0) {
            co::sleep(1);
        }
    }
    
    /**
     * 消费URL
     */
    private function consume()
    {
        while (true) {
            $url = $this->queue->pop(1.0);
            
            if ($url === false) {
                continue;
            }
            
            if (isset($this->visited[$url])) {
                continue;
            }
            
            $this->visited[$url] = true;
            
            try {
                $html = $this->fetch($url);
                $this->parse($html, $url);
            } catch (\Exception $e) {
                $this->logger->error("爬取失败: {$url}, 错误: {$e->getMessage()}");
            }
        }
    }
    
    /**
     * 获取页面
     */
    private function fetch(string $url): string
    {
        $client = $this->clientFactory->create();
        $response = $client->get($url, [
            'timeout' => 10,
            'headers' => [
                'User-Agent' => 'Mozilla/5.0 ...',
            ],
        ]);
        
        return $response->getBody()->getContents();
    }
    
    /**
     * 解析页面
     */
    private function parse(string $html, string $baseUrl)
    {
        $crawler = new Crawler($html, $baseUrl);
        
        // 提取数据
        $data = $crawler->filter('.product')->each(function (Crawler $node) {
            return [
                'title' => $node->filter('.title')->text(),
                'price' => $node->filter('.price')->text(),
                'url' => $node->filter('a')->attr('href'),
            ];
        });
        
        // 保存数据
        $this->saveData($data);
        
        // 提取链接
        $links = $crawler->filter('a')->each(function (Crawler $node) {
            return $node->attr('href');
        });
        
        // 加入队列
        foreach ($links as $link) {
            if (!isset($this->visited[$link])) {
                $this->queue->push($link);
            }
        }
    }
    
    private function saveData(array $data)
    {
        // 批量插入数据库
        DB::table('products')->insert($data);
    }
}

10. 必考题

Q1: 解释Swoole的多进程模型

A: Swoole采用Multi-Reactor + Multi-Worker的多进程架构:

  1. Master进程:

    • 主进程,负责监听端口
    • 管理Reactor线程和Worker进程
    • 不处理业务逻辑
  2. Manager进程:

    • 管理Worker/Task进程
    • 监控进程状态,异常退出时自动拉起
  3. Reactor线程:

    • 负责网络IO事件监听
    • 接收连接、接收数据、发送数据
    • 将数据分发给Worker进程
  4. Worker进程:

    • 处理业务逻辑
    • 可以创建大量协程
    • 多个Worker可以并行处理
  5. Task进程:

    • 处理异步任务
    • 不阻塞Worker进程

优势:

  • 充分利用多核CPU
  • 进程隔离,某个Worker崩溃不影响其他
  • Reactor负责IO,Worker负责计算,职责分离

Q2: Hyperf相比传统框架有什么优势?

A:

维度LaravelHyperf
启动方式每次请求重新加载常驻内存
并发模型同步阻塞协程异步
QPS500-100050000+
内存占用30-50MB/请求共享200MB
数据库连接每次创建连接池复用
适用场景传统Web高并发、微服务

核心优势:

  1. 性能: 协程+连接池,QPS提升50-100倍
  2. 资源利用: 常驻内存,减少重复加载
  3. 微服务: 内置RPC、服务注册、熔断降级
  4. 开发体验: 注解驱动、AOP、依赖注入

Q3: 如何处理协程中的异常?

A:

// 方案1: try-catch
go(function () {
    try {
        $result = riskyOperation();
    } catch (\Exception $e) {
        $this->logger->error($e->getMessage());
    }
});

// 方案2: 全局异常处理器
use Hyperf\ExceptionHandler\ExceptionHandlerDispatcher;

class CoRoutineExceptionHandler extends ExceptionHandler
{
    public function handle(Throwable $throwable, ResponseInterface $response)
    {
        $this->logger->error($throwable->getMessage());
        return $response->withStatus(500);
    }
}

// 方案3: defer延迟调用
go(function () {
    defer(function () {
        // 协程结束时清理资源
        if ($error = error_get_last()) {
            $this->logger->error($error['message']);
        }
    });
    
    riskyOperation();
});

Q4: 如何实现优雅的服务重启?

A:

// 1. 监听SIGUSR1信号
Process::signal(SIGUSR1, function () use ($server) {
    echo "收到重启信号\n";
    
    // 停止接收新连接
    $server->shutdown();
    
    // 等待现有连接处理完毕
    Timer::after(5000, function () {
        exit(0);
    });
});

// 2. Worker进程优雅退出
$server->on('workerStop', function (Server $server, int $workerId) {
    // 关闭数据库连接
    DB::disconnect();
    
    // 清理资源
    Cache::flush();
});

// 3. 使用reload命令(推荐)
// php bin/hyperf.php server:reload

流程:

  1. 发送reload信号
  2. Manager进程逐个重启Worker
  3. 旧Worker处理完当前请求后退出
  4. 新Worker启动并接收请求
  5. 整个过程对用户无感知

Q5: 如何设计一个百万并发的系统?

A:

架构设计:

                    ┌──────────┐
                    │   CDN    │
                    └────┬─────┘
                         │
                    ┌────▼─────┐
                    │  Nginx   │ (负载均衡)
                    └────┬─────┘
                         │
          ┌──────────────┼──────────────┐
          │              │              │
     ┌────▼────┐    ┌───▼────┐    ┌───▼────┐
     │ Hyperf#1│    │Hyperf#2│    │Hyperf#3│
     └────┬────┘    └───┬────┘    └───┬────┘
          │             │             │
          └─────────────┼─────────────┘
                        │
        ┌───────────────┼───────────────┐
        │               │               │
   ┌────▼────┐     ┌───▼───┐     ┌────▼────┐
   │  Redis  │     │ MySQL │     │  MQ     │
   │ (缓存)  │     │(主从) │     │(削峰)   │
   └─────────┘     └───────┘     └─────────┘

关键技术:

  1. 负载均衡: Nginx + Keepalived
  2. 缓存策略: CDN + Redis多级缓存
  3. 数据库: 读写分离 + 分库分表
  4. 消息队列: 削峰填谷
  5. 限流降级: 令牌桶 + 熔断器
  6. 服务治理: 微服务拆分 + 服务注册

性能指标:

  • QPS: 100万+
  • 响应时间: P99 < 100ms
  • 可用性: 99.99%

总结

核心要点

  1. 异步编程: 理解协程原理,掌握异步编程模式
  2. 性能优化: 连接池、缓存、并发控制
  3. 架构设计: 微服务、分布式、高可用
  4. 问题排查: 监控、日志、链路追踪
  5. 实战经验: 秒杀、推送、爬虫等场景

学习建议

  1. 理论基础: 操作系统、网络协议、数据结构
  2. 实践项目: 从简单HTTP服务器到复杂微服务
  3. 源码阅读: Swoole、Hyperf核心代码
  4. 性能测试: 压测、性能分析、优化
  5. 生产实践: 部署、监控、故障处理

推荐资源


祝您成功! 🎉