Worker储存客户端连接、跨进程通讯的问题研究
1.首先,该wiki是基于公司的项目《[[室内导航解决方案]]》的构架上,对服务端代码的更改。
2. 其次本人是ue4、c++出生,对php从来没有了解过。在了解workman之前我对php的认识只是知道使用php连接数据库,然后通过get返回数据给c++使用。对php的语法,代码规范等都不了解。所以有哪些地方不对的欢迎指正。一:worker单进程时,数组变量的共用问题
需求:我们在处理客户端的连接时需要将客户端链接分为微信端和Web端两大类,同时它们是一一对应的,即一个微信端需要对应一个web端。思路:我们的思路是建立两个数组($WX=array();$Web=array())。将所有的微信连接存在$WX里面,将所有的Web连接存在Web里面。
遇到的问题:这两个数组并没有一直存储起来。连接到来时明确的存进了数组里面,但消息到来时这两个数组都为空。
解决办法:①将这两个数组定义到worker类里面去。②:在自己的运行类中每个函数的第一句加上“global $ws_worker;”使我们的$ws_worker成为全局的。(用c++的语法,简直难以理解这种写法!)
//在库文件worker.php里面添加、声明两个数组
//定义两个数组用于存储所有的WX连接,所有的Web连接
public $WX=array();
public $Web=array();
//在我们自己写的run.php里面
//每个函数里面申明我们的全局worker
$ws_worker->onMessage = function($connection, $data)
{
global $ws_worker;
......
}
我们在这样处理后,可以很好的保存这些链接,方便当一个wx客户端消息到来时,我们找到对应的Web进行消息转发。但是还是只是局限于$ws_worker->count = 1;开启一个进程时。
二: worker多个进程时,进程间的相互通讯
需求:我们在利用worker做服务端时,针对连接量比较少时,采用$ws_worker->count = 1;完全可以处理。但是如果说遇到连接量比较大项目,那么$ws_worker->count = 4;还是很有必要的。思路:采用基于worker的组件 Channel进行各个线程间的通讯。Channel就是两个php文件Client.php、Server.php。下载就可以用了
解决办法:①:在worker进程启动时“onWorkerStart”这个函数里面进行事件的注册(我理解为注册)。②在需要线程间通讯时我们对注册的对应事件进行调用“\Channel\Client::publish('Findandclose', $event_data);”
// 每个worker进程启动时(这个函数是进程间通讯,事件注册用)
$ws_worker->onWorkerStart = function($ws_worker)
{
//需要跨线程做的事都写在这里
// Channel客户端连接到Channel服务端
Channel\Client::connect('127.0.0.1', 2206);
// 注册"Findandforward"事件,用于消息转发时当前进程找不到对应连接时调用
Channel\Client::on('Findandforward', function($event_data)use($ws_worker){
if($event_data->type == 'Message')
{
if($event_data->from == 'WX')
{
foreach ($ws_worker->Web as $everyweb)
{
if($event_data->openid == $everyweb['openid'])
{
$everyweb['connect']->send($event_data->data);
break;
}
}
}
}
});
// 注册"Findandclose"事件,用于连接断开时当前进程找不到对应连接
Channel\Client::on('Findandclose', function($event_data)use($ws_worker){
if($event_data['Who_closed']== 'WX_closed')
{
foreach( $ws_worker->Web as $key => $value ) {
if($value['openid'] ==$event_data['openid'])
{
$ws_worker->Web[$key]['connect']->close();
array_splice($ws_worker->Web,$key,1);//重置数组
var_dump($ws_worker->Web);
savelog("在其它进程找到了需要关闭的Web连接"." ".$event_data['openid']);
break;
}
}
}
if($event_data['Who_closed']== 'Web_closed')
{
foreach( $ws_worker->WX as $key => $value ) {
if($value['openid'] ==$event_data['openid'])
{
$ws_worker->WX[$key]['connect']->close();
array_splice($ws_worker->WX,$key,1);//重置数组
var_dump($ws_worker->WX);
savelog("在其它进程找到了需要关闭的WX连接"." ".$event_data['openid']);
break;
}
}
}
});
};
//调用我们的跨线程事件
if($jsondata->from == 'WX')
{
savelog("WX is sen Message the WX id is"." ".$jsondata->openid);
//循环$Web匹配本次消息的openid对应有就将小程序的消息转发给web
$isfind_theweb=false;//定义一个Bool变量,判断在当前线程是否找到对应的连接
foreach ($ws_worker->Web as $everyweb) {
if($jsondata->openid == $everyweb['openid'])
{
$isfind_theweb=true;//当前线程找到了对应连接
$everyweb['connect']->send($jsondata->data);
break;
}
}
if(!$isfind_theweb)//如果在这个进程没有找到对应的web,我们发送全局事件
{
savelog("当前进程未找到匹配连接");
$event_data = $jsondata;
// 向所有worker进程发布Findandforward事件
\Channel\Client::publish('Findandforward', $event_data);//Findandforward是我们要调用的事件名,$event_data是我们要传递的参数
}
}