PHP之异步处理

个人理解在项目中使用消息队列一般是有如下几个原因:

1.把瞬间服务器的请求处理换成异步处理,缓解服务器的压力

2.实现数据顺序排列获取

  • redis 队列 实现 异步处理的效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
/**
* 请求过来以后,如果数据过大,导致响应速度过慢,则可以先把要处理的数据保存到redis中, 然后直接去响应
*/
//redis数据入队操作
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
for($i=0;$i<50;$i++){
try{
$redis->LPUSH('click',rand(1000,5000));
}catch(Exception $e){
echo $e->getMessage();
}
}

在后台进行数据处理 守护进程 去处理数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
//redis数据出队操作,从redis中将请求取出
$redis = new Redis();
$redis->pconnect('127.0.0.1',6379);
while(true){
try{
$value = $redis->LPOP('click');
if(!$value){
break;
}
var_dump($value)."\n";
/*
* 利用$value进行逻辑和数据处理
*/
}catch(Exception $e){
echo $e->getMessage();
}
}

  • 使用ajax 去处理  

1
2
3
// 使用ajax 去请求另外一个方法
$.get("doAsync.php", { name: 'raykaeso',job:'PHP Programmer'});

  • 使用popen,执行本地文件

1
2
3
<?php
pclose(popen('php /var/www/doAsync.php &', 'r'));

  • 使用curl

设置curl的超时时间 CURLOPT_TIMEOUT 为1 (最小为1),因此客户端需要等待1秒,curl请求地址必须为绝对路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

$param = array(
'name'=>'raykaeso',
'job'=>'PHP Programmer'
);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"http://www.example.com/doAsync.php");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($param)); //将数组转换为URL请求字符串
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 1);
curl_exec($ch);
curl_close($ch);


  • 使用fsockopen,需要自己拼接header部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
function doRequest($url, $param=array(),$timeout =10){
$urlParmas = parse_url($url);
$host = $urlParmas['host'];
$path = $urlParmas['path'];
$port = isset($urlParmas['port'])? $urlParmas['port'] :80;
$errno = 0;
$errstr = '';

$fp = fsockopen($host, $port, $errno, $errstr, $timeout);
$query = isset($param)? http_build_query($param) : '';
$out = "POST ".$path." HTTP/1.1\r\n";
$out .= "host:".$host."\r\n";
$out .= "content-length:".strlen($query)."\r\n";
$out .= "content-type:application/x-www-form-urlencoded\r\n";
$out .= "connection:close\r\n\r\n";
$out .= $query;

fputs($fp, $out);
fclose($fp);
}
$url = 'http://www.example.com/doAsync.php';
$param = array(
'name'=>'raykaeso',
'job'=>'PHP Programmer'
);
doRequest($url, $param);

注意:

1、如果使用Apache作为Web服务器,让PHP支持异步首先必须得在Apache配置文件httpd.conf配置enablesendfile on。

2、在异步执行的PHP文件中建议加上一下两个配置:

1
2
3
4

ignore_user_abort(true); // 忽略客户端断开
set_time_limit(0); // 设置执行不超时

最后,引入 鸟哥 的 经典 fsockopen 分装 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php

function triggerRequest($url, $post_data = array(), $cookie = array()){
$method = "GET"; //可以通过POST或者GET传递一些参数给要触发的脚本
$url_array = parse_url($url); //获取URL信息,以便平凑HTTP HEADER
$port = isset($url_array['port'])? $url_array['port'] : 80;

$fp = fsockopen($url_array['host'], $port, $errno, $errstr, 30);
if (!$fp) …{
return FALSE;
}
$getPath = $url_array['path'] ."?". $url_array['query'];
if(!empty($post_data))…{
$method = "POST";
}
$header = $method . " " . $getPath;
$header .= " HTTP/1.1\r\n";
$header .= "Host: ". $url_array['host'] . "\r\n "; //HTTP 1.1 Host域不能省略
/**//*以下头信息域可以省略
$header .= "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13 \r\n";
$header .= "Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,q=0.5 \r\n";
$header .= "Accept-Language: en-us,en;q=0.5 ";
$header .= "Accept-Encoding: gzip,deflate\r\n";
*/

$header .= "Connection:Close\r\n";
if(!empty($cookie))…{
$_cookie = strval(NULL);
foreach($cookie as $k => $v){
$_cookie .= $k."=".$v."; ";
}
$cookie_str = "Cookie: " . base64_encode($_cookie) ." \r\n";//传递Cookie
$header .= $cookie_str;
}
if(!empty($post_data)){
$_post = strval(NULL);
foreach($post_data as $k => $v)…{
$_post .= $k."=".$v."&";
}
$post_str = "Content-Type: application/x-www-form-urlencoded\r\n";//POST数据
$post_str .= "Content-Length: ". strlen($_post) ." \r\n";//POST数据的长度
$post_str .= $_post."\r\n\r\n "; //传递POST数据
$header .= $post_str;
}
fwrite($fp, $header);
//echo fread($fp, 1024); //我们不关心服务器返回
fclose($fp);
return true;
}