Hyperf 协程框架入门

定位

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 提供了协程化版本:

用途不能用要用
MySQLPDO、mysqliHyperfDBDB(基于协程 MySQL 客户端)
Redisredis 扩展HyperfRedisRedis
HTTPcurl、file_get_contentsHyperfGuzzleClientFactory(协程版 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,再换框架。 顺序反了,付出的是几周的迁移成本加上长期的维护负担。

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇