tp5+workerman+gatewayworker实现每分钟将数据广播到群组客户端

1.安装

安装workerman

composer require topthink/think-worker

安装gatewayworker

composer require workerman/gateway-worker

安装mysql,查询数据库返回数据使用

composer require workerman/mysql

准备工作到此结束
下面开始代码
在app下新建gatewayapp文件夹
在gatewayapp文件夹下新建controller、run两个文件夹,建立配置文件const.php,启动文件start_for_win.bat
在controller文件夹下新建GwEvents.php
在run文件夹下新建start_businessworker.php、start_gateway.php、start_register.php
目录结构如下
tp5+workerman+gatewayworker实现每分钟将数据广播到群组客户端_第1张图片
业务文件GwEvents.php内容



/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

namespace app\gatewayapp\controller;

use GatewayWorker\Lib\Gateway;
use think\Config;
use think\Db;
use think\Log;
use think\Controller;
use think\Request;
use Workerman\Lib\Timer;

require_once dirname(__FILE__) . '/../../../vendor/autoload.php';

/**
 * 主逻辑
 * 主要是处理 onConnect onMessage onClose 三个方法
 * onConnect 和 onClose 如果不需要可以不用实现并删除
 */
class GwEvents {

    /**
     * 新建一个类的静态成员,用来保存数据库实例
     */
    public static $db = null;

    /**
     * 进程启动后初始化数据库连接
     */
    public static function onWorkerStart($worker) {
    //数据库连接配置
        self::$db = new \Workerman\MySQL\Connection('数据库地址', '端口', '用户名', '密码', '数据库名');
//减少价格,定时器减少价格并且保存到数据库,,这里做的是抢购活动 根据查询条件筛选符合商品
//设定5秒减少一次价格
        $scanDbTime = 5;
        Timer::add($scanDbTime, function() {
            $nowTime = time();
            $blogInfo = self::$db->select("*")->from("table_det")->where("startTime<='" . $nowTime . "'")->where("endTime>='" . $nowTime . "'")->query();
            if ($blogInfo) {
                foreach ($blogInfo as $key => $value) {
                    $goodNow[$key] = self::$db->select("*")->from("table")->where("wxId='" . $blogInfo[$key]['Id'] . "'")->where("status=1")->row();//这里有个坑,如果使用query(),拿不到数据
                    if ($goodNow[$key]) {
                    //这里做的是减价处理,按照时间不短减少价格 到1块为止。
                        if ($goodNow[$key]['Price'] == 1) {
                        } else if ($goodNow[$key]['Price'] > 100) {
                            $goodNowt = self::$db->select('*')->from('table')->where("wxId='" . $blogInfo[$key]['Id'] . "'")->where("status=1")->row();
                            $nowPrice = $goodNowt['Price'] - 100;
                            $goodNowa = self::$db->query("UPDATE `table` SET `Price` = " . $nowPrice . " WHERE wxId='" . $blogInfo[$key]['Id'] . "'");
                        } else {
                            $goodNowb = self::$db->query("UPDATE `table` SET `Price` = 1 WHERE wxId='" . $blogInfo[$key]['Id'] . "'");
                        }
                    } else {
                        
                    }
                }
            } else {
                
            }
        });
//广播数据
//设定5秒向客户端广播一次
        $returnWxTime = 5;
        Timer::add($returnWxTime, function() {
            $nowTime = time();
            $blogInfo = self::$db->select("*")->from("table_det")->where("startTime<='" . $nowTime . "'")->where("endTime>='" . $nowTime . "'")->query();
            if ($blogInfo) {
                foreach ($blogInfo as $key => $value) {
                    $goodNow[$key] = self::$db->select("*")->from("table")->where("wxId='" . $blogInfo[$key]['Id'] . "'")->where("status=1")->row();
                    if ($goodNow[$key]) {
                        $arr[$key]['watchNum'] = $goodNow[$key]['watchNum'];
                        $arr[$key]['status'] = 'goodsInfo';
                        $arr[$key]['nowPrice'] = $goodNow[$key]['Price'] / 100;
                        //向群组广播数据 json 编码
                        Gateway::sendToGroup($blogInfo[$key]['Id'], json_encode($arr[$key]));
                    } else {
//                        
                    }
                }
            } else {
                
            }
        });
        //推送购买记录原理相同,不在赘述,同样是使用定时器完成操作。
    }

    /**
     * 当客户端发来消息时触发
     * @param int $client_id 连接id
     * @param mixed $message 具体消息
     */
    public static function onMessage($client_id, $data) {
// 向所有人发送
        $message = json_decode($data, true);
        $groupId = $message['Id'];
        $userId = $message['userId'];

// client_id与uid绑定
        Gateway::bindUid($client_id, $userId);
// 加入某个群组(可调用多次加入多个群组)
        Gateway::joinGroup($client_id, $groupId);
// 使用数据库实例
        $blogInfo = self::$db->select('*')->from('table')->where("wxId='" . $groupId . "'")->row();
        $arr['watchNum'] = $blogInfo['wNum'];//围观人数
        $arr['status'] = 'goodsInfo';//消息类别
        $arr['nowPrice'] = $blogInfo['goodPrice'] / 100;//现在价格
// 打印结果
        Gateway::sendToGroup($groupId, json_encode($arr));
    }

以下内容基本不需要修改,参考开发手册即可。

start_businessworker.php内容



/* 
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

use Workerman\Worker;
use GatewayWorker\BusinessWorker;
use Workerman\Autoloader;
 
$businessInstance = new GwBusinessWorker();
call_user_func_array(array($businessInstance,'index'),array());
 
class GwBusinessWorker {
 
    public function __construct() {
        require_once dirname(__FILE__) . '/../../../vendor/autoload.php';
        include_once dirname(__FILE__).'/../const.php';
    }
 
    public function index() {
        // bussinessWorker 进程
        $worker = new BusinessWorker();
        // worker名称
        $worker->name = GW_WORKER_NAME;
        // bussinessWorker进程数量
        $worker->count = GW_BUSINESS_WORKER_COUNT;
        // 服务注册地址
        $worker->registerAddress = GW_REGISTER_ADDRESS;
        //设置处理业务的类,此处制定Events的命名空间
        $worker->eventHandler = GW_BUSINESS_EVENT_HANDLER;
 
        // 如果不是在根目录启动,则运行runAll方法
        if(!defined('GLOBAL_START')) {
            Worker::runAll();
        }
    }
 
}

start_gateway.php内容



/* 
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

use Workerman\Worker;
use GatewayWorker\Gateway;
use Workerman\Autoloader;
 
$gatewayInstance = new GwGateway();
call_user_func_array(array($gatewayInstance,'index'),array());
 
class GwGateway {
 
    private $gatewayAddress;
 
    public function __construct() {
        require_once dirname(__FILE__) . '/../../../vendor/autoload.php';
        include_once dirname(__FILE__).'/../const.php';
        // gateway 进程,这里使用websocket协议
        $this->gatewayAddress = sprintf('websocket://%s',GW_GATEWAY_ADDRESS);
//        $gateway = new Gateway('protocol://ip:port');
    }
 
    public function index() {
        $gateway = new Gateway($this->gatewayAddress);
        // 设置名称,方便status时查看
        $gateway->name = GW_GATEWAY_NAME;
        // 设置进程数,gateway进程数建议与cpu核数相同
        $gateway->count = GW_GATEWAY_COUNT;
        // 本机ip,分布式部署时请设置成内网ip(非127.0.0.1)
        $gateway->lanIp = GW_LOCAL_HOST_IP;
        // 内部通讯起始端口,假如$gateway->count=4,起始端口为4000
        // 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口
        $gateway->startPort = GW_GATEWAY_START_PORT;
        // 服务注册地址
        $gateway->registerAddress = GW_REGISTER_ADDRESS;
 
        // 心跳间隔,单位:秒,0 表示不发送心跳检测
        $gateway->pingInterval = GW_GATEWAY_PING_INTERVAL;
        // 心跳数据
        $gateway->pingData = '{"type":"ping"}';
 
        /*
        // 当客户端连接上来时,设置连接的onWebSocketConnect,即在websocket握手时的回调
        $gateway->onConnect = function($connection) {
            $connection->onWebSocketConnect = function($connection , $http_header) {
                // 可以在这里判断连接来源是否合法,不合法就关掉连接
                // $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket链接
                if($_SERVER['HTTP_ORIGIN'] != 'http://kedou.workerman.net') {
                    $connection->close();
                }
                // onWebSocketConnect 里面$_GET $_SERVER是可用的
                // var_dump($_GET, $_SERVER);
            };
        };
        */
 
        // 如果不是在根目录启动,则运行runAll方法
        if(!defined('GLOBAL_START')) {
            Worker::runAll();
        }
    }
 
}

start_register.php内容



/* 
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

use GatewayWorker\Register;
use Workerman\Worker;
 
$registerInstance = new GwRegister();
call_user_func_array(array($registerInstance,'index'),array());
 
class GwRegister {
 
    public function __construct() {
        require_once dirname(__FILE__) . '/../../../vendor/autoload.php';
        include_once dirname(__FILE__).'/../const.php';
    }
 
    public function index() {
        // register 必须是text协议
        $registerAddress = sprintf('text://%s',GW_REGISTER_PROTOCOL);
        $register = new Register($registerAddress);
 
        // 如果不是在根目录启动,则运行runAll方法
        if(!defined('GLOBAL_START')) {
            Worker::runAll();
        }
    }
 
}

const.php内容



/* 
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

// 注册协议
define('GW_REGISTER_PROTOCOL','0.0.0.0:1236');
 
// 注册地址
define('GW_REGISTER_ADDRESS','127.0.0.1:1236');
 
// 网关地址
define('GW_GATEWAY_ADDRESS','127.0.0.1:2346');
 
// 网关起始端口
define('GW_GATEWAY_START_PORT','2900');
 
// 心跳检测间隔,单位:秒,0 表示不发送心跳检测
define('GW_GATEWAY_PING_INTERVAL',30);
 
// 本机ip,分布式部署时请设置成内网ip(非127.0.0.1)
define('GW_LOCAL_HOST_IP','127.0.0.1');
 
// 网关名称
define('GW_GATEWAY_NAME','Gateway001');
 
// worker进程名称
define('GW_WORKER_NAME','BusinessWorker001');
 
// Gateway进程数量,建议与CPU核数相同
define('GW_GATEWAY_COUNT',2);
 
// BusinessWorker进程数量,建议设置为CPU核数的1倍-3倍
define('GW_BUSINESS_WORKER_COUNT',6);
 
// Business业务处理类,可以带命名空间
define('GW_BUSINESS_EVENT_HANDLER','application\gatewayapp\controller\GwEvents');

start_for_win.bat内容

php run\start_register.php run\start_gateway.php run\start_businessworker.php
pause

你可能感兴趣的:(thinkphp)