首先说明: 环境是在Windows下.
因为项目需要用到websocket协议进行服务端主动推送,所以选择引入GatewayWorker
一.安装GatewayWorker
下载完成后进行解压, 可以看到整个文件夹的目录结构如下所示:
这些文件是个整体, 在Windows下理论上可以放在任何目录中整体运行 start_for_win.bat
脚本.
start_for_win.bat
是 Windows 环境下 GatewayWorker 所有 Worker 进程的启动文件
start.php
是 Linux 环境下 GatewayWorker 所有 Worker 进程的启动文件
二.修改IP:端口与协议
1.start_businessworker.php
-> BusinessWorker 进程的启动文件
// bussinessWorker 进程 $worker = new BusinessWorker(); // worker名称 $worker->name = 'YourAppBusinessWorker'; // bussinessWorker进程数量 $worker->count = 4; // 服务注册地址 $worker->registerAddress = '127.0.0.1:1238';
2.start_gateway.php
-> Gateway 进程的启动文件
// gateway 进程,这里使用Text协议,可以用telnet测试 $gateway = new Gateway("Websocket://0.0.0.0:8282"); // gateway名称,status方便查看 $gateway->name = 'YourAppGateway'; // gateway进程数 $gateway->count = 4; // 本机ip,分布式部署时使用内网ip $gateway->lanIp = '127.0.0.1'; // 内部通讯起始端口,假如$gateway->count=4,起始端口为4000 // 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口 $gateway->startPort = 2900; // 服务注册地址 $gateway->registerAddress = '127.0.0.1:1238'; // 心跳间隔(看需求是否启用心跳包) // $gateway->pingInterval = 10; // 心跳数据 // $gateway->pingData = '{"type":"ping"}';
3.tart_register.php
-> Register 服务进程的启动文件
// register 必须是text协议 $register = new Register('text://0.0.0.0:1238');
全都正常配置后运行star_for_win.bat
, 可以看到如下画面
三.Events.php文件
Events.php 是 BusinessWorker 进程的实际业务处理类
因为在tp中完成所有业务逻辑,GatewayWorker仅仅当做一个单向的推送通道,所以我将Events.php修改如下:
<?php /** * This file is part of workerman. * * Licensed under The MIT License * For full copyright and license information, please see the MIT-LICENSE.txt * Redistributions of files must retain the above copyright notice. * * @author walkor<walkor@workerman.net> * @copyright walkor<walkor@workerman.net> * @link http://www.workerman.net/ * @license http://www.opensource.org/licenses/mit-license.php MIT License */ /** * 用于检测业务代码死循环或者长时间阻塞等问题 * 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload * 然后观察一段时间workerman.log看是否有process_timeout异常 */ //declare(ticks=1); use \GatewayWorker\Lib\Gateway; /** * 主逻辑 * 主要是处理 onConnect onMessage onClose 三个方法 * onConnect 和 onClose 如果不需要可以不用实现并删除 */ class Events { /** * 当客户端连接时触发 * 如果业务不需此回调可以删除onConnect * * @param int $client_id 连接id */ public static function onConnect($client_id) { // 绑定操作 Gateway::sendToClient($client_id, json_encode(array( 'type' => 'init', 'client_id' => $client_id ))); } /** * 当客户端发来消息时触发 * @param int $client_id 连接id * @param mixed $message 具体消息 */ public static function onMessage($client_id, $message) { // 向所有人发送 // Gateway::sendToAll("$client_id said $message\r\n"); //将接收到的json信息转成数组 $msg = json_decode($message,true); //如果数据为空则不处理 if (!$msg) { return; } //判断消息类型 switch ($msg['type']) { //收到消息 case 'MessageReceived': Gateway::sendToAll(json_encode(['type' => 'MessageReceived','content' => '收到消息'])); break; case 'ping': Gateway::sendToAll(json_encode(['type' => 'ping','content' => 'Heartbeat'])); break; } } /** * 当用户断开连接时触发 * @param int $client_id 连接id */ public static function onClose($client_id) { // 向所有人发送 // GateWay::sendToAll("$client_id logout\r\n"); } }
四.安装GatewayClient拓展
自行安装好composer后
打开cmd切换到项目根目录下, 输入
composer require workerman/gatewayclient
安装完成后, 在需要调用GatewayClient接口的控制器里引用命名空间
use GatewayClient\Gateway;
并设置 Gateway::$registerAddress 属性,告知 GatewayClient 与哪个 GatewayWorker (集群)通讯
Gateway::$registerAddress = '127.0.0.1:1238';
现在, 就可以在我们的项目中调用GatewayClient接口实现各种功能了!
五.TP中控制器主动发送消息部分
<?php namespace app\index\controller; use think\Db; use think\Controller; use GatewayClient\Gateway; use think\Request; class Msg extends Controller { public function _initialize() { // 设置GatewayWorker服务的Register服务ip和端口,请根据实际情况改成实际值(ip不能是0.0.0.0) Gateway::$registerAddress = '127.0.0.1:1238'; } public function index() { // 这个页面的代码会贴在下一步 return $this->fetch('msg/index'); } // 发送消息 public function send() { $param = input(); // Gateway::$registerAddress = '127.0.0.1:1238'; Gateway::sendToAll(json_encode(['type'=>'MessageReceived', 'data'=>$param])); } // 被访问的测试页面 public function test() { $request = Request::instance(); $url = url('/index/msg/send'); $url = $request->domain() . $url; var_dump($url); $data = [ 'aaa' => 111, 'bbb' => 222 ]; $res = get_curl($url, $data); // $res = file_get_contents($url); var_dump($res); } }
六.index.html测试看结果demo部分
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> 这里只做接收消息展示用, 除了心跳包, 不发送任何消息. </body> <script> ws = new WebSocket("ws://"+document.domain+":8282"); ws.onopen = function() { console.log("连接成功"); //10秒发送一次心跳包 var heart = JSON.stringify({"type":"ping"}); setInterval(function(){ ws.send(heart); },10000); }; // 服务端主动推送消息时会触发这里的onmessage ws.onmessage = function(e){ // console.log(e); var data = eval("("+e.data+")"); // console.log(data); var type = data.type || ''; // console.log(type); switch(type){ case 'ping': console.log('heartbeat'); break; // 当mvc框架调用GatewayClient发消息时直接打印出来 case 'MessageReceived': // 接收到tp中主动发送数据提示的标记 console.log('MessageReceived', data); break; default : console.log(e.data); } }; </script> </html>
全都准备就绪之后, 访问 http://domain/index/msg/index (我这里是这个路由, 这里根据自己项目的路由访问)
打开F12, 可以看到 连接成功
再访问发送测试消息的路由, 我这里是 http://solar.cc/index/msg/test, 回到上一步打开的index页面,
可以看到控制台内输出了我们控制器中主动发送的消息
记录一下
-End-