为什么用fopen打开远程URL会很慢?
2015-10-24 11:45
183 查看
1. HTTP HEADER之诡
$content = file_get_contents('http://www.baidu.com/logo.png');
这是php中一种读取网络图片的方式,就跟读本地文件一样,用起来十分方便。但是这种方式存在一个问题,部分网络地址读起来速度非常快,但总有少数网址,读起来要卡顿10秒、20秒甚至1分钟。网上对于这个问题有很多的讨论,但答案基本都有问题。
对于慢,最常见的解释是DNS查询慢。的确,通过curl和wget的对比可以发现,获取同一个文件,curl需要0.5s,而wget需要1.5s,从wget的屏幕输出上看,其中1秒钟被他花在了DNS查询上。虽然DNS查询会占据1s的时间,但也不应该卡顿10秒呀。其中必有蹊跷。
国外有个同学终于给出了一个靠谱的答案:
By analyzing it with Wireshark, the issue (in my case and probably yours too) was that the remote web server DIDN'T CLOSE THE TCP CONNECTION UNTIL 15 SECONDS (i.e. "keep-alive"). Indeed, file_get_contents doesn't send a "connection" HTTP header, so the remote web server considers by default that's it's a keep-alive connection and doesn't close the TCP stream until 15 seconds (It might not be a standard value - depends on the server conf). A normal browser would consider the page is fully loaded if the HTTP payload length reaches the length specified in the response Content-Length HTTP header. File_get_contents doesn't do this and that's a shame.
问题出在file_get_contents的行为上:(1)它发起http连接的时候,没有带http头,(2)它接收完所有数据后,没有主动断开和服务器的http连接。
找到问题原因之后,解决方案就相对简单了:让file_get_contents也带上一个http的头,并且在头里面标注:Connection: close; 要求服务器发完数据后立即断开连接。
file_get_contents原型如下:
string file_get_contents ( string $filename [, bool $use_include_path = false [, resource $context [, int $offset = -1 [, int $maxlen ]]]] )
其中$context参数就是用来传递http头的。代码如下:
$opts = array( 'http'=>array( 'method'=>"GET", 'Connection'=>"close\r\n" ) ); $context = stream_context_create($opts); file_get_contents($filename, false, $context);
添加上$context后,性能立即得到了提升,每次读请求只需要1~2秒了。
注1:file_get_contents实际上是对fopen、fread、fclose的一个封装,所以它存在的问题,在fopen上同样存在。对于fopen来说,解决方案也是相同的。
2. DNS之谜
经过添加$option,性能已经得到了很大的提升。这时候DNS查询带来的开销就不可忽视了。如何消除掉这1秒的开销?目前我还没有基于fopen、file_get_contents的方案,只能用curl方案了。curl方案既不存在上面的HTTPHEADER问题,也不存在DNS问题,是个非常不错的方案。
如果你选择用curl方案,同时还希望用上stream接口来读数据,可以了解一下curl的CUROPT_FILE这个option。
curl_setopt($ch, CURLOPT_FILE, $fp);
2. 总结
本文提出了解决网络读取图片慢的两种方案,对于移动开发、爬虫开发,微信开发,支付宝开发等各领域的朋友都有借鉴意义。本文还让我重新思考了一个方法论:当网上搜不到答案时,外国友人用Wireshark监听数据包的方式其实是最直接合理高效的。为什么我没有这么做?想想,还是以前总结出来的工具论,我对这些工具的使用不熟练,不会往这个方向上想,就算想了,也不愿意行动。应该多动手,多学习!
相关文章推荐
- SUSE Linux – Zypper 命令示例
- ubuntu下配置opensips服务器
- Linux下使用cronolog切割服务器日志文件的方法
- OpenGL Bresenhan画线法
- 基于Mesos和Docker的分布式计算平台
- LinuxCNC+EtherCAT(4)之LinuxCNC overview
- Linux内核定时器
- Shell简介:什么是Shell,Shell命令的两种执行方式
- Linux OS下分区的擦除与重写
- openlayer3跨域问题解决方法
- 架构设计:系统间通信(9)——通信管理与RMI 下篇
- How To Create A Local Repository For SUSE Linux
- ecshop广告-》单张,多张
- VS中使用openssl/aes加密解密
- centos6.7x86_64php7安装笔记 new
- 查看linux下arp信息
- 查看linux下网卡mac地址
- linux下通过lseek()实现文件大小设置
- 查看linux路由信息
- OpenCV之创建Mat函数的方法