PHP用ReactPHP怎样异步调用服务_PHPReactPHP异步调用法【非阻】

4次阅读

reacthttpbrowser 是 reactphp 提供的非阻塞异步 http 客户端,基于事件循环实现单进程并发,需显式运行 $loop->run(),适用于 cli 或长连接场景,不适用于同步框架的常规请求生命周期。

PHP用ReactPHP怎样异步调用服务_PHPReactPHP异步调用法【非阻】

reactphpReacthttpBrowser 是最直接的异步 HTTP 客户端

PHP 原生 curlfile_get_contents() 都是阻塞的,而 ReactPHP 提供了真正非阻塞的 HTTP 请求能力。核心是 ReactHttpBrowser ——它基于事件循环,不依赖线程或多进程,单进程即可并发发请求。

使用前确保已安装:composer require react/http-client(注意:新版 ReactPHP 已将客户端移至 react/http 包,实际应装 react/http,但客户端类仍在 ReactHttpBrowser)。

  • 必须显式运行事件循环:$loop->run(),否则请求永远不会发出
  • 不能在同步框架(如 laravelsymfony)的常规 HTTP 请求生命周期里直接用——会卡住整个响应;适合 CLI 场景或长连接服务(如 websocket 后端)
  • Browser 默认不处理重定向,需手动判断 301/302 并重新发起请求
  • 超时需通过 timeout 选项控制,例如 ['timeout' => 5.0],否则可能无限等待

发多个请求时别用 await,要用 promiseall() 并行

ReactPHP 基于 Promise,不是 async/await(PHP 8.1+ 的 async 关键字也不适用于 ReactPHP)。常见错误是写成串行等待:

// ❌ 错误:这仍是阻塞式写法(且语法不合法) $result1 = await $browser->get('https://api.a'); $result2 = await $browser->get('https://api.b'); // 等完第一个才发第二个

正确做法是构造 Promise 数组,用 ReactPromiseall() 并发触发:

立即学习PHP免费学习笔记(深入)”;

$promises = [     $browser->get('https://api.a'),     $browser->get('https://api.b'),     $browser->post('https://api.c', ['json' => ['x' => 1]]) ]; ReactPromiseall($promises)->then(function ($responses) {     // 所有响应同时到达,按顺序对应 $promises 中的请求     echo $responses[0]->getBody() . "n"; }); $loop->run(); // 必须启动循环
  • 每个 $browser->xxx() 返回的是 PromiseInterface,不是结果本身
  • 不要在 then() 回调里再发新请求却不返回 Promise——会导致链断裂,后续无法 catch
  • 若某请求失败,all() 会 reject,可用 ReactPromisesettle() 改为“全部完成,不管成败”

遇到 Call to a member function then() on NULL 多半是没传 $loop

ReactPHP 组件高度依赖事件循环实例。漏传或传错 $loop,会导致内部资源未初始化,Browser 构造失败却静默返回 null,之后调用 then() 就崩了。

检查点:

  • 是否用 ReactEventLoopFactory::create() 显式创建了循环?
  • 是否把该循环实例传给了 new Browser($loop)
  • 是否在 Browser 实例化后、调用请求前,修改或销毁了 $loop
  • 是否在 Composer 自动加载未生效时,手动 require 了文件却没触发 autoloader?

最小可运行示例必须包含这三块:创建 loop → 创建 browser → 发请求 + then()$loop->run()

HTTP/2、连接复用、DNS 缓存这些得靠底层驱动,PHP 默认不支持

ReactPHP 的 HTTP 客户端默认走纯 PHP 实现的 TCP 连接,不自动启用 HTTP/2,也不共享连接池。这意味着:

  • 每个请求都新建 TCP 连接(除非手动复用 Connector),开销比 Guzzle + cURL 高
  • DNS 查询每次都会发生,无内置缓存;高频请求建议配合 ReactDnsResolverResolver 使用自定义解析器
  • 若目标服务强制 HTTP/2(如某些云 API),Browser 会降级到 HTTP/1.1,可能被拒绝或限流
  • 上传大文件时,记得用 stream 而非一次性读入内存,避免 OOM

真正需要高性能、高兼容性的异步调用,ReactPHP 更适合作为底层通信模块嵌入守护进程;若只是偶尔异步拉数据,Guzzle + curl_multi 可能更稳、更省心。

text=ZqhQzanResources