1. 为什么需要多协议支持物联网设备种类繁多通信协议也各不相同。就像我们平时用的手机有的用移动网络有的连Wi-Fi还有的通过蓝牙传输数据。在物联网项目中我们经常会遇到设备使用不同协议的情况TCP协议常用于工业设备稳定性高但开发复杂度大WebSocket网页实时通信的首选适合管理后台MQTT物联网专用协议低功耗设备的最爱去年我做了一个智能农业项目就遇到了温室传感器用MQTT、控制终端用WebSocket、灌溉设备用TCP的复杂情况。如果每个协议单独部署服务不仅服务器成本翻倍设备间的联动也会变得异常麻烦。2. 环境准备与组件安装2.1 基础环境搭建先确保你的开发环境已经准备好这些基础材料# 创建ThinkPHP8项目 composer create-project topthink/think tp8-project # 进入项目目录 cd tp8-projectThink-Worker是Workerman的ThinkPHP封装版相当于给Workerman穿上了ThinkPHP的外套。安装时要注意版本兼容性# 安装Think-Worker自带GatewayWorker composer require topthink/think-worker # 安装GatewayClientTCP通信必备 composer require workerman/gatewayclient # 安装MQTT支持 composer require workerman/mqtt我遇到过最坑的问题是PHP版本冲突。Workerman要求PHP7.3而ThinkPHP8需要PHP7.4。建议直接用PHP8.0以上版本能避开很多奇怪的问题。2.2 多协议配置技巧要让单服务器支持多协议关键是要理解GatewayWorker的架构。它采用经典的GatewayWorker模式Gateway进程负责维持客户端连接BusinessWorker进程处理实际业务逻辑配置时要注意这些参数参数名TCP协议建议值WebSocket建议值listentcp://0.0.0.0:8282websocket://0.0.0.0:8283startPort29003000nameTCPGatewayWebSocketGatewaypidFileruntime/tcp.pidruntime/ws.pid3. 核心代码实现3.1 多协议启动配置在config目录下创建两个配置文件// gateway_worker.php (TCP配置) return [ listen tcp://0.0.0.0:8282, context [], name TCPGateway, count 4, pidFile runtime_path(tcp.pid) ]; // gateway_worker_ws.php (WebSocket配置) return [ listen websocket://0.0.0.0:8283, name WebSocketGateway, pidFile runtime_path(ws.pid) ];然后在worker目录创建事件处理类namespace app\worker; use GatewayWorker\Lib\Gateway; use Workerman\Worker; use Workerman\Mqtt\Client as MqttClient; class Events { private static $mqtt; public static function onWorkerStart(Worker $worker) { // MQTT连接配置 $mqtt new MqttClient(mqtt://127.0.0.1:1883, [ username admin, password 123456 ]); $mqtt-onConnect function($mqtt) { $mqtt-subscribe(device/#); // 订阅所有设备主题 }; $mqtt-connect(); self::$mqtt $mqtt; } }3.2 设备绑定实战物联网设备绑定就像给快递贴标签没有标签就不知道往哪送。推荐两种绑定方式方式一主动上报绑定适合TCP设备public static function onMessage($client_id, $message) { // 假设设备上报数据格式{sn:DEV123,data:...} $data json_decode($message, true); if(isset($data[sn])) { // 将设备序列号与连接ID绑定 Gateway::bindUid($client_id, $data[sn]); // 可以加入设备分组 Gateway::joinGroup($client_id, sensors); } }方式二WebSocket认证绑定前端连接后发送认证信息// 前端代码 const ws new WebSocket(ws://yourserver:8283); ws.onopen () { ws.send(JSON.stringify({ action: auth, token: 用户令牌 })); };后端处理绑定public static function onMessage($client_id, $message) { $data json_decode($message, true); if($data[action] auth) { // 验证token获取用户ID $uid TokenService::getUserId($data[token]); Gateway::bindUid($client_id, $uid); } }4. 消息推送的三种姿势4.1 TCP设备控制通过GatewayClient发送控制指令use GatewayClient\Gateway; // 设置GatewayWorker服务地址 Gateway::$registerAddress 127.0.0.1:1236; // 向指定设备发送指令 Gateway::sendToUid(DEV123, json_encode([ cmd pump_on, duration 30 ]));4.2 WebSocket实时消息后台管理端推送告警消息// 获取所有在线管理员 $admins [admin1, admin2]; foreach($admins as $uid) { if(Gateway::isUidOnline($uid)) { Gateway::sendToUid($uid, json_encode([ type alert, content 温度过高 ])); } }4.3 MQTT主题发布控制智能灯场景// 在Events类中添加方法 public static function controlLight($deviceId, $command) { $topic device/{$deviceId}/control; self::$mqtt-publish($topic, json_encode([ cmd $command, time time() ])); }5. 性能优化实战经验5.1 连接管理技巧物联网设备经常掉线要做好连接管理public static function onClose($client_id) { // 记录设备离线日志 $uid Gateway::getUidByClientId($client_id); Log::write(设备{$uid}离线); // 清除绑定关系 Gateway::unbindUid($client_id, $uid); }5.2 定时任务优化用Workerman的定时器替代Crontabpublic static function onWorkerStart($worker) { // 每5秒检查设备心跳 Timer::add(5, function(){ $time time(); foreach(Gateway::getAllUidList() as $uid) { if($time - Gateway::getSession($uid)[last_heartbeat] 30) { Gateway::closeClient(Gateway::getClientIdByUid($uid)); } } }); }5.3 内存泄漏排查长时间运行后内存增长试试这些方法在Events.php中定期调用gc_collect_cycles()避免在回调函数中使用大数组检查MQTT订阅是否及时取消// 示例清理无效订阅 Timer::add(3600, function() { self::$mqtt-unsubscribe([old_topic/#]); });6. 常见坑与解决方案坑1端口冲突现象启动第二个Gateway时报端口占用解决确保每个Gateway配置中的startPort间隔足够大坑2MQTT断连现象运行几小时后MQTT连接断开解决添加心跳检测和自动重连机制$mqtt-onClose function() use ($mqtt) { sleep(5); $mqtt-connect(); };坑3WebSocket跨域现象前端连接时报跨域错误解决在WebSocket Gateway配置中添加origin检查context [ ssl [ verify_peer false ] ], origin [https://yourdomain.com]最近在智慧园区项目中实践这套方案时单台4核8G服务器稳定支撑了2000设备并发连接。关键是要做好协议分流和设备分组管理比如把高频通信的设备单独分组低频设备合并分组。
ThinkPHP8集成Think-Worker实现多协议(TCP/WebSocket/MQTT)物联网设备管理与消息推送实战
1. 为什么需要多协议支持物联网设备种类繁多通信协议也各不相同。就像我们平时用的手机有的用移动网络有的连Wi-Fi还有的通过蓝牙传输数据。在物联网项目中我们经常会遇到设备使用不同协议的情况TCP协议常用于工业设备稳定性高但开发复杂度大WebSocket网页实时通信的首选适合管理后台MQTT物联网专用协议低功耗设备的最爱去年我做了一个智能农业项目就遇到了温室传感器用MQTT、控制终端用WebSocket、灌溉设备用TCP的复杂情况。如果每个协议单独部署服务不仅服务器成本翻倍设备间的联动也会变得异常麻烦。2. 环境准备与组件安装2.1 基础环境搭建先确保你的开发环境已经准备好这些基础材料# 创建ThinkPHP8项目 composer create-project topthink/think tp8-project # 进入项目目录 cd tp8-projectThink-Worker是Workerman的ThinkPHP封装版相当于给Workerman穿上了ThinkPHP的外套。安装时要注意版本兼容性# 安装Think-Worker自带GatewayWorker composer require topthink/think-worker # 安装GatewayClientTCP通信必备 composer require workerman/gatewayclient # 安装MQTT支持 composer require workerman/mqtt我遇到过最坑的问题是PHP版本冲突。Workerman要求PHP7.3而ThinkPHP8需要PHP7.4。建议直接用PHP8.0以上版本能避开很多奇怪的问题。2.2 多协议配置技巧要让单服务器支持多协议关键是要理解GatewayWorker的架构。它采用经典的GatewayWorker模式Gateway进程负责维持客户端连接BusinessWorker进程处理实际业务逻辑配置时要注意这些参数参数名TCP协议建议值WebSocket建议值listentcp://0.0.0.0:8282websocket://0.0.0.0:8283startPort29003000nameTCPGatewayWebSocketGatewaypidFileruntime/tcp.pidruntime/ws.pid3. 核心代码实现3.1 多协议启动配置在config目录下创建两个配置文件// gateway_worker.php (TCP配置) return [ listen tcp://0.0.0.0:8282, context [], name TCPGateway, count 4, pidFile runtime_path(tcp.pid) ]; // gateway_worker_ws.php (WebSocket配置) return [ listen websocket://0.0.0.0:8283, name WebSocketGateway, pidFile runtime_path(ws.pid) ];然后在worker目录创建事件处理类namespace app\worker; use GatewayWorker\Lib\Gateway; use Workerman\Worker; use Workerman\Mqtt\Client as MqttClient; class Events { private static $mqtt; public static function onWorkerStart(Worker $worker) { // MQTT连接配置 $mqtt new MqttClient(mqtt://127.0.0.1:1883, [ username admin, password 123456 ]); $mqtt-onConnect function($mqtt) { $mqtt-subscribe(device/#); // 订阅所有设备主题 }; $mqtt-connect(); self::$mqtt $mqtt; } }3.2 设备绑定实战物联网设备绑定就像给快递贴标签没有标签就不知道往哪送。推荐两种绑定方式方式一主动上报绑定适合TCP设备public static function onMessage($client_id, $message) { // 假设设备上报数据格式{sn:DEV123,data:...} $data json_decode($message, true); if(isset($data[sn])) { // 将设备序列号与连接ID绑定 Gateway::bindUid($client_id, $data[sn]); // 可以加入设备分组 Gateway::joinGroup($client_id, sensors); } }方式二WebSocket认证绑定前端连接后发送认证信息// 前端代码 const ws new WebSocket(ws://yourserver:8283); ws.onopen () { ws.send(JSON.stringify({ action: auth, token: 用户令牌 })); };后端处理绑定public static function onMessage($client_id, $message) { $data json_decode($message, true); if($data[action] auth) { // 验证token获取用户ID $uid TokenService::getUserId($data[token]); Gateway::bindUid($client_id, $uid); } }4. 消息推送的三种姿势4.1 TCP设备控制通过GatewayClient发送控制指令use GatewayClient\Gateway; // 设置GatewayWorker服务地址 Gateway::$registerAddress 127.0.0.1:1236; // 向指定设备发送指令 Gateway::sendToUid(DEV123, json_encode([ cmd pump_on, duration 30 ]));4.2 WebSocket实时消息后台管理端推送告警消息// 获取所有在线管理员 $admins [admin1, admin2]; foreach($admins as $uid) { if(Gateway::isUidOnline($uid)) { Gateway::sendToUid($uid, json_encode([ type alert, content 温度过高 ])); } }4.3 MQTT主题发布控制智能灯场景// 在Events类中添加方法 public static function controlLight($deviceId, $command) { $topic device/{$deviceId}/control; self::$mqtt-publish($topic, json_encode([ cmd $command, time time() ])); }5. 性能优化实战经验5.1 连接管理技巧物联网设备经常掉线要做好连接管理public static function onClose($client_id) { // 记录设备离线日志 $uid Gateway::getUidByClientId($client_id); Log::write(设备{$uid}离线); // 清除绑定关系 Gateway::unbindUid($client_id, $uid); }5.2 定时任务优化用Workerman的定时器替代Crontabpublic static function onWorkerStart($worker) { // 每5秒检查设备心跳 Timer::add(5, function(){ $time time(); foreach(Gateway::getAllUidList() as $uid) { if($time - Gateway::getSession($uid)[last_heartbeat] 30) { Gateway::closeClient(Gateway::getClientIdByUid($uid)); } } }); }5.3 内存泄漏排查长时间运行后内存增长试试这些方法在Events.php中定期调用gc_collect_cycles()避免在回调函数中使用大数组检查MQTT订阅是否及时取消// 示例清理无效订阅 Timer::add(3600, function() { self::$mqtt-unsubscribe([old_topic/#]); });6. 常见坑与解决方案坑1端口冲突现象启动第二个Gateway时报端口占用解决确保每个Gateway配置中的startPort间隔足够大坑2MQTT断连现象运行几小时后MQTT连接断开解决添加心跳检测和自动重连机制$mqtt-onClose function() use ($mqtt) { sleep(5); $mqtt-connect(); };坑3WebSocket跨域现象前端连接时报跨域错误解决在WebSocket Gateway配置中添加origin检查context [ ssl [ verify_peer false ] ], origin [https://yourdomain.com]最近在智慧园区项目中实践这套方案时单台4核8G服务器稳定支撑了2000设备并发连接。关键是要做好协议分流和设备分组管理比如把高频通信的设备单独分组低频设备合并分组。