PHP fsockopen受服务器KeepAlive影响的解决

在开发过程中常常遇到这样的需求,模拟浏览器访问某接口,并获取返回数据。我们比较常使用的方法是fsockopen与接口建立连接,然后发出指令,然后通过fgets接受返回值。

但是我们发现,通过PHP模拟访问接口往往比浏览器访问同样的接口慢很多。这个问题困扰过我很久,今天终于找到原因了。我看网上很多朋友有同样的问题,分享出来供大家参考。

我们常常写这样的代码:

while(!feof($sHnd)) {
    $line = fgets($sHnd, 4096);
}

fgets会获取文件描述符$sHnd的当前的4096(也可能是别的常数)个字节,如果还没有到4096个字节遇到换行符了,则只返回换行符及换行符之前的内容。

许多文档教程里也都是这么讲的,这段代码许多情况下也能正常执行。我一步一步跟踪PHP语句的耗时,发现前面若干次的fgets都很快,最耗时的是最后一次fgets,有时长达几秒,有时长达十几秒。

原来这是服务器的KeepAlive功能造成的,Apache有这么一个设置(nginx等其他web服务器也都有):KeepAlive,如果这个设置置为On,则完成一次请求后,服务器并不会关闭TCP连接,而是保持连接等待浏览器下次发起其他请求时直接利用这个连接。但是当fgets获取最后一段内容时没有发现换行符,也没有文件结束标志(feof()),所以fgets获取完内容后仍继续等待,希望遇到换行符或者其他内容以达到4096个字符。于是,就这样服务器和PHP耗上了,互相等待。耗了一会后,服务器先耗不起了,毕竟服务器的连接数很宝贵,当连接若干秒没有活动,就会关掉这个连接(Apache通过KeepAliveTimeout这个选项进行设置,这个值通常为5-15)。服务器关掉连接之后,PHP这边的fgets这才失落得返回最后一批内容,访问接口过程结束。

清楚了慢的原因就知道了解决方案了:

服务器返回的HTTP头中包含有内容长度这个属性,当已接受的内容长度与之相等时,我们就可以断定:接口内容已经获取完毕,不必再等了。具体做法是:每次获取不超过剩余总长度的内容(min(4096, $leftlength))。剩余总长度为0时,跳出while(feof($xxxxx))的循环。

经过这样的修改,php通过sock方式访问接口速度慢的问题已经基本解决了,但还不够完美,继续速度优化的思路还在KeepAlive上。

大家都知道访问接口的耗时相当一部分是浪费在建立连接上,如果我们需要频繁调用接口的话,还有很大的优化余地。既然服务器保持了连接,那如果PHP也把连接保存下来那是不是就不用建立连接了?答案是肯定的:第一次访问接口时使用pfsockopen(pfsockopen与fsockopen唯一的区别就是它建立的是长连接)函数建立与服务器的连接,在访问完成后不关掉(fclose)连接,以后的访问就直接使用这个连接。具体到代码里就是:先判断有没有连接,如果有,继续用,如果没有,建立pfsockopen连接。

另外,如果接口返回内容比较短(比如:小于50字符)的话,还有优化的余地,那就是在HTTP请求头的Accept-Encoding中去掉gzip。它的作用是告诉服务器,我(浏览器)可以接受压缩过的内容,如果服务器也支持gzip就压缩后再返回,浏览器得到内容后解压缩再显示。但是如果内容太短的话,压缩后体积反而会增加,再加上压缩、解压缩的时间,就更加得不偿失了。

经过以上几步,访问接口速度应该与浏览器一样,理论上还会稍微快一点点。

关于KeepAlive,可以参考这个专题:HTTP Keep-Alive是什么?如何工作?

时间: 2024-08-08 01:28:45

PHP fsockopen受服务器KeepAlive影响的解决的相关文章

时间同步提示RPC服务器不可用的解决方法

时间同步方面: 1.RPC服务没有启动 " 开始"--〉"设置"--〉"控制面板"找到"管理工具"--〉"服务",找到"Remote Procedure Call (RPC)",双击打开,在"启动类型"里选择"自动"后,点击确定:找到"Remote Procedure Call (RPC) Locator",双击打开,在&quo

编写不受魔术引号影响的php应用

原创作品author流水孟春,转载请注明出处lib.cublog.cn 阅读前提:你必须看过php手册上的"第IV部分安全"的"第10章魔术引号".如果没看过,也没问题,现在马上花10分钟先看一下php手册上的这东西. 魔术引号(Magic Quote)是一个自动将进入 PHP 脚本的数据进行转义的过程 你可能想让你的程序兼容多个数据库,但你使用的不同的数据库可能使用不同的转义符,而我们的程序又有可能运行在不同的php.ini配置的主机上,关于magic_quote

WINDOWS 2008R2域服务器密码忘记或被别人修改,且服务器做RAID 5解决

WINDOWS 2008R2域服务器密码忘记或被别人修改,且服务器做RAID 5解决 提前准备:A.下载服务器raid卡的驱动放在U盘上.B.找一张相同版本的操作光盘 1.使用与本地机器相同版本的操作系统安装光盘,从光盘启动.(记住不要插入任何U盘设备)如果插入U盘会提示,现有版本和之前版本不一致的情况 2.在以下画面选择下一步, 3.选择左下角的修复计算机,如下图: 4. win2008 R2服务器上做RAID5时是无法找到本地的操作系统,必须加装RAID卡驱动后才有硬盘显示,此时点击加载驱动

window7远程桌面到服务器不能复制粘贴解决办法

用远程桌面登陆服务器不能在本机和远程服务器之间粘贴文本了,即不能从本机复制文本粘贴到服务器,也不能从服务器复制文本粘贴到本机. 以下是解决方法之一,试了几次都很管用户: 在服务器上打开任务管理器,查看进程,若存在 rdpclip.exe 进程 ,关闭此进程后,  开始->运行->rdpclip.exe 重新运行此程序,恢复正常. 一般是此进程出了问题导致. window7远程桌面到服务器不能复制粘贴解决办法

TIOBE 2014年11月编程语言排行榜:R受大数据影响跃至12位

TIOBE发布了11月份编程语言排行榜,前三甲依然为C.Java.Objective-C.受大数据影响,本月R语言上升至12位,上个月排名第15位,看其走势下个月有望进前十. 得益于大数据炒作,还有些语言包括Julia (#126), LabView (#63),Mathematica (#80), MATLAB (#24),S (#84),SAS (#21),SPSS (#104) 以及 Stata (#110)份额均有所上升. 编程语言排行榜TOP 20榜单: 前10名编程语言长期走势图: 

Linux启动ftp服务器530 Permission denied解决方法

 Linux启动ftp服务器530 Permission denied解决方法重新在虚拟机下安装了linux.现在我想启动linux自带的ftp服务器:#service  vsftpd  start .如果想linux启动是自动启动ftp服务器:#chkconfig  vsftpd  on  . 运行putty,以root身份进入,出现了报错  530 Permission denied  ,感觉很奇怪,因以普通用户是可以进入的. 原因是我们 /etc/vsftpd/ftpusers  和  /

CRT远程连接服务器字符输出乱码解决一例

环境: 服务器:Centos 6.2 远端:win 7 CRT版本:7.1.1 现象回顾: 1.服务器端:中文字符显示正常,如下: 2.CRT连接,出现乱码,如下图所示: 3.对CRT设置调整,如下: Options -> Session Options -> Appearance -> Font -> 新宋体 ->  字符集:中文GB2312 -> Character encoding:UTF-8 4.修改后重新执行操作,如下图所示: ****************

受台风“威马逊”影响 广西沿海将迎来大暴雨

气象部门预计,今年第9号台风“威马逊”最大可能7月18日凌晨到中午在海南登陆,随后进入北部湾海面,中心附近最大风力有12级.从17日晚起,桂南和北部湾将开始受到台风影响,18-20日,我区将出现一次较强风雨天气过程. 广西气象台总工程师林开平分析认为,查阅历史资料,可以发现1982年第17号台风的路径跟“威马逊”预报的路径十分吻合,当年这个台风带来的降水十分可观,广西南部和网络pos机西部的降水都在暴雨以上,特别是沿海一带降雨均达到特大暴雨量级.具体预报如下: 17日晚到19日,玉林.贵港.北海

PHP CURL本地可以采集服务器上不能采集解决办法

PHP CURL本地可以采集服务器上不能采集解决办法,今天采集一个站,在本机上写好代码,发到网站服务器上确采集不到数据.这里分析,会不会是目标站对网站做了防采集. 网上搜了下解决办法,这里用PHP CURL伪造IP和来源测试看看.代码如下. //随机IP function Rand_IP(){ $ip2id= round(rand(600000, 2550000) / 10000); //第一种方法,直接生成 $ip3id= round(rand(600000, 2550000) / 10000