请教各位大神一个问题
我用GatewayWorker做了一个websocket定时向客户端发送数据的功能,通过利用定时器定时从某服务器获取数据,并发送给客户端。
且客户端可以切换发送数据的内容,每次切换回关掉之前的定时器后,再打开一个新的定时器。但是当我切换过快的话,却发现之前定时器没有被关闭,仍在发送数据,这样相当于发送了两次数据(比如,定时器时间间隔是5s执行一次,这时,出现的情况是相当于5s内会执行两次)。。(我测试了一下,如果我把切换的时间控制在2s的样子,则不会出现这个问题)
这是什么原因,应该如何解决?
在论坛中看见,说是多进程造成的,那我应该如何去把定时器放在同一个进程中。第一次玩GatewayWorker,还请大神指点一二
public static function onClose($client_id)
{
// 向所有人发送
global $this_client_id,$closed_data,$change2closed;
Gateway::sendToAll(json_encode('closing'));
if ($change2closed === true){
$preBName = $closed_data['get']['preBName'];
$pre_timer = $closed_data['get']['add_timer'];
$BName = $closed_data['get']['BName'];
}else{
$preBName = $GLOBALS['client_id_groupName'][$client_id];
$pre_timer = $GLOBALS[$preBName."_timer"];
}
$pre_countGroup = Gateway::getClientCountByGroup($preBName);
if ($pre_countGroup==0){
GateWay::sendToAll(json_encode('To all start_deleting'));
Gateway::sendToClient($this_client_id,json_encode('start_deleting'));
$del_result = \Workerman\Lib\Timer::del($pre_timer);
if ($del_result===false){
GateWay::sendToAll(json_encode(array(
'error' => 'del error!',
'preBName' => $preBName,
'pre_timer' => $pre_timer
)));
}
unset($GLOBALS[$preBName."_timer"]);
unset($GLOBALS['client_id_groupName'][$client_id]);
}
if ($change2closed === true){
$check_countByGroup = Gateway::getClientCountByGroup($BName);
if ($check_countByGroup!=0){
Gateway::joinGroup($this_client_id,$BName);
Gateway::sendToClient($this_client_id,json_encode(array(
'type' => 'addTimer',
'client_id' => $this_client_id,
'timer' => $GLOBALS[$BName."_timer"]
)));
Events::getXmlInfo($this_client_id,$closed_data,5);
}else{
Gateway::joinGroup($this_client_id,$closed_data['get']['BName']);
Events::getXmlInfo($this_client_id,$closed_data,5);
//调用定时函数为类的静态方法,具体可参考文档wokerman定时器手册
$timer = \Workerman\Lib\Timer::add(5,array('Events','getXmlInfo'),array($this_client_id,$closed_data,5));
$GLOBALS[$closed_data['get']['BName']."_timer"] = $timer;
Gateway::sendToClient($this_client_id,json_encode(array(
'type' => 'addTimer',
'client_id' => $this_client_id,
'timer' => $timer
)));
}
$change2closed = false;
Gateway::sendToClient($this_client_id,json_encode('close_succeed'));
}
}[/code]
[code] /*当客户端连接上gateway完成websocket握手时触发的回调函数。*/
public static function onWebSocketConnect($client_id,$data){
$httpReal = ['http://local.test.com','http://mytest.test1.cn'];
if (array_search($data['server']['HTTP_ORIGIN'],$httpReal) === false){
Gateway::sendToClient($client_id,"HTTP_ORIGIN为".$data['server']['HTTP_ORIGIN']."与".$httpReal."不一致,请修改httpReal与HTTP_ORIGIN一致");
Gateway::closeClient($client_id);
}else{
global $change2closed;
$change2closed = false;
$pre_cid = $data['get']['pre_cid'];
if ($pre_cid != "null"){//主要用于做同平台账号切换时,通过判断pre_cid(即client_id)是否为null,null则为第一次加载(即首次通过浏览器窗口打开此页面);若果不为空,则关闭这个client
global $this_client_id,$closed_data;
$this_client_id = $client_id; //设置一个全局可供调用的当前client_id供on_Close触发时,可以调用到正在执行中的client_id
$closed_data = $data; //设置一个全局可供调用的$closed_data供on_Close触发时,可以调用到本次传过来的$data(主要是使用$data['get']中的数据)
$change2closed = true;
Gateway::sendToClient($client_id,json_encode('start closeClient'));
Gateway::closeClient($pre_cid);
}else{
$check_countByGroup = Gateway::getClientCountByGroup($data['get']['BName']);
Gateway::sendToClient($client_id,json_encode(array(
'first-load' => '**********************',
'check_countByGroup' => $check_countByGroup
)));
if ($check_countByGroup!=0){
Gateway::joinGroup($client_id,$data['get']['BName']);
$GLOBALS['client_id_groupName'][$client_id] = $data['get']['BName'];
Gateway::sendToClient($client_id,json_encode(array(
'type' => 'addTimer',
'client_id' => $client_id,
'timer' => $GLOBALS[$data['get']['BName']."_timer"]
)));
Events::getXmlInfo($client_id,$data,5);
}else{
Gateway::joinGroup($client_id,$data['get']['BName']);
$GLOBALS['client_id_groupName'][$client_id] = $data['get']['BName'];
Events::getXmlInfo($client_id,$data,5);
//调用定时函数为类的静态方法,具体可参考文档wokerman定时器手册
if ($GLOBALS[$data['get']['BName']."_timer"] === null){
$timer = \Workerman\Lib\Timer::add(5,array('Events','getXmlInfo'),array($client_id,$data,5));
$GLOBALS[$data['get']['BName']."_timer"] = $timer;
}
Gateway::sendToClient($client_id,json_encode(array(
'type' => 'addTimer',
'client_id' => $client_id,
'timer' => $timer
)));
}
}
}
}
这里有一个 全局定时器项目,你可以试下
https://github.com/walkor/global-timer
你好,我是在Event.php中的onWebSocketConnect中设置的定时器,我需要在每次建立websocket连接时判断,当前分组是否有client_id在线,如果有,则直接将新的client_id加入该分组,无需再创建定时器,如果该分组没有client_id在线,则新的client_id加入分组,同时创建定时器。
所以,我这种情况,我应该怎么去应用,我看了一下你发我的这个全局定时器,不是很明白在我的项目中应该如何去应用,是否可以详细说一下。
代码如下:
public static function onClose($client_id)
{
// 向所有人发送
global $this_client_id,$closed_data,$change2closed;
Gateway::sendToAll(json_encode('closing'));
if ($change2closed === true){
$preBName = $closed_data['get']['preBName'];
$pre_timer = $closed_data['get']['add_timer'];
$BName = $closed_data['get']['BName'];
}else{
$preBName = $GLOBALS['client_id_groupName'][$client_id];
$pre_timer = $GLOBALS[$preBName."_timer"];
}
$pre_countGroup = Gateway::getClientCountByGroup($preBName);
if ($pre_countGroup==0){
GateWay::sendToAll(json_encode('To all start_deleting'));
Gateway::sendToClient($this_client_id,json_encode('start_deleting'));
$del_result = \Workerman\Lib\Timer::del($pre_timer);
if ($del_result===false){
GateWay::sendToAll(json_encode(array(
'error' => 'del error!',
'preBName' => $preBName,
'pre_timer' => $pre_timer
)));
}
unset($GLOBALS[$preBName."_timer"]);
unset($GLOBALS['client_id_groupName'][$client_id]);
}
if ($change2closed === true){
$check_countByGroup = Gateway::getClientCountByGroup($BName);
if ($check_countByGroup!=0){
Gateway::joinGroup($this_client_id,$BName);
Gateway::sendToClient($this_client_id,json_encode(array(
'type' => 'addTimer',
'client_id' => $this_client_id,
'timer' => $GLOBALS[$BName."_timer"]
)));
Events::getXmlInfo($this_client_id,$closed_data,5);
}else{
Gateway::joinGroup($this_client_id,$closed_data['get']['BName']);
Events::getXmlInfo($this_client_id,$closed_data,5);
//调用定时函数为类的静态方法,具体可参考文档wokerman定时器手册
$timer = \Workerman\Lib\Timer::add(5,array('Events','getXmlInfo'),array($this_client_id,$closed_data,5));
$GLOBALS[$closed_data['get']['BName']."_timer"] = $timer;
Gateway::sendToClient($this_client_id,json_encode(array(
'type' => 'addTimer',
'client_id' => $this_client_id,
'timer' => $timer
)));
}
$change2closed = false;
Gateway::sendToClient($this_client_id,json_encode('close_succeed'));
}
}
public static function onWebSocketConnect($client_id,$data){
$httpReal = [''http://local.test.com','http://mytest.test1.cn'];
if (array_search($data['server']['HTTP_ORIGIN'],$httpReal) === false){
Gateway::sendToClient($client_id,"HTTP_ORIGIN为".$data['server']['HTTP_ORIGIN']."与".$httpReal."不一致,请修改httpReal与HTTP_ORIGIN一致");
Gateway::closeClient($client_id);
}else{
global $change2closed;
$change2closed = false;
$pre_cid = $data['get']['pre_cid'];
if ($pre_cid != "null"){//主要用于做同平台账号切换时,通过判断pre_cid(即client_id)是否为null,null则为第一次加载(即首次通过浏览器窗口打开此页面);若果不为空,则关闭这个client
global $this_client_id,$closed_data;
$this_client_id = $client_id; //设置一个全局可供调用的当前client_id供on_Close触发时,可以调用到正在执行中的client_id
$closed_data = $data; //设置一个全局可供调用的$closed_data供on_Close触发时,可以调用到本次传过来的$data(主要是使用$data['get']中的数据)
$change2closed = true;
Gateway::sendToClient($client_id,json_encode('start closeClient'));
Gateway::closeClient($pre_cid);
}else{
$check_countByGroup = Gateway::getClientCountByGroup($data['get']['BName']);
Gateway::sendToClient($client_id,json_encode(array(
'first-load' => '**',
'check_countByGroup' => $check_countByGroup
)));
if ($check_countByGroup!=0){
Gateway::joinGroup($client_id,$data['get']['BName']);
$GLOBALS['client_id_groupName'][$client_id] = $data['get']['BName'];
Gateway::sendToClient($client_id,json_encode(array(
'type' => 'addTimer',
'client_id' => $client_id,
'timer' => $GLOBALS[$data['get']['BName']."_timer"]
)));
Events::getXmlInfo($client_id,$data,5);
}else{
Gateway::joinGroup($client_id,$data['get']['BName']);
$GLOBALS['client_id_groupName'][$client_id] = $data['get']['BName'];
Events::getXmlInfo($client_id,$data,5);
//调用定时函数为类的静态方法,具体可参考文档wokerman定时器手册
if ($GLOBALS[$data['get']['BName']."_timer"] === null){
$timer = \Workerman\Lib\Timer::add(5,array('Events','getXmlInfo'),array($client_id,$data,5));
$GLOBALS[$data['get']['BName']."_timer"] = $timer;
}
Gateway::sendToClient($client_id,json_encode(array(
'type' => 'addTimer',
'client_id' => $client_id,
'timer' => $timer
)));
}
}
}
}
代码太乱看不清。
请问你能发个邮箱给我不,我发你邮箱 我整理下发给你 看可以吗
我修改了一下原贴,把代码站上去了,你看这样子好点不
全局定时器用法和普通定时器用法一样
那我想再问下,我是否还需要像例子中说的($worker->id === 0)去指定进程id呢,如果还需要指定的话,例子中是在onWorkerStart中去指定的进程id,但是我是想在onWebSocketConnect中去指定,这个我如何去拿到类似的$worker这个参数?
cal_WebSite\GatewayWorker\vendor\workerman\gateway-worker\src\Lib\GlobalTimer.php on line 40
dor\workerman\gateway-worker\src\Lib\GlobalTimer.php on line 40
Worker process terminated with ERROR: E_ERROR "Class 'Channel\Client' not found in G:\PHPStudy\Local_WebSite\GatewayWorker\vendor\workerman\
gateway-worker\src\Lib\GlobalTimer.php on line 40"
process G:\PHPStudy\Local_WebSite\GatewayWorker\Applications\YourApp\start_businessworker.php terminated and try to restart
accountType none 4 [ok]
我把全局定时器部署后,连接切换时会报这个,请问是什么原因?
另外,还有一个很奇怪的问题就是,我现在的GatewayWorker和我的网站使用的是两个不同的站点(即网站域名和js中的ws:xxx:7272的域名不同),部署在两台不同的服务器上时,就会出现这个问题。当我把GatewayWorker和我的网站部署在同一台服务器上(即网站域名和js中的ws:xxx:7272的域名相同)时,则不会出现帖子最先说的发送两次数据的问题