定位
Hyperf 是基于 Swoole/Swow 的协程框架,常驻内存。国内高并发场景(秒杀、IM、网关)用得不少。
它和传统 PHP 框架的根本区别:进程不会在请求结束后退出,所以「初始化一次、复用多次」成为可能,速度大幅提升——但也意味着所有全局状态都会在请求之间残留。
安装
# 需要先装 Swoole 扩展
pecl install swoole
1. php.ini 里启用
echo "extension=swoole.so" >> /etc/php.ini
1. 创建项目
composer create-project hyperf/hyperf-skeleton hyperf-demo
cd hyperf-demo
php bin/hyperf.php start
最小示例
注解路由
Hyperf 用注解(PHP 8 的 Attribute)定义路由:
<?php
namespace AppController;
use HyperfHttpServerAnnotationAutoController;
use HyperfHttpServerContractRequestInterface;
use HyperfHttpServerContractResponseInterface;
#[AutoController]
class UserController
{
// GET /user/index
public function index(ResponseInterface $response)
{
return $response->json(['users' => []]);
}
// GET /user/read?id=1
public function read(RequestInterface $request)
{
return ['id' => $request->input('id')];
}
}
注解路由需要 php bin/hyperf.php vendor:publish hyperf/translation 之类的前置配置,也可以用传统的方式在 config/routes.php 里注册。
依赖注入
<?php
namespace AppService;
use HyperfContractConfigInterface;
class UserService
{
// 构造器注入,容器自动解析
public function __construct(private ConfigInterface $config) {}
public function find(int $id): array
{
return ['id' => $id, 'name' => 'demo'];
}
}
核心概念
1. 协程 ≠ 多线程
Swoole 的协程是单线程内协作式调度。遇到 IO(数据库、HTTP、文件)时让出执行权,不是并行计算。
// 并发执行 3 个请求,总耗时约等于最慢的那个
[$a, $b, $c] = parallel([
fn() => $client->get('https://api.a.com'),
fn() => $client->get('https://api.b.com'),
fn() => $client->get('https://api.c.com'),
]);
这是协程最大的价值:IO 密集场景下用同步写法获得异步性能。
2. 必须用协程化的客户端
这是最容易踩的坑。 传统 PHP 扩展(PDO、mysqli、file_get_contents、curl)是阻塞的,在协程里调用会堵死整个进程。
Hyperf 提供了协程化版本:
| 用途 | 不能用 | 要用 |
|---|---|---|
| MySQL | PDO、mysqli | HyperfDBDB(基于协程 MySQL 客户端) |
| Redis | redis 扩展 | HyperfRedisRedis |
| HTTP | curl、file_get_contents | HyperfGuzzleClientFactory(协程版 Guzzle) |
一个 file_get_contents 就能让整个服务的 QPS 掉到个位数,而且不报错,非常难查。
3. 全局状态会残留
class BadService
{
private array $cache = []; // 协程间共享!
public function get(int $id)
{
// 第一个请求写入后,第二个请求会读到别人的数据
return $this->cache[$id] ??= $this->load($id);
}
}
在 FPM 里这是「单次请求的缓存」,在 Hyperf 里是「所有请求共享的缓存」。
解决办法:用 HyperfContextContext 存请求级数据:
use HyperfContextContext;
Context::set('user_id', $id); // 只在当前协程内有效
Context::get('user_id');
常见坑
① 注解路由不生效
需要清缓存:php bin/hyperf.php di:init-proxy 或重启。注解是编译期扫描的,改了不会自动生效。
② 内存泄漏
常驻内存意味着泄漏会累积。常见原因:全局数组只增不减、事件监听器重复注册、协程未回收。要用 php bin/hyperf.php 的监控命令观察内存曲线。
③ 调试困难
协程环境下断点调试不直观(一个进程跑多个协程)。日志要在每条前面加协程 ID 才好追踪。
什么时候选它
- 明确有高并发需求(QPS 上千)
- 大量 IO 密集调用(多服务聚合、爬虫、网关)
- 团队愿意投入学习成本,能接受调试难度上升
不选它的场景:普通业务系统(FPM 完全够用)、团队没有 Swoole 经验、项目对稳定性要求极高而团队不熟悉协程。
一个冷静的判断
先确认你真的需要。
一个 FPM + OPcache + Redis 缓存的站点,单机跑几千 QPS 并不难。协程框架带来的提升是在「每请求初始化开销」上,如果你的瓶颈在 SQL 或外部 API,换框架没有用。
先 profile,再换框架。 顺序反了,付出的是几周的迁移成本加上长期的维护负担。