在开发过程中常常遇到这样的需求,模拟浏览器访问某接口,并获取返回数据。我们比较常使用的方法是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方式访问接口慢的原因与优化方案的更多相关文章

  1. 使用HttpURLConnection方式访问接口

    import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import ...

  2. 两种访问接口的方式(get和post)

    跨机器.跨语言的远程访问形式一共有三种:scoket发送数据包.http发送请求.rmi远程连接: http发送请求方式:分为post和get两种方式 importjava.io.IOExceptio ...

  3. 没有活动事务 链接服务器的 OLE DB 访问接口 "SQLNCLI" 无法启动分布式事务

    在windows2003下执行分布式事务的时候出现如下情况. 一. 问题现象在执行分布式事务时,在sql server 2005下收到如下错误: 链接服务器"xxxxxxx"的 O ...

  4. Dojo Data Store——统一数据访问接口

    原文地址:http://www.infoq.com/cn/articles/wq-dojo-data-store 无论在传统的桌面应用还是在主流的互联网应用中,数据始终占据着软件应用中的核心地位.当下 ...

  5. SharePoint—用REST方式访问列表

    REST的定义与作用 在SharePoint 2010中,基本上有如下几种数据访问方式: 服务器端对象模型 LINQ to SharePoint Web Service 客户端对象模型 ADO.NET ...

  6. java servlet手机app访问接口(一)数据加密传输验证

    前面几篇关于servlet的随笔,算是拉通了 servlet的简单使用流程,接下去的文章将主要围绕手机APP访问接口这块出发续写,md5加密传输--->短信验证--->手机推送---> ...

  7. httpclient 认证方式访问http api/resutful api并获取json结果

    最近,因公司线上环境rabbitmq经常发生堆积严重的现象,于是跟运维组讨论,帮助开发个集中监控所有rabbitmq服务器运行情况的应用,需要通过java访问rabbitmq暴露的http api并接 ...

  8. 链接服务器"(null)"的 OLE DB 访问接口 "Microsoft.Jet.OLEDB.4.0" 返回了消息 "未指定的错误"。[手稿]

    消息 7302,级别 16,状态 1,第 1 行 无法创建链接服务器 "(null)" 的 OLE DB 访问接口 "Microsoft.JET.OLEDB.4.0&qu ...

  9. 21. 无法执行该操作,因为链接服务器”Server_202”的 OLE DB 访问接口 “SQLNCLI10″ 无法启动分布式事务”

    无法执行该操作,因为链接服务器”Server_202”的 OLE DB 访问接口 “SQLNCLI10″ 无法启动分布式事务” 原因:调用存储过程的方式有问题,必须用JDBC方式调用存储过程才可以正常 ...

随机推荐

  1. Codeforces 35E Parade 扫描线

    题意: 给出\(n\)个底边在\(x\)轴上的矩形,求外面的轮廓线顶点. 分析: 将每个矩形拆成两个事件:\(\\\{ l, y, + \\\}\)和\(\\\{ r, y, - \\\}\)分别表示 ...

  2. Java消息中间件--初级篇

    一. 为什么使用消息中间件? 假设用户登录系统   传统方式 用户登录  调用短息服务   积分服务  日志服务等各种服务  如果短息服务出现问题就无法发送短信而且用户登录成功必须所有调用全部完成返回 ...

  3. day19 Dgango进阶 路由系统及 ORM 详解

    完成一个完整的网页服务,需要有以下: 再次回顾一下Django 的完成开发流程: 一些值的获取: 对于性别,为互斥属性: 爱好则为多选: 需要使用新的方法 getlist 来获取多个爱好: 单选下拉框 ...

  4. 项目管理者必知:适用于仪表盘项目的7个优秀JavaScript库

    仪表盘是用于目标或业务流程的视觉指示工具,也用于切割杂乱无章的数据,从而分割出要点的重要工具.它可以帮助评估信息并及时做出正确的决定,一款实时可视化的仪表盘通常由图标.测绘图.图形符号以及数据表格等组 ...

  5. IOS开发---菜鸟学习之路--(二十)-二维码扫描功能的实现

    本章将讲解如何实现二维码扫描的功能 首先在github上下载ZBar SDK地址https://github.com/bmorton/ZBarSDK 然后将如下的相关类库添加进去 AVFoundati ...

  6. Google Chrome开发者工具-移动仿真:触摸事件仿真

    如果你在开发PAD/手机所用WEB版应用,需要在桌面审查页面元素.调试脚本,模拟移动设备尺寸.事件.位置等信息, 那么可以使用Chrome开发者工具(DevTools)提供的强大的移动仿真功能,支持主 ...

  7. 剑指offer42:翻转单词顺序 VS 左旋转字符串(更高效、简便的解法)

    题目:输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变.为简单起见,标点符号和普通字母一样处理.例如输入字符串"I am a student." ,则输出" ...

  8. 流浪者(rover)

    流浪者(rover) 题目描述 有一位流浪者正在一个n∗mn∗m的网格图上流浪.初始时流浪者拥有SS点体力值. 流浪者会从(1,1)(1,1)走向(n,m)(n,m),并且他只会向下走((x,y)→( ...

  9. CDOJ 1256 二维前缀和处理

    昊昊喜欢运动 他NN 天内会参加MM 种运动(每种运动用一个[1,m][1,m] 的整数表示) 舍友有QQ 个问题 问昊昊第ll 天到第rr 天参加了多少种不同的运动 Input 输入两个数NN , ...

  10. Codeforces Round #362 (Div. 2) B 模拟

    B. Barnicle time limit per test 1 second memory limit per test 256 megabytes input standard input ou ...