Hyperf 底层核心手册
适用版本: Hyperf 3.1
文档类型: 必备 + 架构设计
更新时间: 2025-10-26
目录
1. 依赖注入容器
1.1 容器原理
核心概念: Hyperf基于PSR-11实现的依赖注入容器,支持构造函数注入、属性注入、方法注入。
容器架构
┌─────────────────────────────────────┐
│ Application │
│ ┌───────────────────────────────┐ │
│ │ Container (PSR-11) │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Definition Storage │ │ │
│ │ │ - Class Bindings │ │ │
│ │ │ - Singleton Instances │ │ │
│ │ │ - Factory Closures │ │ │
│ │ └─────────────────────────┘ │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Annotation Scanner │ │ │
│ │ └─────────────────────────┘ │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Proxy Generator │ │ │
│ │ └─────────────────────────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
1.2 依赖注入方式
构造函数注入(推荐)
namespace App\Service;
use App\Repository\UserRepository;
class UserService
{
private UserRepository $repository;
// 自动注入依赖
public function __construct(UserRepository $repository)
{
$this->repository = $repository;
}
public function getUser(int $id)
{
return $this->repository->find($id);
}
}
属性注入
namespace App\Controller;
use Hyperf\Di\Annotation\Inject;
use App\Service\UserService;
class UserController
{
#[Inject]
private UserService $userService;
public function show(int $id)
{
return $this->userService->getUser($id);
}
}
手动获取
use Hyperf\Context\ApplicationContext;
// 从容器获取实例
$container = ApplicationContext::getContainer();
$userService = $container->get(UserService::class);
1.3 作用域管理
use Hyperf\Di\Annotation\Inject;
class ExampleService
{
// 每次注入都创建新实例(非单例)
#[Inject(lazy: false)]
private NonSingletonService $service;
// 延迟加载(默认)
#[Inject(lazy: true)]
private LazyService $lazyService;
}
1.4 接口绑定
配置文件: config/autoload/dependencies.php
return [
// 接口绑定到实现类
\App\Contract\UserServiceInterface::class => \App\Service\UserService::class,
// 绑定到工厂
\App\Contract\CacheInterface::class => \App\Factory\CacheFactory::class,
// 单例绑定
'cache' => function () {
return new \Redis();
},
];
工厂模式:
namespace App\Factory;
use Hyperf\Contract\ConfigInterface;
class CacheFactory
{
public function __invoke(ConfigInterface $config)
{
$driver = $config->get('cache.driver');
return match($driver) {
'redis' => new \Redis(),
'memcached' => new \Memcached(),
default => throw new \Exception('Unsupported cache driver'),
};
}
}
1.5 容器进阶
循环依赖检测
// Hyperf会自动检测并抛出异常
class ServiceA
{
public function __construct(ServiceB $serviceB) {}
}
class ServiceB
{
public function __construct(ServiceA $serviceA) {} // 循环依赖!
}
// 解决方案1: 使用Setter注入
class ServiceA
{
private ServiceB $serviceB;
public function setServiceB(ServiceB $serviceB)
{
$this->serviceB = $serviceB;
}
}
// 解决方案2: 使用延迟代理
class ServiceA
{
#[Inject(lazy: true)]
private ServiceB $serviceB;
}
条件绑定
use Hyperf\Contract\ContainerInterface;
// 根据环境绑定不同实现
return [
LoggerInterface::class => function (ContainerInterface $container) {
$env = env('APP_ENV');
return match($env) {
'production' => new ProductionLogger(),
'testing' => new TestLogger(),
default => new DevLogger(),
};
},
];
2. AOP切面编程
2.1 AOP原理
实现机制: Hyperf通过动态生成代理类实现AOP,在类加载时织入切面逻辑。
原始类 (UserService)
↓
注解扫描器
↓
代理类生成器
↓
UserService_Proxy (包含切面逻辑)
↓
容器返回代理类实例
2.2 切面定义
namespace App\Aspect;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
#[Aspect]
class LogAspect extends AbstractAspect
{
// 要拦截的类
public array $classes = [
\App\Service\UserService::class,
];
// 要拦截的注解
public array $annotations = [
\App\Annotation\Log::class,
];
// 执行优先级(数字越小越优先)
public int $priority = 0;
public function process(ProceedingJoinPoint $proceedingJoinPoint)
{
// 前置逻辑
$startTime = microtime(true);
$className = $proceedingJoinPoint->className;
$methodName = $proceedingJoinPoint->methodName;
$arguments = $proceedingJoinPoint->arguments;
echo "[Before] {$className}::{$methodName}\n";
try {
// 执行原方法
$result = $proceedingJoinPoint->process();
// 后置逻辑
$duration = microtime(true) - $startTime;
echo "[After] 耗时: {$duration}s\n";
return $result;
} catch (\Throwable $e) {
// 异常处理
echo "[Exception] {$e->getMessage()}\n";
throw $e;
}
}
}
2.3 常用切面场景
性能监控
#[Aspect]
class PerformanceAspect extends AbstractAspect
{
public array $annotations = [
\App\Annotation\Monitor::class,
];
public function process(ProceedingJoinPoint $proceedingJoinPoint)
{
$startTime = microtime(true);
$startMemory = memory_get_usage();
$result = $proceedingJoinPoint->process();
$duration = microtime(true) - $startTime;
$memoryUsed = memory_get_usage() - $startMemory;
// 上报到监控系统
$this->report([
'method' => $proceedingJoinPoint->className . '::' . $proceedingJoinPoint->methodName,
'duration' => $duration,
'memory' => $memoryUsed,
]);
return $result;
}
}
// 使用
class UserService
{
#[Monitor]
public function getUser(int $id)
{
// ...
}
}
缓存切面
#[Aspect]
class CacheAspect extends AbstractAspect
{
#[Inject]
private CacheInterface $cache;
public array $annotations = [
\App\Annotation\Cacheable::class,
];
public function process(ProceedingJoinPoint $proceedingJoinPoint)
{
// 获取注解
$metadata = $proceedingJoinPoint->getAnnotationMetadata();
$cacheable = $metadata->method[\App\Annotation\Cacheable::class] ?? null;
if (!$cacheable) {
return $proceedingJoinPoint->process();
}
// 生成缓存键
$key = $this->generateKey($proceedingJoinPoint, $cacheable);
// 尝试从缓存获取
$cached = $this->cache->get($key);
if ($cached !== null) {
return unserialize($cached);
}
// 执行方法并缓存结果
$result = $proceedingJoinPoint->process();
$this->cache->set($key, serialize($result), $cacheable->ttl);
return $result;
}
private function generateKey(ProceedingJoinPoint $point, $annotation): string
{
$prefix = $annotation->prefix ?? 'cache';
$className = $point->className;
$methodName = $point->methodName;
$args = md5(serialize($point->arguments));
return "{$prefix}:{$className}:{$methodName}:{$args}";
}
}
// 注解定义
#[Attribute(Attribute::TARGET_METHOD)]
class Cacheable
{
public function __construct(
public int $ttl = 3600,
public string $prefix = 'cache'
) {}
}
// 使用
class UserService
{
#[Cacheable(ttl: 1800, prefix: 'user')]
public function getUser(int $id)
{
return Db::table('users')->find($id);
}
}
权限验证
#[Aspect]
class AuthAspect extends AbstractAspect
{
public array $annotations = [
\App\Annotation\RequirePermission::class,
];
public function process(ProceedingJoinPoint $proceedingJoinPoint)
{
$metadata = $proceedingJoinPoint->getAnnotationMetadata();
$permission = $metadata->method[\App\Annotation\RequirePermission::class];
// 获取当前用户
$user = Context::get('user');
// 验证权限
if (!$user || !$user->hasPermission($permission->value)) {
throw new \Exception('Permission denied');
}
return $proceedingJoinPoint->process();
}
}
// 使用
class AdminController
{
#[RequirePermission('admin.users.delete')]
public function deleteUser(int $id)
{
// ...
}
}
2.4 切面链调用
// 优先级:1 -> 2 -> 3
#[Aspect(priority: 1)]
class LogAspect extends AbstractAspect { }
#[Aspect(priority: 2)]
class CacheAspect extends AbstractAspect { }
#[Aspect(priority: 3)]
class AuthAspect extends AbstractAspect { }
// 执行顺序:
// LogAspect(before)
// -> CacheAspect(before)
// -> AuthAspect(before)
// -> 原方法
// -> AuthAspect(after)
// -> CacheAspect(after)
// -> LogAspect(after)
3. 协程上下文
3.1 Context原理
核心问题: 在协程环境下,全局变量会被所有协程共享,导致数据错乱。
// ❌ 错误:全局变量在协程间共享
$userId = null;
go(function () {
global $userId;
$userId = 1001;
co::sleep(1);
echo $userId; // 可能被其他协程修改为1002
});
go(function () {
global $userId;
$userId = 1002;
});
解决方案: Hyperf的Context基于协程ID隔离数据。
use Hyperf\Context\Context;
go(function () {
Context::set('user_id', 1001);
co::sleep(1);
echo Context::get('user_id'); // 始终是1001
});
go(function () {
Context::set('user_id', 1002);
co::sleep(1);
echo Context::get('user_id'); // 始终是1002
});
3.2 Context API
use Hyperf\Context\Context;
// 设置值
Context::set('key', 'value');
// 获取值
$value = Context::get('key');
// 获取值(带默认值)
$value = Context::get('key', 'default');
// 判断是否存在
$exists = Context::has('key');
// 删除
Context::destroy('key');
// 复制上下文到新协程
$data = Context::copy();
go(function () use ($data) {
Context::set($data);
});
// 覆盖上下文
Context::override('key', 'new_value');
// 获取所有数据
$all = Context::getContainer();
3.3 请求级上下文
use Hyperf\Context\Context;
use Hyperf\HttpServer\Contract\RequestInterface;
class UserMiddleware
{
public function process($request, $handler)
{
// 从请求头获取用户信息
$token = $request->header('Authorization');
$user = $this->auth->verify($token);
// 存储到上下文
Context::set('user', $user);
Context::set('request_id', uniqid());
Context::set('request_time', microtime(true));
$response = $handler->handle($request);
// 记录日志
$duration = microtime(true) - Context::get('request_time');
$this->logger->info('Request completed', [
'request_id' => Context::get('request_id'),
'user_id' => $user->id,
'duration' => $duration,
]);
return $response;
}
}
// 在任意地方获取当前用户
class UserService
{
public function getCurrentUser()
{
return Context::get('user');
}
}
3.4 协程上下文传递
use Hyperf\Context\Context;
use Hyperf\Coroutine\Parallel;
class ParallelService
{
public function process()
{
// 当前用户信息
$user = Context::get('user');
$parallel = new Parallel();
// 子协程1
$parallel->add(function () use ($user) {
// 手动传递上下文
Context::set('user', $user);
// 业务逻辑
return $this->task1();
});
// 子协程2
$parallel->add(function () use ($user) {
Context::set('user', $user);
return $this->task2();
});
return $parallel->wait();
}
}
// 自动传递上下文的封装
class ContextParallel
{
public static function run(array $tasks)
{
$context = Context::getContainer();
$parallel = new Parallel();
foreach ($tasks as $task) {
$parallel->add(function () use ($task, $context) {
// 自动恢复上下文
foreach ($context as $key => $value) {
Context::set($key, $value);
}
return $task();
});
}
return $parallel->wait();
}
}
// 使用
$results = ContextParallel::run([
fn() => $this->fetchUser(),
fn() => $this->fetchOrders(),
fn() => $this->fetchProducts(),
]);
3.5 常见陷阱
// ❌ 陷阱1: 在协程外访问上下文
$user = Context::get('user'); // 可能为null(非协程环境)
// ✅ 正确:判断是否在协程中
if (\Hyperf\Coroutine\Coroutine::inCoroutine()) {
$user = Context::get('user');
}
// ❌ 陷阱2: 忘记在子协程中传递上下文
go(function () {
$user = Context::get('user'); // null!
});
// ✅ 正确:使用Hyperf的协程工具
use Hyperf\Coroutine\Coroutine;
Coroutine::create(function () {
$user = Context::get('user'); // 自动继承父协程上下文
});
// ❌ 陷阱3: 上下文数据过大
Context::set('large_data', $veryLargeArray); // 内存占用高
// ✅ 正确:只存储必要的数据
Context::set('user_id', $user->id); // 而不是整个User对象
4. 注解系统
4.1 注解原理
扫描流程:
1. 启动时扫描所有类
↓
2. 解析类/方法/属性的注解
↓
3. 收集到AnnotationReader
↓
4. 根据注解生成元数据
↓
5. 注册到容器/路由/监听器等
4.2 内置注解
依赖注入
use Hyperf\Di\Annotation\Inject;
class UserController
{
#[Inject]
private UserService $userService;
#[Inject(lazy: true)]
private LogService $logService; // 延迟加载
}
路由注解
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;
use Hyperf\HttpServer\Annotation\PostMapping;
use Hyperf\HttpServer\Annotation\Middleware;
#[Controller(prefix: '/api/users')]
#[Middleware(\App\Middleware\AuthMiddleware::class)]
class UserController
{
#[GetMapping(path: '')]
public function index()
{
return ['users' => []];
}
#[GetMapping(path: '/{id}')]
public function show(int $id)
{
return ['user' => ['id' => $id]];
}
#[PostMapping(path: '')]
public function store(UserRequest $request)
{
return ['created' => true];
}
}
异步任务
use Hyperf\AsyncQueue\Annotation\AsyncQueueMessage;
class NotificationService
{
#[AsyncQueueMessage]
public function sendEmail(string $to, string $subject, string $body)
{
// 自动投递到异步队列
mail($to, $subject, $body);
}
}
// 调用时自动异步执行
$notificationService->sendEmail('user@example.com', 'Welcome', 'Hello!');
缓存注解
use Hyperf\Cache\Annotation\Cacheable;
use Hyperf\Cache\Annotation\CachePut;
use Hyperf\Cache\Annotation\CacheEvict;
class UserService
{
// 缓存查询结果
#[Cacheable(prefix: 'user', ttl: 3600, value: '_#{id}')]
public function getUser(int $id)
{
return Db::table('users')->find($id);
}
// 更新缓存
#[CachePut(prefix: 'user', value: '_#{user.id}')]
public function updateUser(User $user)
{
Db::table('users')->where('id', $user->id)->update($user->toArray());
return $user;
}
// 删除缓存
#[CacheEvict(prefix: 'user', value: '_#{id}')]
public function deleteUser(int $id)
{
Db::table('users')->delete($id);
}
}
4.3 自定义注解
namespace App\Annotation;
use Attribute;
use Hyperf\Di\Annotation\AbstractAnnotation;
// 1. 定义注解
#[Attribute(Attribute::TARGET_METHOD)]
class RateLimit extends AbstractAnnotation
{
public function __construct(
public int $limit = 100, // 限流次数
public int $window = 60, // 时间窗口(秒)
public string $key = '' // 限流键
) {}
}
// 2. 创建切面处理注解
namespace App\Aspect;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use Hyperf\Redis\Redis;
#[Aspect]
class RateLimitAspect extends AbstractAspect
{
public array $annotations = [
\App\Annotation\RateLimit::class,
];
#[Inject]
private Redis $redis;
public function process(ProceedingJoinPoint $proceedingJoinPoint)
{
$metadata = $proceedingJoinPoint->getAnnotationMetadata();
$rateLimit = $metadata->method[\App\Annotation\RateLimit::class];
// 生成限流键
$key = $rateLimit->key ?: $this->generateKey($proceedingJoinPoint);
$cacheKey = "rate_limit:{$key}";
// 检查限流
$current = $this->redis->incr($cacheKey);
if ($current == 1) {
$this->redis->expire($cacheKey, $rateLimit->window);
}
if ($current > $rateLimit->limit) {
throw new \Exception('Rate limit exceeded');
}
return $proceedingJoinPoint->process();
}
private function generateKey(ProceedingJoinPoint $point): string
{
// 基于IP + 方法名生成键
$ip = Context::get('request')->getServerParams()['remote_addr'] ?? 'unknown';
$method = $point->className . '::' . $point->methodName;
return md5($ip . $method);
}
}
// 3. 使用注解
class ApiController
{
#[RateLimit(limit: 10, window: 60)]
public function index()
{
return ['message' => 'OK'];
}
}
4.4 注解收集器
namespace App\Annotation;
use Hyperf\Di\Annotation\AbstractAnnotation;
use Hyperf\Di\Annotation\AnnotationCollector;
// 注册注解监听
class CustomAnnotationListener implements ListenerInterface
{
public function listen(): array
{
return [
AfterWorkerStart::class,
];
}
public function process(object $event)
{
// 获取所有带有特定注解的类
$classes = AnnotationCollector::getClassesByAnnotation(MyAnnotation::class);
foreach ($classes as $class => $annotation) {
// 处理注解
echo "Found {$class} with annotation\n";
}
// 获取某个类的所有方法注解
$methods = AnnotationCollector::getClassMethodAnnotation(UserController::class);
foreach ($methods as $method => $annotations) {
foreach ($annotations as $annotation) {
// 处理每个方法的注解
}
}
}
}
5. 事件系统
5.1 事件驱动架构
┌──────────┐ ┌──────────────┐ ┌──────────┐
│ 事件发布 │─────>│ 事件分发器 │─────>│ 监听器1 │
│ Emit │ │ Dispatcher │ └──────────┘
└──────────┘ └──────────────┘ ┌──────────┐
│──────────────>│ 监听器2 │
│ └──────────┘
│ ┌──────────┐
└──────────────>│ 监听器3 │
└──────────┘
5.2 事件定义
namespace App\Event;
class UserRegistered
{
public function __construct(
public int $userId,
public string $email,
public \DateTime $registeredAt
) {}
}
5.3 监听器
namespace App\Listener;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use App\Event\UserRegistered;
#[Listener]
class SendWelcomeEmailListener implements ListenerInterface
{
public function listen(): array
{
// 监听的事件列表
return [
UserRegistered::class,
];
}
public function process(object $event): void
{
if ($event instanceof UserRegistered) {
// 发送欢迎邮件
$this->sendEmail($event->email, 'Welcome!');
}
}
private function sendEmail(string $to, string $subject)
{
// 发送邮件逻辑
echo "Sending email to {$to}: {$subject}\n";
}
}
#[Listener(priority: 1)] // 优先级高
class LogUserRegistrationListener implements ListenerInterface
{
public function listen(): array
{
return [UserRegistered::class];
}
public function process(object $event): void
{
if ($event instanceof UserRegistered) {
// 记录日志
echo "User {$event->userId} registered at {$event->registeredAt->format('Y-m-d H:i:s')}\n";
}
}
}
5.4 事件发布
use Hyperf\Context\ApplicationContext;
use Psr\EventDispatcher\EventDispatcherInterface;
class UserService
{
#[Inject]
private EventDispatcherInterface $eventDispatcher;
public function register(string $email, string $password)
{
// 创建用户
$userId = Db::table('users')->insertGetId([
'email' => $email,
'password' => password_hash($password, PASSWORD_DEFAULT),
'created_at' => date('Y-m-d H:i:s'),
]);
// 发布事件
$this->eventDispatcher->dispatch(new UserRegistered(
userId: $userId,
email: $email,
registeredAt: new \DateTime()
));
return $userId;
}
}
5.5 实战案例:订单流程
// 事件定义
namespace App\Event\Order;
class OrderCreated
{
public function __construct(public Order $order) {}
}
class OrderPaid
{
public function __construct(public Order $order) {}
}
class OrderShipped
{
public function __construct(public Order $order, public string $trackingNumber) {}
}
class OrderCompleted
{
public function __construct(public Order $order) {}
}
// 监听器1: 订单创建后减库存
#[Listener]
class ReduceStockListener implements ListenerInterface
{
public function listen(): array
{
return [OrderCreated::class];
}
public function process(object $event): void
{
foreach ($event->order->items as $item) {
Db::table('products')
->where('id', $item->product_id)
->decrement('stock', $item->quantity);
}
}
}
// 监听器2: 支付成功后发货
#[Listener]
class ShipOrderListener implements ListenerInterface
{
public function listen(): array
{
return [OrderPaid::class];
}
public function process(object $event): void
{
// 自动发货逻辑
$trackingNumber = $this->createShipment($event->order);
// 发布发货事件
$this->eventDispatcher->dispatch(
new OrderShipped($event->order, $trackingNumber)
);
}
}
// 监听器3: 发货后发送通知
#[Listener]
class NotifyShipmentListener implements ListenerInterface
{
public function listen(): array
{
return [OrderShipped::class];
}
public function process(object $event): void
{
// 发送短信/邮件通知
$this->smsService->send(
$event->order->user->phone,
"您的订单已发货,运单号:{$event->trackingNumber}"
);
}
}
// 监听器4: 订单完成后返佣
#[Listener]
class GiveCommissionListener implements ListenerInterface
{
public function listen(): array
{
return [OrderCompleted::class];
}
public function process(object $event): void
{
// 计算并发放佣金
$commission = $event->order->total * 0.05;
Db::table('commissions')->insert([
'user_id' => $event->order->user_id,
'order_id' => $event->order->id,
'amount' => $commission,
'created_at' => date('Y-m-d H:i:s'),
]);
}
}
6. 中间件机制
6.1 洋葱模型
┌──────────────────────────────────────────┐
│ Request │
│ ┌────────────────────────────────────┐ │
│ │ Middleware 1 (before) │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ Middleware 2 (before) │ │ │
│ │ │ ┌────────────────────────┐ │ │ │
│ │ │ │ Controller Logic │ │ │ │
│ │ │ └────────────────────────┘ │ │ │
│ │ │ Middleware 2 (after) │ │ │
│ │ └──────────────────────────────┘ │ │
│ │ Middleware 1 (after) │ │
│ └────────────────────────────────────┘ │
│ Response │
└──────────────────────────────────────────┘
6.2 中间件定义
namespace App\Middleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class CorsMiddleware implements MiddlewareInterface
{
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
// 前置逻辑
$origin = $request->getHeaderLine('Origin');
// 处理OPTIONS预检请求
if ($request->getMethod() === 'OPTIONS') {
return $this->response()
->withHeader('Access-Control-Allow-Origin', $origin)
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
->withStatus(204);
}
// 继续执行
$response = $handler->handle($request);
// 后置逻辑
return $response
->withHeader('Access-Control-Allow-Origin', $origin)
->withHeader('Access-Control-Allow-Credentials', 'true');
}
}
6.3 中间件注册
全局中间件: config/autoload/middlewares.php
return [
'http' => [
\App\Middleware\CorsMiddleware::class,
\App\Middleware\LogMiddleware::class,
],
];
路由中间件:
use Hyperf\HttpServer\Annotation\Middleware;
#[Controller]
#[Middleware(\App\Middleware\AuthMiddleware::class)]
class UserController
{
#[GetMapping(path: '/profile')]
#[Middleware(\App\Middleware\CheckProfileMiddleware::class)]
public function profile()
{
// 先经过AuthMiddleware,再经过CheckProfileMiddleware
}
}
6.4 常用中间件
认证中间件
namespace App\Middleware;
class AuthMiddleware implements MiddlewareInterface
{
#[Inject]
private JwtService $jwt;
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$token = $request->getHeaderLine('Authorization');
if (!$token || !str_starts_with($token, 'Bearer ')) {
return $this->response()->withStatus(401)->json(['error' => 'Unauthorized']);
}
$token = substr($token, 7);
try {
$payload = $this->jwt->verify($token);
// 存储用户信息到上下文
Context::set('user', $payload);
return $handler->handle($request);
} catch (\Exception $e) {
return $this->response()->withStatus(401)->json(['error' => 'Invalid token']);
}
}
}
限流中间件
namespace App\Middleware;
class RateLimitMiddleware implements MiddlewareInterface
{
#[Inject]
private Redis $redis;
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$ip = $request->getServerParams()['remote_addr'] ?? 'unknown';
$key = "rate_limit:{$ip}";
$current = $this->redis->incr($key);
if ($current == 1) {
$this->redis->expire($key, 60);
}
// 每分钟最多100次请求
if ($current > 100) {
return $this->response()
->withStatus(429)
->json(['error' => 'Too many requests']);
}
$response = $handler->handle($request);
// 添加限流信息到响应头
return $response
->withHeader('X-RateLimit-Limit', '100')
->withHeader('X-RateLimit-Remaining', max(0, 100 - $current));
}
}
请求日志中间件
namespace App\Middleware;
class LogMiddleware implements MiddlewareInterface
{
#[Inject]
private LoggerInterface $logger;
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$startTime = microtime(true);
$requestId = uniqid('req_');
Context::set('request_id', $requestId);
// 记录请求
$this->logger->info('Request started', [
'request_id' => $requestId,
'method' => $request->getMethod(),
'uri' => (string) $request->getUri(),
'ip' => $request->getServerParams()['remote_addr'] ?? 'unknown',
]);
$response = $handler->handle($request);
// 记录响应
$duration = microtime(true) - $startTime;
$this->logger->info('Request completed', [
'request_id' => $requestId,
'status' => $response->getStatusCode(),
'duration' => round($duration * 1000, 2) . 'ms',
]);
return $response->withHeader('X-Request-Id', $requestId);
}
}
7. 数据库连接池
7.1 连接池原理
┌─────────────────────────────────────┐
│ Connection Pool │
│ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │
│ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ 5 │ │ (空闲连接)
│ └───┘ └───┘ └───┘ └───┘ └───┘ │
│ │
│ ┌───┐ ┌───┐ ┌───┐ │
│ │ 6 │ │ 7 │ │ 8 │ │ (使用中)
│ └───┘ └───┘ └───┘ │
└─────────────────────────────────────┘
↑ ↓
获取连接 归还连接
7.2 MySQL连接池配置
config/autoload/databases.php:
return [
'default' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', 3306),
'database' => env('DB_DATABASE', 'test'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'pool' => [
'min_connections' => 1, // 最小连接数
'max_connections' => 10, // 最大连接数
'connect_timeout' => 10.0, // 连接超时
'wait_timeout' => 3.0, // 等待超时
'heartbeat' => -1, // 心跳间隔(-1不启用)
'max_idle_time' => 60, // 最大空闲时间
],
],
];
7.3 使用连接池
use Hyperf\DbConnection\Db;
// 自动从连接池获取连接并归还
$users = Db::table('users')->get();
// 事务(保证在同一个连接)
Db::transaction(function () {
Db::table('users')->insert(['name' => 'John']);
Db::table('orders')->insert(['user_id' => 1]);
});
// 手动管理连接
$connection = Db::connection();
try {
$users = $connection->table('users')->get();
} finally {
// 不需要手动归还,协程结束时自动归还
}
7.4 Redis连接池
config/autoload/redis.php:
return [
'default' => [
'host' => env('REDIS_HOST', '127.0.0.1'),
'port' => (int) env('REDIS_PORT', 6379),
'auth' => env('REDIS_AUTH', null),
'db' => (int) env('REDIS_DB', 0),
'pool' => [
'min_connections' => 1,
'max_connections' => 10,
'connect_timeout' => 10.0,
'wait_timeout' => 3.0,
'heartbeat' => -1,
'max_idle_time' => 60,
],
],
];
使用示例:
use Hyperf\Redis\Redis;
#[Inject]
private Redis $redis;
public function cacheUser(int $id)
{
// 自动从连接池获取
$user = $this->redis->get("user:{$id}");
if (!$user) {
$user = Db::table('users')->find($id);
$this->redis->setex("user:{$id}", 3600, json_encode($user));
}
return json_decode($user, true);
}
7.5 连接池监控
use Hyperf\DbConnection\Pool\PoolFactory;
use Hyperf\Di\Annotation\Inject;
class PoolMonitorService
{
#[Inject]
private PoolFactory $poolFactory;
public function getStats(): array
{
$pool = $this->poolFactory->getPool('default');
return [
'current_connections' => $pool->getCurrentConnections(),
'idle_connections' => $pool->getIdleConnections(),
'using_connections' => $pool->getUsingConnections(),
'max_connections' => $pool->getOption()->getMaxConnections(),
];
}
}
8. 服务治理
8.1 服务注册与发现
Consul配置
config/autoload/consul.php:
return [
'uri' => 'http://127.0.0.1:8500',
'token' => '',
'check' => [
'deregister_critical_service_after' => '90m',
'interval' => '1s',
],
];
服务注册
use Hyperf\ServiceGovernance\Annotation\Service;
#[Service(
name: 'UserService',
server: 'jsonrpc-http',
protocol: 'jsonrpc-http',
publishTo: 'consul'
)]
class UserService implements UserServiceInterface
{
public function getUser(int $id): array
{
return Db::table('users')->find($id);
}
}
服务发现与调用
use Hyperf\RpcClient\Annotation\RpcReference;
class OrderService
{
#[RpcReference(
name: 'UserService',
protocol: 'jsonrpc-http',
loadBalancer: 'random'
)]
private UserServiceInterface $userService;
public function createOrder(int $userId, array $items)
{
// 自动通过服务发现调用远程UserService
$user = $this->userService->getUser($userId);
// 创建订单
return Db::table('orders')->insert([
'user_id' => $userId,
'items' => json_encode($items),
]);
}
}
8.2 熔断降级
use Hyperf\CircuitBreaker\Annotation\CircuitBreaker;
class PaymentService
{
#[CircuitBreaker(
timeout: 3.0, // 超时时间
failCounter: 5, // 失败次数阈值
successCounter: 3, // 成功次数恢复
fallback: 'paymentFallback' // 降级方法
)]
public function pay(Order $order): bool
{
// 调用第三方支付接口
$result = $this->thirdPartyPay($order);
return $result;
}
// 降级方法
public function paymentFallback(Order $order): bool
{
// 记录失败订单,稍后重试
Db::table('failed_payments')->insert([
'order_id' => $order->id,
'reason' => 'Circuit breaker open',
'created_at' => date('Y-m-d H:i:s'),
]);
return false;
}
}
8.3 限流
use Hyperf\RateLimit\Annotation\RateLimit;
class ApiController
{
#[RateLimit(
create: 10, // 每秒生成10个令牌
consume: 1, // 每次消费1个令牌
capacity: 100, // 令牌桶容量
key: 'user_#{user.id}' // 限流键(SpEL表达式)
)]
public function index()
{
return ['data' => []];
}
}
8.4 配置中心
// 配置Apollo
return [
'enable' => true,
'server' => 'http://127.0.0.1:8080',
'appid' => 'your-app-id',
'cluster' => 'default',
'namespaces' => ['application'],
'interval' => 5, // 轮询间隔
];
// 使用配置
use Hyperf\Contract\ConfigInterface;
#[Inject]
private ConfigInterface $config;
public function getApiKey()
{
// 自动从Apollo获取最新配置
return $this->config->get('api.key');
}
9. 高频题
Q1: Hyperf与Laravel有什么区别?
A:
| 维度 | Laravel | Hyperf |
|---|---|---|
| 运行模式 | PHP-FPM(同步阻塞) | Swoole(异步协程) |
| 性能 | 中等 | 高(5-10倍) |
| 并发能力 | 低 | 高 |
| 学习曲线 | 平缓 | 陡峭 |
| 生态 | 成熟丰富 | 快速发展 |
| 适用场景 | 中小型项目 | 高并发、微服务 |
Q2: Hyperf如何避免协程数据污染?
A:
- 使用Context存储请求级数据
- 避免使用全局变量
- 数据库连接使用连接池
- 单例类要注意线程安全
// ❌ 错误
class UserService
{
private $currentUser; // 会被所有协程共享
}
// ✅ 正确
class UserService
{
public function getCurrentUser()
{
return Context::get('user');
}
}
Q3: Hyperf的AOP是如何实现的?
A:
- 启动时扫描注解
- 生成代理类(继承原类并重写方法)
- 容器返回代理类实例
- 调用方法时先执行切面逻辑
// 原类
class UserService
{
public function getUser($id) { }
}
// 生成的代理类(简化)
class UserService_Proxy extends UserService
{
public function getUser($id)
{
// 执行前置切面
foreach ($this->aspects as $aspect) {
$aspect->before();
}
// 执行原方法
$result = parent::getUser($id);
// 执行后置切面
foreach ($this->aspects as $aspect) {
$aspect->after();
}
return $result;
}
}
Q4: Hyperf如何优化性能?
A:
- 启用OPcache
- 使用连接池
- 合理设置Worker数量(CPU核数1-2倍)
- 使用协程并发
- 启用缓存(Redis/本地缓存)
- 异步任务队列
- 数据库索引优化
Q5: Hyperf如何实现平滑重启?
A:
# 发送SIGUSR1信号
kill -USR1 $(cat runtime/hyperf.pid)
# 或使用命令
php bin/hyperf.php server:reload
原理:
- Manager进程收到信号
- 逐个重启Worker进程
- 旧Worker处理完当前请求后退出
- 新Worker加载新代码
- 不影响正在处理的请求
Q6: Hyperf适合什么场景?
✅ 适合:
- 高并发API服务
- 微服务架构
- WebSocket实时通信
- RPC服务
- 消息队列消费
❌ 不适合:
- 低并发简单项目(用Laravel更简单)
- CPU密集型任务
- 需要大量PHP扩展的遗留项目
10. 总结
核心要点
- 容器: PSR-11依赖注入、接口绑定
- AOP: 代理类、切面链
- Context: 协程隔离、请求级数据
- 注解: 声明式编程、元数据驱动
- 事件: 解耦业务逻辑
- 中间件: 洋葱模型、请求处理
- 连接池: 资源复用、性能优化
- 服务治理: 注册发现、熔断降级
学习路径
- 入门: HTTP服务器、依赖注入、注解
- 进阶: AOP、事件、中间件、连接池
- 高级: 微服务、服务治理、性能优化
推荐资源
- 官方文档: hyperf.wiki
- GitHub: github.com/hyperf/hype…
- Hyperf社区: hyperf.io
建议:
- 理解协程原理和注意事项
- 掌握依赖注入和AOP机制
- 了解与传统框架的区别
- 准备微服务架构案例
- 关注性能优化和最佳实践