安装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
目录结构如下
业务文件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