PHP的fsockopen方式访问接口慢的原因与优化方案
在开发过程中常常遇到这样的需求,模拟浏览器访问某接口,并获取返回数据。我们比较常使用的方法是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就压缩后再返回,浏览器得到内容后解压缩再显示。但是如果内容太短的话,压缩后体积反而会增加,再加上压缩、解压缩的时间,就更加得不偿失了。
经过以上几步,访问接口速度应该与浏览器一样,理论上还会稍微快一点点。
http://blog.csdn.net/lzr77/article/details/9704405
http://blog.csdn.net/dazhi_100/article/details/46806519
PHP的fsockopen方式访问接口慢的原因与优化方案的更多相关文章
- 使用HttpURLConnection方式访问接口
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import ...
- 两种访问接口的方式(get和post)
跨机器.跨语言的远程访问形式一共有三种:scoket发送数据包.http发送请求.rmi远程连接: http发送请求方式:分为post和get两种方式 importjava.io.IOExceptio ...
- 没有活动事务 链接服务器的 OLE DB 访问接口 "SQLNCLI" 无法启动分布式事务
在windows2003下执行分布式事务的时候出现如下情况. 一. 问题现象在执行分布式事务时,在sql server 2005下收到如下错误: 链接服务器"xxxxxxx"的 O ...
- Dojo Data Store——统一数据访问接口
原文地址:http://www.infoq.com/cn/articles/wq-dojo-data-store 无论在传统的桌面应用还是在主流的互联网应用中,数据始终占据着软件应用中的核心地位.当下 ...
- SharePoint—用REST方式访问列表
REST的定义与作用 在SharePoint 2010中,基本上有如下几种数据访问方式: 服务器端对象模型 LINQ to SharePoint Web Service 客户端对象模型 ADO.NET ...
- java servlet手机app访问接口(一)数据加密传输验证
前面几篇关于servlet的随笔,算是拉通了 servlet的简单使用流程,接下去的文章将主要围绕手机APP访问接口这块出发续写,md5加密传输--->短信验证--->手机推送---> ...
- httpclient 认证方式访问http api/resutful api并获取json结果
最近,因公司线上环境rabbitmq经常发生堆积严重的现象,于是跟运维组讨论,帮助开发个集中监控所有rabbitmq服务器运行情况的应用,需要通过java访问rabbitmq暴露的http api并接 ...
- 链接服务器"(null)"的 OLE DB 访问接口 "Microsoft.Jet.OLEDB.4.0" 返回了消息 "未指定的错误"。[手稿]
消息 7302,级别 16,状态 1,第 1 行 无法创建链接服务器 "(null)" 的 OLE DB 访问接口 "Microsoft.JET.OLEDB.4.0&qu ...
- 21. 无法执行该操作,因为链接服务器”Server_202”的 OLE DB 访问接口 “SQLNCLI10″ 无法启动分布式事务”
无法执行该操作,因为链接服务器”Server_202”的 OLE DB 访问接口 “SQLNCLI10″ 无法启动分布式事务” 原因:调用存储过程的方式有问题,必须用JDBC方式调用存储过程才可以正常 ...
随机推荐
- 第2章c++简单程序设计
第2章c++简单程序设计 知识梳理 以下是我遗忘以及认为重要的知识整理: 1.标识符的构成规则: 以大写字母.小写字母或下划线 _ 开始 由大写字母.小写字母.下划线 _ 或数字(0~9)组成 大写字 ...
- Python 命令总结
本章内容 pip pip install -r requirement.py(里面写入需要安装的包的名字) pip install django==1.9 #需要安装那个版本 P ...
- xml文件的生成
关于android中自定义xml文件的生成,请看示例代码(主要来源于黑马教程): import java.io.File; import java.io.FileNotFoundException; ...
- Python中的Json模块dumps、loads、dump、load函数介绍
Json模块dumps.loads.dump.load函数介绍 1.json.dumps() json.dumps() 用于将dict类型的数据转成str,因为如果直接将dict类型的数据写入json ...
- maven学习(九)——maven中的坐标、依赖以及仓库
一.Maven坐标 1.1.什么是坐标? 在平面几何中坐标(x,y)可以标识平面中唯一的一点. 1.2.Maven坐标主要组成 groupId:组织标识(包名) artifactId:项目名称 ver ...
- POJ-1087 二分图匹配,最大流。
A Plug for UNIX 题意很迷,不过很水. 题意:一个房间有m个插座,每个插座有一个型号, ...
- 【bzoj2245】[SDOI2011]工作安排 费用流
题目描述 你的公司接到了一批订单.订单要求你的公司提供n类产品,产品被编号为1~n,其中第i类产品共需要Ci件.公司共有m名员工,员工被编号为1~m员工能够制造的产品种类有所区别.一件产品必须完整地由 ...
- JS 处理json数据
$.parseJSON( jsonstr ); //jQuery.parseJSON(jsonstr),可以将json字符串转换成json对象 JSON.parse(jsonstr); //可以将js ...
- ZOJ 3362 Beer Problem(SPFA费用流应用)
Beer Problem Time Limit: 2 Seconds Memory Limit: 32768 KB Everyone knows that World Finals of A ...
- Repeater嵌套(灵活的)
页面代码 <form id="form1" runat="server"> <asp:Repeater ID="rptCategor ...