Workerman 中 GuzzleHttp 必须用 Promise 异步调用并绑定 React 事件循环,禁用同步方法(如 get()、wait());否则会阻塞事件循环。需在 onWorkerStart 中初始化 Client 并指定 SocketAdapter handler,或改用 AsyncTcpConnection 发送原生 HTTP 请求。

Workerman 中 GuzzleHttp 同步调用会阻塞事件循环
Workerman 是基于 ReactPHP 事件循环的常驻进程框架,所有 I/O 操作必须是非阻塞的。而 GuzzleHttp 默认使用 PHP 的 curl(同步)或 stream_socket_client(同步),一旦发起请求,就会卡住当前 worker 进程,导致后续连接无法及时响应——这不是 Guzzle 的问题,是调用方式错了。
关键点:不能直接 new GuzzleHttp\Client 然后调用 $client->get(),这等同于在事件循环里塞了个阻塞操作。
必须用 Guzzle 的 Promise + Workerman 的 event-loop 集成
Guzzle 支持异步请求,但前提是底层 HTTP handler 必须是非阻塞的。Workerman 自带的 Worker::$globalEvent(通常是 React\EventLoop\ExtEvLoop 或 React\EventLoop\StreamSelectLoop)可以和 Guzzle 的 React\Adapter\Socket\SocketAdapter 配合使用。
实操建议:
- 安装
guzzlehttp/guzzle和react/socket(composer require guzzlehttp/guzzle react/socket) - 创建 Guzzle client 时显式传入
handler,用React\Adapter\Socket\SocketAdapter替代默认 handler - 务必用
then()注册回调,而不是wait()或直接取getBody() - 不要在
then()回调里做耗时操作(如 DB 查询、文件写入),否则仍会拖慢事件循环
示例片段:
$loop = Worker::$globalEvent;
$handler = new \React\Adapter\Socket\SocketAdapter($loop);
$client = new \GuzzleHttp\Client(['handler' => $handler]);
$promise = $client->getAsync('https://api.example.com/data');
$promise->then(
function ($response) {
echo $response->getBody()->getContents();
},
function ($exception) {
echo 'Request failed: ' . $exception->getMessage();
}
);
常见错误:混用同步方法或忘记启动 loop
现象包括:请求发不出去、callback 不触发、worker 进程 CPU 占满但无响应。多数是因为以下任一情况:
- 调用了
$promise->wait()—— 这会主动阻塞当前事件循环,Workerman 严禁此操作 - 没确认
Worker::$globalEvent已初始化(比如在onWorkerStart外提前 new Client) - 用了
curl_multi类 handler,但未正确绑定到 React loop(Guzzle 官方不推荐这种 hack) - 在非事件驱动上下文(如命令行脚本、定时器回调外)直接 new Client 而未指定 handler
注意:Worker::$globalEvent 在 onWorkerStart 之后才可用,Client 实例建议也在该回调中构建并缓存到全局变量或 Worker 属性中。
替代方案:优先考虑 Workerman 自带的 AsyncTcpConnection
如果只是发简单 HTTP 请求(GET/POST,无复杂中间件、重试、Cookie 管理需求),用 Workerman 原生的 Workerman\Connection\AsyncTcpConnection 更轻量、更可控,且完全适配其事件模型。
例如手动拼 HTTP/1.1 请求头发 GET:
$conn = new \Workerman\Connection\AsyncTcpConnection('tcp://httpbin.org:80');
$conn->onConnect = function($conn) {
$conn->send("GET /get HTTP/1.1\r\nHost: httpbin.org\r\nConnection: close\r\n\r\n");
};
$conn->onMessage = function($conn, $data) {
echo "Response: " . substr($data, 0, 200);
};
$conn->connect();
复杂业务逻辑下 Guzzle 仍是首选,但得守住「Promise + 正确 handler + 不 wait」这条线。最容易被忽略的是 handler 初始化时机和 loop 绑定完整性——错一步,就退化成同步阻塞。

















