最近在项目中碰到这样一个需求,PHP执行一些操作后,需要发送通知邮件,原来的代码如下:
$result_array = array('handle_action'=>$handle_action, 'result'=> YesOrNo::YES, 'message'=>'单据审批通过' ); echo json_encode($result_array); //处理完毕,邮件通知下一位处理人 $this->sendmailtonextaudit($bean_id, $bean_code, $status, $handle_action, $handle_opinion); exit();
但是发送邮件的这步操作$this->sendmailtonextaudit()大约需要6秒多,这样前台就必须等待了,为了实现前台无需等待的需求,考虑将发送邮件的代码放到后台去执行,处理代码如下:
$result_array = array('handle_action'=>$handle_action, 'result'=> YesOrNo::YES, 'message'=>'单据审批通过' ); while(ob_get_level()) { ob_end_clean();//清除之前开启了的缓冲区 } header('HTTP/1.1 200 Ok'); header('Connection:close');//告诉浏览器,连接关闭了,这样浏览器就不用等待服务器的响应 header("Content-Encoding: none"); ignore_user_abort(true); ob_start(); echo json_encode($result_array); $size = ob_get_length(); header("Content-Length:$size");//告诉浏览器这个网页文件的长度,只有这样浏览器才会接收到相应长度的信息后中断连接 ob_end_flush();//输出当前缓冲 flush();//输出PHP缓冲 sleep(30);//休眠PHP,让php把前面的输出作完 set_time_limit(0);//不受时间限制 //处理完毕,邮件通知下一位处理人 $this->sendmailtonextaudit($bean_id, $bean_code, $status, $handle_action, $handle_opinion); exit();
相应前台代码(是用ajax调用的):
$.ajax({ type:"POST", url:"/public/common/ajaxaudittrade", data:params, success: function(res){ var obj = eval( "(" + res + ")" ); alert(obj.message);//这一步可以很快返回 toUrl('/trade/approved/index'); } });
以上颜色加深的alert能够很快执行(不到1秒),这说明后台处理是正确的,sleep之前的内容已输出。现在问题是,后面的toUrl不会马上执行,必须等到sleep之后的代码执行完毕,才能执行toUrl。看到这里,我原先以为是前后台连接没有断开,浏览器一直在等待服务器(Apache)的响应,后来发现不是这样的。这里的连接确实已断开,之所以toUrl一直在等待,并不是它没被执行,而是执行了在等待响应而已,说的有点迷糊了吧,下面来解释下。
web访问中,每一个会话(session)对应一个进程,上面说到的sleep后面要执行的代码和toUrl请求执行的代码,是在同一个会话下,所以它们属于同一个进程。这就导致sleep不执行完,toUrl不会执行。所以现在考虑让sleep后面的代码启用新的进程来执行,这就像“守护进程”一样,要做到:
1.让新进程摆脱原会话的控制;
2.让新进程摆脱原进程组的控制;
3.让新进程摆脱原控制终端的控制;
关于PHP中“守护进程”的用法,正在摸索中,弄明白后,会补充到这个帖子中。
PS:以上纯属个人见解,如有不对,请猛喷之。