PHP Fiber 协程完整最佳实践---一、Fiber 基础原理 传统 PHP:同步阻塞 ┌─────────────────────────────┐ │ 请求1→ 执行 → 等IO → 完成 │ 进程被占用 └─────────────────────────────┘ Fiber 协程:协作式多任务 ┌──────────────────────────────────────┐ │ Fiber1 执行 → 挂起 → Fiber2 执行 │ │ Fiber2 挂起 → Fiber1 恢复 → 完成 │ 同一进程 └──────────────────────────────────────┘---二、Fiber 核心 API<?php// Fiber 生命周期$fiber=newFiber(function():string{echo"1. Fiber 开始\n";$value=Fiber::suspend('第一次挂起');// 挂起,返回值给外部echo"3. 恢复,收到: {$value}\n";Fiber::suspend('第二次挂起');echo"5. 再次恢复\n";return'最终返回值';});$result1=$fiber->start();// 启动,运行到第一个 suspendecho"2. 外部收到: {$result1}\n";$result2=$fiber->resume('hello');// 恢复,传值给 Fiberecho"4. 外部收到: {$result2}\n";$fiber->resume();// 最终恢复echo"6. Fiber 完成: ".$fiber->getReturn()."\n";// 输出:// 1. Fiber 开始// 2. 外部收到: 第一次挂起// 3. 恢复,收到: hello// 4. 外部收到: 第二次挂起// 5. 再次恢复// 6. Fiber 完成: 最终返回值---三、状态检查<?php $fiber=newFiber(function():void{Fiber::suspend();});var_dump($fiber->isStarted());// falsevar_dump($fiber->isSuspended());// falsevar_dump($fiber->isRunning());// falsevar_dump($fiber->isTerminated());// false$fiber->start();var_dump($fiber->isStarted());// truevar_dump($fiber->isSuspended());// truevar_dump($fiber->isTerminated());// false$fiber->resume();var_dump($fiber->isTerminated());// true---四、事件循环(核心模式)<?php// 简单事件循环实现classEventLoop{private array $fibers=[];private array $timers=[];publicfunctionadd(Fiber $fiber):void{$this->fibers[]=$fiber;}publicfunctionaddTimer(float $delay,callable $callback):void{$this->timers[]=['at'=>microtime(true)+$delay,'callback'=>$callback,];}publicfunctionrun():void{// 启动所有 Fiberforeach($this->fibers as $fiber){if(!$fiber->isStarted()){$fiber->start();}}// 循环调度while($this->hasPending()){// 处理定时器$now=microtime(true);foreach($this->timers as $key=>$timer){if($now>=$timer['at']){($timer['callback'])();unset($this->timers[$key]);}}// 恢复挂起的 Fiberforeach($this->fibers as $key=>$fiber){if($fiber->isSuspended()){$fiber->resume();}if($fiber->isTerminated()){unset($this->fibers[$key]);}}usleep(1000);// 1ms 轮询间隔}}privatefunctionhasPending():bool{return!empty($this->fibers)||!empty($this->timers);}}// 使用$loop=newEventLoop();$loop->add(newFiber(function():void{echo"任务A 开始\n";Fiber::suspend();// 模拟等待echo"任务A 完成\n";}));$loop->add(newFiber(function():void{echo"任务B 开始\n";Fiber::suspend();echo"任务B 完成\n";}));$loop->run();// 任务A 开始// 任务B 开始// 任务A 完成// 任务B 完成---五、模拟异步 HTTP 请求<?php// 用 Fiber 模拟并发 HTTP 请求classAsyncHttpClient{private EventLoop $loop;publicfunction__construct(EventLoop $loop){$this->loop=$loop;}publicfunctionget(string $url):Fiber{$fiber=newFiber(function()use($url):string{// 非阻塞 socket$context=stream_context_create(['http'=>['timeout'=>5]]);$host=parse_url($url,PHP_URL_HOST);$path=parse_url($url,PHP_URL_PATH)?:'/';$socket=stream_socket_client("tcp://{$host}:80",$errno,$errstr,5,STREAM_CLIENT_CONNECT|STREAM_CLIENT_ASYNC_CONNECT);stream_set_blocking($socket,false);// 发送请求fwrite($socket,"GET {$path} HTTP/1.1\r\nHost: {$host}\r\nConnection: close\r\n\r\n");// 挂起等待响应Fiber::suspend($socket);// 恢复后读取响应$response='';while(!feof($socket)){$response.=fread($socket,8192);}fclose($socket);return$response;});return$fiber;}}---六、生产级调度器<?php classScheduler{private \SplQueue $ready;private array $suspended=[];publicfunction__construct(){$this->ready=new\SplQueue();}publicfunctionspawn(callable $task):void{$fiber=newFiber($task);$this->ready->enqueue($fiber);}publicfunctionrun():void{while(!$this->ready->isEmpty()){$fiber=$this->ready->dequeue();try{if(!$fiber->isStarted()){$value=$fiber->start();}elseif($fiber->isSuspended()){$value=$fiber->resume();}// 如果还没结束,重新入队if(!$fiber->isTerminated()){$this->ready->enqueue($fiber);}}catch(\Throwable $e){echo"Fiber 异常: ".$e->getMessage()."\n";}}}}// 使用$scheduler=newScheduler();$scheduler->spawn(function():void{for($i=0;$i<3;$i++){echo"任务1: step {$i}\n";Fiber::suspend();// 主动让出控制权}});$scheduler->spawn(function():void{for($i=0;$i<3;$i++){echo"任务2: step {$i}\n";Fiber::suspend();}});$scheduler->run();// 任务1: step 0// 任务2: step 0// 任务1: step 1// 任务2: step 1// 任务1: step 2// 任务2: step 2---七、异常处理<?php $fiber=newFiber(function():void{try{echo"Fiber 运行中\n";Fiber::suspend();echo"Fiber 恢复\n";}catch(\Exception $e){echo"Fiber 内部捕获: ".$e->getMessage()."\n";}});$fiber->start();// 向 Fiber 内部抛出异常$fiber->throw(new\Exception('外部注入的异常'));// 输出:Fiber 内部捕获: 外部注入的异常---八、与 ReactPHP 集成(生产推荐)<?php// composer require react/event-loop react/http react/promiseuse React\EventLoop\Loop;use React\Promise\Promise;// ReactPHP 已内置 Fiber 支持(v3+)$loop=Loop::get();// 将 Promise 转为 Fiber 可等待functionawait(Promise $promise):mixed{$fiber=Fiber::getCurrent();$result=null;$error=null;$promise->then(function($value)use($fiber,&$result):void{$result=$value;$fiber->resume($value);},function($reason)use($fiber,&$error):void{$error=$reason;$fiber->throw($reasoninstanceof\Throwable?$reason:new\RuntimeException((string)$reason));});returnFiber::suspend();}// 使用$fiber=newFiber(function()use($loop):void{// 模拟异步等待$promise=newPromise(function($resolve)use($loop):void{$loop->addTimer(1.0,fn()=>$resolve('数据加载完成'));});$result=await($promise);echo $result."\n";});$fiber->start();$loop->run();---九、数据库异步查询<?php// 用 Fiber 包装同步数据库查询,实现伪并发classAsyncDatabase{private \PDO $pdo;private Scheduler $scheduler;publicfunction__construct(\PDO $pdo,Scheduler $scheduler){$this->pdo=$pdo;$this->scheduler=$scheduler;}publicfunctionquery(string $sql,array $params=[]):mixed{// 挂起当前 Fiber,让其他任务先跑Fiber::suspend();$stmt=$this->pdo->prepare($sql);$stmt->execute($params);return$stmt->fetchAll();}}// 并发查询示例$scheduler=newScheduler();$scheduler->spawn(function()use($db):void{$users=$db->query('SELECT * FROM users LIMIT 10');echo"用户数: ".count($users)."\n";});$scheduler->spawn(function()use($db):void{$orders=$db->query('SELECT * FROM orders LIMIT 10');echo"订单数: ".count($orders)."\n";});$scheduler->run();// 两个查询交替执行---十、Fiber vs 其他方案对比 ┌───────────────┬──────────┬─────────────┬──────────┬────────────┐ │ 特性 │ Fiber │ Swoole 协程 │ ReactPHP │ 传统多进程 │ ├───────────────┼──────────┼─────────────┼──────────┼────────────┤ │ 需要扩展 │ 否 │ 是 │ 否 │ 否 │ ├───────────────┼──────────┼─────────────┼──────────┼────────────┤ │ 真正非阻塞 IO │ 否 │ 是 │ 是 │ 否 │ ├───────────────┼──────────┼─────────────┼──────────┼────────────┤ │ 学习成本 │ 低 │ 高 │ 中 │ 低 │ ├───────────────┼──────────┼─────────────┼──────────┼────────────┤ │ 生产成熟度 │ 中 │ 高 │ 高 │ 高 │ ├───────────────┼──────────┼─────────────┼──────────┼────────────┤ │ PHP 版本要求 │8.1+│ 任意 │ 任意 │ 任意 │ ├───────────────┼──────────┼─────────────┼──────────┼────────────┤ │ 适合场景 │ 框架底层 │ 高并发服务 │ 异步应用 │ 简单并发 │ └───────────────┴──────────┴─────────────┴──────────┴────────────┘---十一、最佳实践原则1.Fiber 不是银弹 └── 不能解决真正的 IO 阻塞(需配合非阻塞 IO)2.适合用 Fiber 的场景 ├── 框架中间件管道 ├── 生成器替代方案 ├── 协作式任务调度 └── 异步库的底层实现3.不适合直接用 Fiber 的场景 ├── 高并发 HTTP 服务 → 用 Swoole ├── 真正异步 IO → 用 ReactPHP/Amp └── 简单脚本 → 直接同步4.生产推荐组合 └── Fiber+ReactPHP v3 或 Fiber+Amp v3---推荐学习路径 Fiber 基础 → 事件循环原理 → ReactPHP/Amp → Swoole 协程 ↓ ↓ ↓ ↓ 理解挂起 理解调度机制 生产异步应用 高性能服务