当前位置: 首页 > 图文教程 > 网络编程 > PHP > PHP web开发HTTP协议中的KeepAlive

PHP
php zlib压缩和解压缩swf文件的代码
PHP 常用函数库和一些实用小技巧
php 时间计算问题小结
php 禁止页面缓存输出
PHP 地址栏信息的获取代码
据说是雅虎的一份PHP面试题附答案
PHP setcookie() cannot modify header information 的解决方法
Google PR查询接口checksum新算法
php zend 相对路径问题
php mssql 时间格式问题
php str_pad 函数使用详解
php strtotime 函数UNIX时间戳
php array_intersect()函数使用代码
PHP mkdir()定义和用法
php array_flip() 删除数组重复元素
php完全过滤HTML,JS,CSS等标签
php 删除数组元素
PHP $_SERVER详解
php 动态多文件上传
防止MySQL注入或HTML表单滥用的PHP程序

PHP web开发HTTP协议中的KeepAlive


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-09-28   浏览: 218 ::
收藏到网摘: n/a

这篇文章已经写完将近一年了,最近从历史邮件里面翻出来,和大家分享一下。

其中使用PHP实现持久的HTTP连接,让我费了很多心思。

曾经想过使用C语言编写一个PHP的扩展来实现,后来发现pfsockopen这个函数,让我豁然开朗,避免重新发明一个轮子,呵呵。

一,KeepAlive的概念:

参见 http://en.wikipedia.org/wiki/HTTP_persistent_connection

二,KeepAlive的客户端实现:

使用了PHP支持的 pfsockopen 来实现,参见:http://cn.php.net/pfsockopen

KeepAlive必要的Header有:

Connection: Keep-Alive
Content-Length: xxx

三,性能对比测试:

几种对比实现方式:

1,使用fsockopen来实现,读取body内容后,关闭连接,参见测试程序中的ohttp_get实现。
2,使用pfsockopen来实现,读取body内容后,不关闭连接,参见测试程序中的phttp_get实现。
3,php实现的file_get_contents
4,第三方测试工具ab

前三种测试在测试程序中都包含了。

测试用例 一:

前三种php实现的客户端单进程单线程请求lighttpd服务器一个16字节的静态文件。顺序请求10000次。
客户端与服务器部署在不同服务器,通过内网请求。

测试结果:

第一次:

[root@localhost ~]# /opt/bin/php tp.php
phttp_get: 5.3641529083252
ohttp_get: 8.1628580093384
file_get_contents: 12.217950105667

第二次:

[root@localhost ~]# /opt/bin/php tp.php
phttp_get: 5.033059835434
ohttp_get: 9.589075088501
file_get_contents: 12.775387048721

第三次:

[root@localhost ~]# /opt/bin/php tp.php
phttp_get: 5.0181269645691
ohttp_get: 8.2286441326141
file_get_contents: 11.089616060257

测试用例 二:

使用第三方工具ab来进行测试,-k参数开打开keepalive支持,不做并发测试,顺序请求10000次。
客户端与服务器部署在不同服务器,通过内网请求。

以下测试结果部分省略:

未打开keepalive:

[root@localhost ~]# ab -n 10000 -c 1 “http://10.69.2.206:8080/sms/ns2/save_msg.txt”

Finished 10000 requests

Concurrency Level: 1
Time taken for tests: 10.410467 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 2480000 bytes
HTML transferred: 160000 bytes
Requests per second: 960.57 [#/sec] (mean)
Time per request: 1.041 [ms] (mean)
Time per request: 1.041 [ms] (mean, across all concurrent requests)
Transfer rate: 232.55 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 30.0 0 3002
Processing: 0 0 0.4 0 9
Waiting: 0 0 0.3 0 9
Total: 0 0 30.0 0 3003

打开keepalive:

[root@localhost ~]# ab -k -n 10000 -c 1 “http://10.69.2.206:8080/sms/ns2/save_msg.txt”

Finished 10000 requests

Concurrency Level: 1
Time taken for tests: 4.148619 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Keep-Alive requests: 9412
Total transferred: 2527060 bytes
HTML transferred: 160000 bytes
Requests per second: 2410.44 [#/sec] (mean)
Time per request: 0.415 [ms] (mean)
Time per request: 0.415 [ms] (mean, across all concurrent requests)
Transfer rate: 594.66 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 5
Processing: 0 0 2.1 0 203
Waiting: 0 0 2.1 0 203
Total: 0 0 2.1 0 203

四,在实际中的应用

以上实现的phttp_get和mysql memcache的 中的“保持连接”概念类似,这种技术一般来说,只适用于fastcgi模式的web服务器。
对于本机之间的http通信,在测试过程中发现phttp_get的优势有限,基本合乎逻辑。
对于本身处理时间比较长的服务,phttp_get的优势也不明显。
综上,phttp_get适用于fastcgi模式的web应用调用远程http服务,且此http服务器响应时间比较短的情况。

五,服务端需要注意的事项

1,http服务器必须支持HTTP/1.1协议
2,php应用必须返回Content-Length:的header,具体实现参见:

http://cn.php.net/manual/en/function.ob-get-length.php

需要在代码中加入:

ob_start();
$size=ob_get_length();
header(”Content-Length: $size”);
ob_end_flush();

最后附上测试代码:

<?php

//$url=http://10.69.2.206:8080/sms/ns2/save_msg.txt

function ohttp_get($host,$port,$query,&$body)
{
$fp=pfsockopen($host,$port,$errno,$errstr,1);
if(!$fp)
{
var_dump($errno,$errstr);
return -1;
}
$out = “GET ${query} HTTP/1.1\r\n”;
$out.= “Host: ${host}\r\n”;
$out.= “Connection: close\r\n”;
$out.= “\r\n”;
fwrite($fp,$out);
$line=trim(fgets($fp));
$header.=$line;
list($proto,$rcode,$result)=explode(” “,$line);
$len=-1;
while( ($line=trim(fgets($fp))) != “” )
{
$header.=$line;
if(strstr($line,”Content-Length:”))
{
list($cl,$len)=explode(” “,$line);
}
if(strstr($line,”Connection: close”))
{
$close=true;
}
}
if($len < 0)
{
echo “ohttp_get must cope with Content-Length header!\n”;
return -1;
}
$body=fread($fp,$len);
if($close)
fclose($fp);
return $rcode;
}
function phttp_get($host,$port,$query,&$body)
{
$fp=pfsockopen($host,$port,$errno,$errstr,1);
if(!$fp)
{
var_dump($errno,$errstr);
return -1;
}
$out = “GET ${query} HTTP/1.1\r\n”;
$out.= “Host: ${host}\r\n”;
$out.= “Connection: Keep-Alive\r\n”;
$out.= “\r\n”;
fwrite($fp,$out);
$line=trim(fgets($fp));
$header.=$line;
list($proto,$rcode,$result)=explode(” “,$line);
$len=-1;
while( ($line=trim(fgets($fp))) != “” )
{
$header.=$line;
if(strstr($line,”Content-Length:”))
{
list($cl,$len)=explode(” “,$line);
}
if(strstr($line,”Connection: close”))
{
$close=true;
}
}
if($len < 0)
{
echo “phttp_get must cope with Content-Length header!\n”;
return -1;
}
$body=fread($fp,$len);
if($close)
fclose($fp);
return $rcode;
}

$time1=microtime(true);
for($i=0;$i<10000;$i++)
{
$host=”10.69.2.206″;
$port=8080;
$query=”/sms/ns2/save_msg.txt”;
$body=”";
$r=ohttp_get($host,$port,$query,$body);
if($r != 200)
{
echo “return code : $r\n”;
}
}
$time2=microtime(true);
for($i=0;$i<10000;$i++)
{
$url=”http://10.69.2.206:8080/sms/ns2/save_msg.txt”;
$host=”10.69.2.206″;
$port=8080;
$query=”/sms/ns2/save_msg.txt”;
$body=”";
$r=phttp_get($host,$port,$query,$body);
if($r != 200)
{
echo “return code : $r\n”;
}
}
$time3=microtime(true);
for($i=0;$i array( ‘timeout’ => 1 )
)
);
$body=file_get_contents($url, 0, $ctx);
$r=200;
if($r != 200)
{
echo “return code : $r\n”;
}
}
$time4=microtime(true);

echo “phttp_get: “.($time3-$time2).”\n”;
echo “ohttp_get: “.($time2-$time1).”\n”;
echo “file_get_contents: “.($time4-$time3).”\n”;

?>