|
本帖最后由 方达 于 2012-7-19 21:06 编辑
【该问题已有 2 种解决方案,附在贴子最后】
在一个实际的网站中经常会有这样的场景:
一个页面中有一些页面模块是固定不变、或者服务器能较快处理生成页面的(如一个页面的header、footer)
而另一些页面模块是需要相当长的时间才能生成(如2~3秒)
遇到这些情况,我们通常会将那些能较快生成的页面片段先返回给浏览器,
然后等那些需要较长时间生成的页面模块准备好后,再返回给浏览器。
(http1.1 的chunked encoding分块编码可以支持)
通过这种方式来达到较好的用户体验,不必让用户干等着空白的页面。
在我不用CI用纯粹php写的时候可以这样写(纯测试写法):
这样的效果是,1,2,3这三块会在不同的时间在浏览器中显示。
而且相比较于ajax技术,这种方式不需要浏览器向后端发送许多xmlhttp请求(确切的说浏览器只有一个request),
减轻了服务器的压力,这对访问量较大的网站来说是有意义的。
可是在我用CI来实现这种效果的过程中,却碰到了很诡异的问题。
比如我要加载2个页面:
PHP复制代码
[font=monospace]$this->load->view('skeleton');
$this->load->view('content');
[/font] 复制代码
我在许多地方都加了类似flush之类的函数,可是浏览器给我的感觉,
CI总是将所有页面都准备好了之后,在最后同一时间同时返回给浏览器的。
也就是说,浏览器的响应时间会阻塞在用时最长的那个页面块上。
之后我阅读了Output.php, 认为_dispaly()函数可能是返回浏览器前最后调用的函数。
(因为在代码中log_message()写道:Final output sent to browser)
于是我这样写
PHP复制代码
$this->load->view('skeleton');
$this->output->_dispaly ('');
sleep(2);
$this->load->view('content');
复制代码
为了说明问题,还在Output.php中的_dispaly()函数内做了log_message输出时间。
- INFO - 2012-07-03 20:53:04 --> echo part 1
- [align=left]DEBUG - 2012-07-03 20:53:04 --> File loaded: application/views/skeleton.php[/align]INFO - 2012-07-03 20:53:04 --> end echo part1
- INFO - 2012-07-03 20:53:04 --> call _display
- INFO - 2012-07-03 20:53:04 --> start output
- INFO - 2012-07-03 20:53:04 -->part1 time:[color=#ff0000][color=Black]1341319984[/color][/color][color=Black] [/color][color=#ff0000][color=Black]//第一部分,time为time()[/color][/color]
- DEBUG - 2012-07-03 20:53:04 --> Final output sent to browser
- DEBUG - 2012-07-03 20:53:04 --> Total execution time: 0.0734
- INFO - 2012-07-03 20:53:04 --> start sleep
- INFO - 2012-07-03 20:53:06 --> end sleep
- INFO - 2012-07-03 20:53:06 --> echo part 2
- DEBUG - 2012-07-03 20:53:06 --> File loaded: application/views/content.php
- INFO - 2012-07-03 20:53:06 --> end echo part 2
- INFO - 2012-07-03 20:53:06 --> call _display
- INFO - 2012-07-03 20:53:06 --> start output
- INFO - 2012-07-03 20:53:06 --> part2 time:[color=#ff0000][color=Black]1341319986[/color][/color][color=Black] [/color][u]//第二部分,time为time()[/u]
- DEBUG - 2012-07-03 20:53:06 --> Final output sent to browser
- DEBUG - 2012-07-03 20:53:06 --> Total execution time: 0.0734
复制代码
从log文件看,按正常思路浏览器应该先显示skeleton,然后显示content了,
可实际情况是,等了约2秒之后(因为part1和part2间sleep了2秒)。
两个模块的内容一起显示了,即显示速度阻塞在了用时最长的页面块。
我迷茫了,Codeigniter究竟在_display()之后对数据做了什么呢?难道又做了buffer?
翻了文档,没有找到如何实现这类情况的方案,而codeigniter对页面加载却是这样默认地实现的。
这算不算是CI的缺陷呢?
我该如何做呢?
//这个帖子是我一天前发的,通过各位的提示,及自己的尝试,今天一个很偶然的机会让我解决了这个问题
根据实际测试结果,我得出如下一个解决方案:(并非唯一)
1.对第一个flush的view,包含
HTML复制代码
<meta http-equiv="Content-Type" content="text/html"; charset=utf-8">
复制代码
2.后端controller一个可选的实现:
经过实际测试,是work的,我用CI版本是2.1.1
各位可以写这样两个php文件来测试:
test_one.php:
PHP复制代码
<meta http-equiv="Content-Type" content="text/html"; charset=utf-8">
<?php
echo 'part1';
ob_flush();
flush();
sleep(2);
echo 'part2';
ob_flush();
flush();
sleep(2);
echo 'part3';
?>
复制代码
test_two.php:
对如上两个文件,test_one在实测时,浏览器有flush的效果,而test_two在实测时,阻塞约4秒后才全部显示
另外补充一下,@太尉天上飞 的观点可以参考:
- 个别浏览器第一个缓冲区数据必须大于一定长度才会输出,一般开头我们会echo str_pad('', 1000);
- 例如:
- <?php
- ob_end_clean();
- echo str_pad('',1024);
- echo 'a';
- flush();
- for($i = 0; $i<10; $i++)
- {
- echo $i.'<br />';
- flush();
- sleep(1);
- }
复制代码
Fin.
|
|