## 场景要求 客户端调用服务器a.php接口,需要执行一个长达5s-20s不等的耗资源操作,但是客户端响应请求时间为5秒(微信公众账号服务器请求响应超时时间),5s以上无回复即断开连接。 ## 解决设想 客户端调用a.php之后,a.php执行异步多线程操作调用b.php,a.php调用成功后即刻反馈给客户端回执,b.php自动执行耗资源操作。 ## 难点 PHP没有真正实现多线程操作的方法。所以需要通过其它手段来进行模拟多线程。 ## 方案一 利用CURL非阻塞调用b.php,实现过程可以参考 http://blog.csdn.net/linvo/article/details/5956629 但是有一个问题,就是a.php会继续等待b.php的响应。 于是临时想了一个解决方案: 在此处代码中,将$curlopt_timeout改为1 ``` /** * 单个CURL调用超时限制 */ public $curlopt_timeout = 1; private $param = array(); ```   但是这样做就违背了curl本身的逻辑限制。 ## 方案二 利用socket 在a.php中加入以下代码 ``` $fp = fsockopen("test.com", 80, $errno, $errstr, 30); if (!$fp){ echo 'error fsockopen'; } else{ stream_set_blocking($fp,0); $http = "GET /test/b.php HTTP/1.1\r\n"; $http .= "Host: test.com\r\n"; $http .= "Connection: Close\r\n\r\n"; fwrite($fp,$http); fclose($fp); } ``` 即可实现a.php调用b.php无阻塞。 代码中stream_set_blocking函数用来设定socket链接为无阻塞方式(默认为阻塞)。 ## 问题 在使用方案二以后,遇到了一个问题,即客户端短时间内多次调用a.php,出现部分请求 没有执行b.php 的情况。 解决方法: 在Nginx的nginx.conf文件中,查看worker_processes为1,判断服务端响应请求的线程启动限制太大,得知服务器本身配置为双核CPU,判断2-4线程比较合适,于是修改worker_processes为4.问题得到解决! ``` 报错:stream_set_blocking() expects parameter 1 to be resource, null given 解决方法,修改php.ini函数设置,找到 disable_functions = 将proc_open和stream_socket_server两个参数删除 重启php ``` 完整代码: ``` /** * @parem $url 网页地址 http://www.test.com/test/test.php * @parem $port 网址端口 默认80 * @parem $t 脚本请求时间 默认30s * @parem $method 请求方式 get/post * @parem $data ['test'=>'1'] * */ public function asyncPHP($url, $port = 80, $t = 30, $method = 'get', $data = null) { $info = parse_url($url); $fp = fsockopen($info["host"], $port, $errno, $errstr, $t); //stream_set_blocking函数用来设定socket链接为无阻塞方式 stream_set_blocking($fp, 0); // 判断是否有数据 if (isset($data) && !empty($data)) { $query = http_build_query($data); // 数组转url 字符串形式 } else { $query = null; } // 判断 请求方式 if ($method == 'post') { $head = "POST " . $info['path'] . " HTTP/1.0" . PHP_EOL; } else { $head = "GET " . $info['path'] . "?" . $query . " HTTP/1.0" . PHP_EOL; } $head .= "Host: " . $info['host'] . PHP_EOL; // 请求主机地址 $head .= "Referer: " . $url . PHP_EOL; if (isset($data) && !empty($data) && ($method == 'post')) { $head .= "Content-type: application/x-www-form-urlencoded" . PHP_EOL; $head .= "Content-Length: " . strlen(trim($query)) . PHP_EOL; $head .= PHP_EOL; $head .= trim($query); } else { $head .= PHP_EOL; } fwrite($fp, $head); fclose($fp); } ```