WebjxCom提示:对于本机之间的http通信
,在测试过程中发现phttp_get的优势有限
,基本合乎逻辑
。对于本身处理时间比较长的服务,phttp_get的优势也不明显
。综上,phttp_get适用于fast
cgi模式的web应用调用远程http服务,且此http
服务器响应时间比较短的情况。  
  这篇文章已经写完将近一年了,最近从历史邮件里面翻出来,和大家分享一下。  
  其中使用
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/phptp.php  
  phttp_get:5.3641529083252  
  ohttp_get:8.1628580093384  
  file_get_contents:12.217950105667  
  第二次:  
  [root@localhost~]#/opt/bin/phptp.php  
  phttp_get:5.033059835434  
  ohttp_get:9.589075088501  
  file_get_contents:12.775387048721  
  第三次:  
  [root@localhost~]#/opt/bin/phptp.php  
  phttp_get:5.0181269645691  
  ohttp_get:8.2286441326141  
  file_get_contents:11.089616060257  
  测试用例二:  
  使用第三方工具ab来进行测试,-k参数开打开keepalive支持,不做并发测试,顺序请求10000次。  
  客户端与服务器部署在不同服务器,通过内网请求。  
  以下测试结果部分省略:  
  未打开keepalive:  
  [root@localhost~]#ab-n10000-c1“http://10.69.2.206:8080/sms/ns2/save_msg.txt”  
  Finished10000requests  
  ConcurrencyLevel:1  
  Timetakenfortests:10.410467seconds  
  Completerequests:10000  
  Failedrequests:0  
  Writeerrors:0  
  Totaltransferred:2480000bytes  
  HTMLtransferred:160000bytes  
  Requestspersecond:960.57[#/sec](mean)  
  Timeperrequest:1.041[ms](mean)  
  Timeperrequest:1.041[ms](mean,acrossallconcurrentrequests)  
  Transferrate:232.55[Kbytes/sec]received  
  ConnectionTimes(ms)  
  minmean[+/-sd]medianmax  
  Connect:0030.003002  
  Processing:000.409  
  Waiting:000.309  
  Total:0030.003003  
  打开keepalive:  
  [root@localhost~]#ab-k-n10000-c1“http://10.69.2.206:8080/sms/ns2/save_msg.txt”  
  Finished10000requests  
  ConcurrencyLevel:1  
  Timetakenfortests:4.148619seconds  
  Completerequests:10000  
  Failedrequests:0  
  Writeerrors:0  
  Keep-Aliverequests:9412  
  Totaltransferred:2527060bytes  
  HTMLtransferred:160000bytes  
  Requestspersecond:2410.44[#/sec](mean)  
  Timeperrequest:0.415[ms](mean)  
  Timeperrequest:0.415[ms](mean,acrossallconcurrentrequests)  
  Transferrate:594.66[Kbytes/sec]received  
  ConnectionTimes(ms)  
  minmean[+/-sd]medianmax  
  Connect:000.105  
  Processing:002.10203  
  Waiting:002.10203  
  Total:002.10203  
  四,在实际中的应用  
  以上实现的phttp_get和
mysqlmemcache的中的“保持连接”概念类似,这种技术一般来说,只适用于fast
cgi模式的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  
  functionohttp_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
”;  
  $out.=“Host:${host}
”;  
  $out.=“Connection:close
”;  
  $out.=“
”;  
  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_getmustcopewithContent-Lengthheader!
”;  
  return-1;  
  }  
  $body=fread($fp,$len);  
  if($close)  
  fclose($fp);  
  return$rcode;  
  }  
  functionphttp_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
”;  
  $out.=“Host:${host}
”;  
  $out.=“Connection:Keep-Alive
”;  
  $out.=“
”;  
  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_getmustcopewithContent-Lengthheader!
”;  
  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“returncode:$r
”;  
  }  
  }  
  $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“returncode:$r
”;  
  }  
  }  
  $time3=microtime(true);  
  for($i=0;$iarray(‘timeout’=>1)  
  )  
  );  
  $body=file_get_contents($url,0,$ctx);  
  $r=200;  
  if($r!=200)  
  {  
  echo“returncode:$r
”;  
  }  
  }  
  $time4=microtime(true);  
  echo“phttp_get:“.($time3-$time2).”
”;  
  echo“ohttp_get:“.($time2-$time1).”
”;  
  echo“file_get_contents:“.($time4-$time3).”
”;  
  ?>