今天在公司,用php写服务端的时候,因为项目中要做老师跟学生一对一辅导功能。然后学生上传问题图片之后在服务端找老师需要长时间的消耗,另外一个方面老师接收到学生的提问之后可能没即时看到或者没在电脑旁边(我们在后台指定老师10s的反应时间)我们会继续找寻下一个老师。这样的话客户端请求到API层面,并没有及时的返回结果,导致客户端代码进入了一个类似死循环里面,整个客户端代码直接崩溃。
但是业务需要这样,php又不能多线程,所以就考虑解决方案,最终选到了使用gearman。首先Gearman是一个用来把工作委派给其他机器、分布式的调用更适合做某项工作的机器、并发的做某项工作在多个调用间做负载均衡、或用来在调用其它语言的函数的系统。其他的东西,童鞋们可以google!
基于gearman可以直接安装php扩展,这样的话更方便一些,至于怎么安装扩展我就不一一介绍了,不对安装的同学可以百度!
我来大概说一下gearman吧。它呢就是生产者和消费者的关系。至于生产者 你可以认为你的API层,或者是业务层面。如果你有一系列动作在服务端需要长时间的去做,甚至超出了php脚本的有效执行时间。这个时候用到了gearman。你那个需要长时间执行的任务可以当作消费者放到gearman的任务队列里面去,api层面可以立即返回结果告诉客户端请求成功。至于后续任务里面如果有错误,或者其他提示的话 你可以用websocket或者你自己的容错机制来官方的告诉客户端提示用户。好了说了这么多上代码吧
public function imgtoteacher(){
$ret = array('err_no'=>1000,'err_msg'=>'system error');
do{
$must = array('subject_id','grade_id','url');
$fields = array();
$this->checkParams('post',$must,$fields);
$user = $this->checkUserSession();
$params = $this->params;
$data = array(
'user_id'=>$user['id'],
'subject_id'=>$params['subject_id'],
'grade_id'=>$params['grade_id'],
'create_time'=>time()
);
$res = $this->wmodel->create_question($data);
if($res){
$question_id = $res['id'];
//插入详情,问题图片一律存问题详情表
$detail = array(
'question_id'=>$question_id,
'img_url' =>$params['url'],
'create_time'=>time()
);
$flag = $this->wmodel->insert_detail($detail);
if(!$flag){
$ret = array('err_no'=>8002,'err_msg'=>'上传问题图片失败,请重试!');
break;
}
//调用gearman挂起找老师的服务
$_array = array('question_id'=>$question_id,'subject_id'=>$params['subject_id'],'grade_id'=>$params['grade_id'],'img_url'=>$params['url'],'send_id'=>$user['id']);
$client= new GearmanClient();
$client->addServer();
$client->doBackground("params", json_encode($_array));
//随机选出老师询问老师是否同意
// $single = $this->wmodel->select_teacher($params['subject_id'],$params['grade_id']);
// if($single){
// //给在线的这个老师发送socket请求询问是否同意
// $post_data = array("type" => "publish",
// "content" => array('type'=>1,'img_url'=>$params['url'],'student_id'=>$user['id'],'question_id'=>$question_id),
// "to" => $single['user_id']);
// $result = curlRequest($this->websocket_url,$post_data);
// }else{
// $ret = array('err_no'=>8003,'err_msg'=>'暂无符合科目年级的在线老师,请询问其他科目年级的问题!!');
// break;
// }
// if($result != 'ok'){
// $ret = array('err_no'=>8001,'err_msg'=>'websocket发送通知失败');
// break;
// }
$ret = array('err_no'=>0,'err_msg'=>'success');
break;
//$ret = $this->sleep_select($question_id,$params['subject_id'],$params['grade_id'],$params['url']);
}else{
$ret = array('err_no'=>8002,'err_msg'=>'上传问题图片失败,请重试!');
break;
}
}while(0);
$this->output($ret);
}
我拿我我自己的代码给大家解释。其中
$client= new GearmanClient();
$client->addServer();
$client->doBackground("params", json_encode($_array));
这一段是连接gearman ,addserver那一点我没写是因为它自己默认的就是当前服务器,这个我们可以认为是生产者。当然了如果你的消费者跟这个在同一个服务器的话 那个地方你可以空着不填写服务器地址。至于
client−>doBackground("params",jsonencode(
_array));这个地方尤其要注意那个参数一定要跟你的消费者保持一直第二个参数是你在消费者里面需要用到的参数。
下面我们再来说消费者
<?php
// _ooOoo_
// o8888888o
// 88" . "88
// (| -_- |)
// O\ = /O
// ____/`---'\____
// .' \\| |// `.
// / \\||| : |||// \
// / _||||| -:- |||||- \
// | | \\\ - /// | |
// | \_| ''\---/'' | |
// \ .-\__ `-` ___/-. /
// ___`. .' /--.--\ `. . __
// ."" '< `.___\_<|>_/___.' >'"".
// | | : `- \`.;`\ _ /`;.`/ - ` : | |
// \ \ `-. \_ __\ /__ _/ .-` / /
//======`-.____`-.___\_____/___.-`____.-'======
// `=---='
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//佛祖保佑 永无BUG 心外无法 法外无心 永不修改
$worker= new GearmanWorker();
$worker->addServer();
$worker->addFunction("params", "find_teacher");
while ($worker->work());
function find_teacher($job)
{
//return ucwords(strtolower($job->workload()));
$params = $job->workload();
$params = json_decode($params,true);
//这里需要你写一些业务处理
}
上面就是消费者代码。使用$job->workload()接收你传来的参数。然后你就可以写你的业务逻辑,处理你需要长时间处理的业务。
已上就是解决了异步处理任务的方法。具体gearman的一些参数和方法请访问
http://php.net/manual/en/book.gearman.php