websocket连接建立后,需让后厨小票机带printer_id等唯一标识主动连接workerman服务器,并在onconnect中解析参数存入$connections[printer_id]=$connection;下单时查该数组精准单播推送,配合formatforkitchen()结构化转换订单字段,区分打印场景,避免原样透传冗余数据。

WebSocket 连接建立后,如何把订单推给指定后厨小票机?
Workerman 本身不内置“打印机驱动”或“硬件协议”,它只负责把订单数据实时推送到连接在后厨的终端设备(比如运行了 WebSocket 客户端的打印服务)。关键在于:你得让后厨小票机主动连上你的 Workerman WebSocket 服务器,并带上唯一标识(如 printer_id 或 kitchen_area),这样你才能按需广播或单播。
常见错误是所有小票机都用同一个连接 ID,结果改单、加菜时全店乱打。正确做法是在客户端连接时就传参:
- 小票机端用 JS 或 PHP 客户端连接
ws://your-domain:8080?printer_id=kitchen_a - 服务端在
$ws_worker->onConnect中解析$connection->getRemoteIp()和 query 参数,存进全局数组或 Redis,例如:$connections[$printer_id] = $connection - 下单时查
$connections['kitchen_b'],只推给对应区域的小票机
订单结构怎么设计,才能兼容一菜一单、套餐拆单、打包分组?
后厨小票不是简单 dump 订单 JSON。不同厨房场景对字段粒度要求差异很大:凉菜间要看到“去皮、少冰”,打包台要看到“牛皮纸袋+泡沫盒”,主厨要看“先炸后炒”。所以不能把前端下单的 order 原样转发,必须做结构化转换。
建议在推送前走一层 formatForKitchen() 函数,按配置动态生成字段:
- 启用“一菜一单”时,把一个订单拆成多条消息,每条含
item_name、qty、notes、print_group - 遇到套餐,检查是否开启“套餐明细打印”,若开启,则展开子项并标记
is_sub_item: true - 外卖单需提取
packaging_group字段(来自天财商龙等系统的扩展字段),并确保该字段随消息一起下发
别直接透传小程序原始 payload——里面可能混着营销券、用户地址等后厨完全不需要的信息。
为什么加菜/退菜消息总比前台晚半秒,甚至漏发?
根本原因是没区分“事件类型”和“连接状态”。Workerman 的 onMessage 是纯通道,如果你在处理加菜逻辑时用了同步 MySQL 查询 + 多次 $connection->send(),而某个小票机刚好网络抖动断连又重连,旧连接句柄还在内存里但已失效,就会卡住整个 loop。
必须做两件事:
- 所有推送操作包在
try...catch里,捕获Connection reset by peer类错误后,从$connections数组中清理掉失效句柄 - 加菜/退菜这类变更消息,统一走异步队列(如 Redis Pub/Sub)中转,Workerman 的 Worker 进程只负责监听队列、校验权限、再精准投递,避免业务逻辑阻塞 IO 线程
- 前端触发加菜后,不要等后厨返回 success 才更新 UI;而是本地乐观更新,后厨失败时再 toast 提示“厨房未收到,请重试”
多台小票机共用一台打印机,怎么避免并发打印错乱?
这不是 Workerman 的问题,是硬件层的串行控制问题。即使你用 printer_id 区分了逻辑路由,如果多台小票机最终都连向同一台 USB 打印机(比如通过 Windows 共享或 CUPS),物理层没有排队机制,就会出现 A 单还没打完、B 单内容插进来,打出半张乱码单。
解决方案只有两个方向:
- 硬件上:换支持网络协议(如 ESC/POS over TCP)的热敏打印机,每台小票机直连独立 IP,由 Workerman 分发到不同 IP 地址,彻底隔离通路
- 软件上:在小票机本地起一个轻量打印代理(如 Python +
escpos库),Workerman 只发 JSON 给它,由它自己做队列、加锁、重试,不依赖系统打印池
别指望靠 sleep(1) 或文件锁解决——高并发下毫秒级竞争,锁粒度不对等于没锁。











