workerman 5.2.3中自定义协议必须同步执行(input/decode/encode均为纯静态方法,不可协程挂起),协程处理应严格限定在onmessage回调内——协议层仅负责分包与编解码,业务逻辑(如异步http、数据库查询)须在onmessage中通过coroutine::create或协程客户端安全执行。

在 Workerman 5.2.3 中,自定义协议本身是同步执行的(input、decode、encode 都是静态方法,不支持 await 或协程挂起),但你完全可以在 onMessage 回调中安全使用协程处理解包后的数据——这是推荐且主流的做法。
自定义协议类保持纯同步
协议层只负责“分包+编解码”,不能也不应包含协程逻辑:
-
input()必须快速返回包长或 0,不能调用任何 I/O 或sleep等阻塞操作 -
decode()和encode()应仅做数据结构转换(如json_decode、pack/unpack),不发起网络请求或文件读写 - 协议类本身不感知协程,Workerman 在收到完整包后才调用
decode,此时已在协程上下文中(只要 Worker 配置了协程驱动)
在 onMessage 中启动协程处理业务
解包后的 $data 已是 PHP 变量,此时可自由使用协程:
- 确保 Worker 已指定协程驱动(如
Swoole::class、Revolt::class或Fiber::class) - 用
Coroutine::create()启动新协程,或直接在回调内调用协程兼容的客户端(如workerman/http-client) - 示例:
use Workerman\Worker;
use Workerman\Coroutine;
use Workerman\Http\Client;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('MyProtocol://0.0.0.0:1234');
$worker->eventLoop = \Workerman\Events\Swoole::class; // 关键:启用协程驱动
$worker->onMessage = function($connection, $data) {
// $data 是 decode() 返回的数组/对象,已准备好
Coroutine::create(function () use ($connection, $data) {
// ✅ 协程内可安全调用异步 HTTP
$client = new Client();
$response = $client->get('https://api.example.com/user?id=' . ($data['id'] ?? 1));
$body = $response->getBody();
$connection->send(['code'=>0, 'data'=>$body]);
});
};
Worker::runAll();
?>
注意 Fiber 驱动的限制
若使用 Fiber::class(依赖 revolt/event-loop):
- PHP 原生阻塞函数(如
file_get_contents、curl_exec)仍会阻塞整个进程,必须改用协程客户端 - 务必安装:
composer require revolt/event-loop -
input/decode/encode仍不可用await,协程只能在onMessage及其子调用中展开
避免常见错误
- ❌ 不要在
input()里做网络请求或 sleep —— 会卡住整个连接接收流程 - ❌ 不要试图在
decode()中yield或await—— 方法签名不支持,且违反协议层职责 - ✅ 正确做法:协议只管“拆包”,业务逻辑(含协程)全放在
onMessage或其调用的 service 类中











