hyperf 底层核心面试手册

2 阅读10分钟

Hyperf 底层核心手册

适用版本: Hyperf 3.1
文档类型: 必备 + 架构设计
更新时间: 2025-10-26


目录

  1. 依赖注入容器
  2. AOP切面编程
  3. 协程上下文
  4. 注解系统
  5. 事件系统
  6. 中间件机制
  7. 数据库连接池
  8. 服务治理
  9. 高频题

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:

维度LaravelHyperf
运行模式PHP-FPM(同步阻塞)Swoole(异步协程)
性能中等高(5-10倍)
并发能力
学习曲线平缓陡峭
生态成熟丰富快速发展
适用场景中小型项目高并发、微服务

Q2: Hyperf如何避免协程数据污染?

A:

  1. 使用Context存储请求级数据
  2. 避免使用全局变量
  3. 数据库连接使用连接池
  4. 单例类要注意线程安全
// ❌ 错误
class UserService
{
    private $currentUser; // 会被所有协程共享
}

// ✅ 正确
class UserService
{
    public function getCurrentUser()
    {
        return Context::get('user');
    }
}

Q3: Hyperf的AOP是如何实现的?

A:

  1. 启动时扫描注解
  2. 生成代理类(继承原类并重写方法)
  3. 容器返回代理类实例
  4. 调用方法时先执行切面逻辑
// 原类
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:

  1. 启用OPcache
  2. 使用连接池
  3. 合理设置Worker数量(CPU核数1-2倍)
  4. 使用协程并发
  5. 启用缓存(Redis/本地缓存)
  6. 异步任务队列
  7. 数据库索引优化

Q5: Hyperf如何实现平滑重启?

A:

# 发送SIGUSR1信号
kill -USR1 $(cat runtime/hyperf.pid)

# 或使用命令
php bin/hyperf.php server:reload

原理:

  1. Manager进程收到信号
  2. 逐个重启Worker进程
  3. 旧Worker处理完当前请求后退出
  4. 新Worker加载新代码
  5. 不影响正在处理的请求

Q6: Hyperf适合什么场景?

✅ 适合:

  1. 高并发API服务
  2. 微服务架构
  3. WebSocket实时通信
  4. RPC服务
  5. 消息队列消费

❌ 不适合:

  1. 低并发简单项目(用Laravel更简单)
  2. CPU密集型任务
  3. 需要大量PHP扩展的遗留项目

10. 总结

核心要点

  1. 容器: PSR-11依赖注入、接口绑定
  2. AOP: 代理类、切面链
  3. Context: 协程隔离、请求级数据
  4. 注解: 声明式编程、元数据驱动
  5. 事件: 解耦业务逻辑
  6. 中间件: 洋葱模型、请求处理
  7. 连接池: 资源复用、性能优化
  8. 服务治理: 注册发现、熔断降级

学习路径

  1. 入门: HTTP服务器、依赖注入、注解
  2. 进阶: AOP、事件、中间件、连接池
  3. 高级: 微服务、服务治理、性能优化

推荐资源


建议:

  • 理解协程原理和注意事项
  • 掌握依赖注入和AOP机制
  • 了解与传统框架的区别
  • 准备微服务架构案例
  • 关注性能优化和最佳实践