PHP 异步编程进阶手册
技术栈: Swoole + Hyperf
文档类型: 进阶实战 + 性能优化
更新时间: 2025-10-26
目录
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+PHP | 500 | 200 | 8GB | 80% | 200ms |
| Nginx+FPM | 2000 | 1000 | 2GB | 60% | 50ms |
| Node.js | 10000 | 10000 | 100MB | 70% | 10ms |
| Swoole | 50000 | 100000 | 200MB | 80% | 2ms |
| Hyperf | 60000 | 100000 | 300MB | 85% | 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的多进程架构:
-
Master进程:
- 主进程,负责监听端口
- 管理Reactor线程和Worker进程
- 不处理业务逻辑
-
Manager进程:
- 管理Worker/Task进程
- 监控进程状态,异常退出时自动拉起
-
Reactor线程:
- 负责网络IO事件监听
- 接收连接、接收数据、发送数据
- 将数据分发给Worker进程
-
Worker进程:
- 处理业务逻辑
- 可以创建大量协程
- 多个Worker可以并行处理
-
Task进程:
- 处理异步任务
- 不阻塞Worker进程
优势:
- 充分利用多核CPU
- 进程隔离,某个Worker崩溃不影响其他
- Reactor负责IO,Worker负责计算,职责分离
Q2: Hyperf相比传统框架有什么优势?
A:
| 维度 | Laravel | Hyperf |
|---|---|---|
| 启动方式 | 每次请求重新加载 | 常驻内存 |
| 并发模型 | 同步阻塞 | 协程异步 |
| QPS | 500-1000 | 50000+ |
| 内存占用 | 30-50MB/请求 | 共享200MB |
| 数据库连接 | 每次创建 | 连接池复用 |
| 适用场景 | 传统Web | 高并发、微服务 |
核心优势:
- 性能: 协程+连接池,QPS提升50-100倍
- 资源利用: 常驻内存,减少重复加载
- 微服务: 内置RPC、服务注册、熔断降级
- 开发体验: 注解驱动、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
流程:
- 发送reload信号
- Manager进程逐个重启Worker
- 旧Worker处理完当前请求后退出
- 新Worker启动并接收请求
- 整个过程对用户无感知
Q5: 如何设计一个百万并发的系统?
A:
架构设计:
┌──────────┐
│ CDN │
└────┬─────┘
│
┌────▼─────┐
│ Nginx │ (负载均衡)
└────┬─────┘
│
┌──────────────┼──────────────┐
│ │ │
┌────▼────┐ ┌───▼────┐ ┌───▼────┐
│ Hyperf#1│ │Hyperf#2│ │Hyperf#3│
└────┬────┘ └───┬────┘ └───┬────┘
│ │ │
└─────────────┼─────────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌────▼────┐ ┌───▼───┐ ┌────▼────┐
│ Redis │ │ MySQL │ │ MQ │
│ (缓存) │ │(主从) │ │(削峰) │
└─────────┘ └───────┘ └─────────┘
关键技术:
- 负载均衡: Nginx + Keepalived
- 缓存策略: CDN + Redis多级缓存
- 数据库: 读写分离 + 分库分表
- 消息队列: 削峰填谷
- 限流降级: 令牌桶 + 熔断器
- 服务治理: 微服务拆分 + 服务注册
性能指标:
- QPS: 100万+
- 响应时间: P99 < 100ms
- 可用性: 99.99%
总结
核心要点
- 异步编程: 理解协程原理,掌握异步编程模式
- 性能优化: 连接池、缓存、并发控制
- 架构设计: 微服务、分布式、高可用
- 问题排查: 监控、日志、链路追踪
- 实战经验: 秒杀、推送、爬虫等场景
学习建议
- 理论基础: 操作系统、网络协议、数据结构
- 实践项目: 从简单HTTP服务器到复杂微服务
- 源码阅读: Swoole、Hyperf核心代码
- 性能测试: 压测、性能分析、优化
- 生产实践: 部署、监控、故障处理
推荐资源
- Swoole官方文档: wiki.swoole.com
- Hyperf官方文档: hyperf.wiki
- 《深入理解Swoole》
- 《Hyperf微服务实战》
祝您成功! 🎉