channel 是 workerman 内部基于内存+文件锁实现的轻量级单机进程间广播通道,仅限同一 worker 实例的子进程间通信,不具备分布式、持久化能力,不可替代 redis。

Channel 是什么,为什么不能当 Redis 用
Channel 不是消息队列,也不是持久化存储。它是 Workerman 内部基于内存 + 文件锁实现的轻量级进程间广播通道,只在同一个 Worker 实例下的多个子进程之间有效。一旦主进程重启、或跨机器部署,Channel 就完全失效——这点和 Redis 或 Kafka 有本质区别。
常见错误现象:客户端收不到广播、重启后订阅丢失、多台服务器间消息不互通。根本原因就是误把它当成了分布式通信方案。
- 适用场景:单机多进程协作,比如一个
Gateway进程收到 WebSocket 消息,通知其他BusinessWorker进程做状态同步 - 不适用场景:跨机器推送、消息持久化、高可靠投递
- 性能优势:无网络开销,广播延迟在微秒级;但容量受限于内存和文件锁粒度
启动 ChannelServer 的关键配置
必须单独起一个 ChannelServer 进程,且所有用到 Channel 的 Worker 都得连它。不是引入类就能自动工作。
典型错误:只写了 new \Workerman\Channel\Client() 却没启动服务端,结果 publish() 静默失败,subscribe() 一直阻塞。
- 服务端启动脚本路径必须显式指定,例如:
php start.php start -d对应的start.php中要包含new \Workerman\Channel\Server('0.0.0.0:2206') - 端口不能被占用,且需确保防火墙放行(本地开发通常不用管,但 Docker 或云服务器上容易被拦)
- 客户端连接时地址必须和 Server 一致,比如 Server 绑定
127.0.0.1:2206,Client 就不能写localhost:2206(某些系统 DNS 解析会失败) - Client 初始化后要调用
connect(),否则后续操作全无效;建议加超时判断:if (!$client->connect()) { exit("channel connect failed"); }
publish/subscribe 的实际调用姿势
publish() 和 subscribe() 看似简单,但参数含义和生命周期很容易搞混。尤其 subscribe() 是长连接+回调模式,不是发一次收一次。
常见错误现象:回调函数从不执行、重复订阅导致多次触发、进程退出后 channel 连接未关闭,server 端堆积连接。
-
publish($channel_name, $data):$data 会被json_encode,所以别传资源句柄、闭包或循环引用数组 -
subscribe($channel_name, $callback):$callback 接收两个参数 ——$channel_name和$data(已自动json_decode),不是只传数据 - 一个 Client 实例可订阅多个频道,但每个频道只能有一个回调;重复调用
subscribe()同一频道会覆盖前一个回调 - 务必在 Worker 进程退出前调用
$client->close(),否则 ChannelServer 会认为连接还活着,最终耗尽连接数
use Workerman\Channel\Client;
$client = new Client();
$client->connect();
$client->subscribe('user_online', function($channel, $data) {
echo "收到 {$channel}: " . json_encode($data) . "\n";
});
// 注意:这里不能直接 exit(),要等事件循环跑起来
\Workerman\Worker::runAll();
调试连不上或收不到消息的三件事
Channel 问题最难排查的地方在于:它不报错,只是“没反应”。与其翻源码,不如先确认这三件事。
- 用
netstat -an | grep :2206(替换成你的端口)看ChannelServer进程是否真在监听;没输出说明服务根本没起来 - 在 Client 端加日志:
var_dump($client->isConnected());,返回false就别往下走了 - 检查
publish()和subscribe()的 $channel_name 是否完全一致(包括大小写、空格、下划线),Channel 区分字符串全匹配,不支持通配符或正则
最常被忽略的是:Client 和 Server 的 PHP 版本或 Swoole 扩展版本不兼容。Workerman 4.x 的 Channel 默认依赖 swoole 的协程能力,如果没装 swoole 或版本太低(如 Channel\Client 会退化为 file-based 模式,行为可能异常。这时候看日志里有没有 "Using file-based channel" 提示。










