>[info]后端 Laravel 需要调用 GatewayClient API 通知 GatewayWorker 绑定 client_id,首先在 Laravel 项目(聊天室)根目录下运行 Composer 命令来安装: ``` composer require workerman/gateway-worker composer require workerman/gatewayclient ``` >[info][下载Linux版本的demo](http://www.workerman.net/download/GatewayWorker.zip) 只保留以下三个文件然后粘贴到项目根目录下,跳过重复文件即可 .![](https://box.kancloud.cn/ca40dc335da755872648f1675d5ce3ce_917x240.png) >[info]将Applications下的YourApp改为GeChat; 修改Applications/GeChat/下的start_businessworker.php里的worker名称和服务注册地址 ``` // worker名称 $worker->name = 'GeChatBusinessWorker'; // bussinessWorker进程数量 $worker->count = 4; // 服务注册地址 $worker->registerAddress = '127.0.0.1:1314'; ``` >[info]修改Applications/GeChat/下的start_gateway.php,云服务器本机IP需改为:0.0.0.0; ``` $gateway = new Gateway('websocket://0.0.0.0:5210'); // gateway名称,status方便查看 $gateway->name = 'GeChatGateway'; // gateway进程数 $gateway->count = 4; // 本机ip,分布式部署时使用内网ip $gateway->lanIp = '0.0.0.0'; // 内部通讯起始端口,假如$gateway->count=4,起始端口为4000 // 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口 $gateway->startPort = 4000; // 服务注册地址 $gateway->registerAddress = '127.0.0.1:1314'; ``` >[info]修改Applications/GeChat/下的start_register.php里的端口号: ``` // register 必须是text协议 $register = new Register('text://0.0.0.0:1314'); ``` >[info]随后进入项目根目录运行命令: ``` php start.php start -d ``` * 开启成功截图: ![](https://box.kancloud.cn/8239a7ecf1177197ce80d4c9792e20d3_787x354.png) >[info]然后在需要调用 GatewayClient 接口的文件里,引用命名空间: ```// GatewayClient 3.0.0版本以后加了命名空间 use GatewayClient\Gateway; ``` >[info]并设置 Gateway::$registerAddress 属性,告知 GatewayClient 与哪个 GatewayWorker (集群)通讯。方便起见,我把它放在了 Laravel 控制器的 __construct() 方法里: ``` public function __construct() { Gateway::$registerAddress = '127.0.0.1:1314'; } ``` >[danger] 这个属性的设置值必须与前面启动的 Gateway 进程和 BusinessWorker 进程的 registerAddress 属性值一致,其中的 1314端口是由 Register 服务进程监听的,用于 Gateway 进程和 BusinessWorker 进程内部通讯。 >[info]客户端web socket 连接Gateway Worker代码: ``` socket = new WebSocket('ws://192.168.10.10:5210'); ``` >[info]比如Events.php下的onConnect()方法,将Gateway Worker绑定的client_id发送给客户端,客户端收到后继续将client_id发送给Laravel框架完成初始化。客户端链接到Gateway Worker后触发: >[warning] Events.php只保留一个onConnect()方法,并且仅在用户登录成功后渲染主页面时使用; ``` public static function onConnect($client_id) { Gateway::sendToClient($client_id, json_encode(array( 'type' => 'init', 'client_id' => $client_id ))); } ``` >[info]客户端收到init指令后触发: ``` socket = new WebSocket('ws://192.168.10.10:5210'); //连接成功时触发 socket.onopen = function(){ // 登录 console.log("websocket握手成功!"); }; //监听收到的消息 socket.onmessage = function(e) { var data = JSON.parse(e.data), type = data.type || '', message = data.data || ''; switch (type) { case 'init': var client_id = data.client_id || ''; $.post(gechat_url_init,{ type: 'init', client_id : client_id, id : uid , username : uname, avatar : avatar, sign : sign }, function (res) { },'json'); console.log('已发送初始化json'); break; ``` >[info]Laravel框架收到post过来的client_id后进行绑定,然后把该用户的好友,群组,离线消息发送给客户端,继而完成初始化; ``` public function init(Request $request) { $message = $request->all(); $message_type = $message['type']; switch ($message_type) { case 'init' : $uid = $request->session()->get('GEEK'); $client_id = $message['client_id']; Gateway::bindUid($client_id, $uid); // uid 与 room_id 已经从 Laravel session里获取 Gateway::sendToUid($uid, json_encode(array( 'type' => 'notice', 'content' => 'init success !', ))); $request->session()->put([ 'client_id' => $client_id, 'username' => $message['username'], 'avatar' => $message['avatar'], 'sign' => $message['sign'] ]); // Laravel 负责 Gateway::setSession($client_id, [ // GatewayWorker 负责 'id' => $uid, 'username' => $message['username'], 'avatar' => $message['avatar'], 'sign' => $message['sign'] ]); /*$_SESSION['uid'] = $uid; $_SESSION['username'] = $message['username']; $_SESSION['avatar'] = $message['avatar']; $_SESSION['sign'] = $message['sign'];*/ //查询有无需要推送的离线信息 $resMsg = ChatLog::where([ ['to_id','=',$uid], ['need_send','=',1] ])->get(); //var_export($resMsg); if (!empty($resMsg)) { foreach ($resMsg as $vo) { $log_message = [ 'type' => 'logMessage', 'data' => [ 'username' => $vo->from_name, 'avatar' => $vo->from_avatar, 'id' => $vo->from_id, 'type' => $vo->type, 'content' => htmlspecialchars($vo->content), 'timestamp' => strtotime($vo->created_at)*1000, ] ]; Gateway::sendToUid($uid, json_encode($log_message)); //设置推送状态为已经推送 } ChatLog::where('to_id','=',$uid)->update(['need_send' => 0]); } //获取它的所有朋友的id $friendsAll = \App\Http\Models\GeFriend::where([ ['friend_id', '=', $uid], ])->pluck('user_id'); if (!empty($friendsAll)) { foreach ($friendsAll as $vo) { $user_client_id = Gateway::getClientIdByUid($vo); if (!empty($user_client_id)) { $online_message = [ 'type' => 'online', 'id' => $uid, ]; Gateway::sendToUid($vo, json_encode($online_message)); } } } $ret = GeChatGroup::where('user_id','=',$uid)->get(); if (!empty($ret)) { foreach ($ret as $key => $vo) { Gateway::joinGroup($client_id, $vo->group_id); //将登录用户加入群组 } } unset($ret); //设置用户为登录状态 $post = GeUser::where('id','=',$uid)->first(); $post -> status = 1; $post -> save(); return 0; break; ``` >[info]后续的所有聊天消息都直接 get/post 到 Laravel 控制器里统一处理,上面的初始化代码没看懂没有关系,你只要把Gateway Worker配置好并且成功启动就可以了,我会在用户主页一节继续对上述代码进行讲解;