php 提前输出,PHP 提前输出响应及注意问题

一、问题

浏览器和服务器之间是通过HTTP进行通信的,浏览器发送请求给服务器,服务器处理完请求后,发送响应结果给浏览器,浏览器展示给用户。如果服务器处理请求时间比较长,那么浏览器就需要等待服务器的处理结果。

但是,有时候,浏览器不需要等待服务器的处理结果,只要发送的请求已经被服务器接收到。所以,这种情况下,浏览器希望服务器接收到请求立即返回一个响应,比如字符串'success'。这样浏览器可以继续执行后续代码。

在PHP中,很容易做到。如果服务器使用的是nginx+fpm,可以使用下面的代码:

echo 'success';

fastcgi_finish_request();

// 执行耗时代码.....

如果服务器使用的是apache,可以使用下面的代码:

ob_end_flush();

ob_start();

echo 'success';

header("Content-Type: text/html;charset=utf-8");

header("Connection: close");

header('Content-Length: '. ob_get_length());

ob_flush();

flush();

// 执行耗时代码.....

二、测试

一个静态页面:index.html,代码如下:

测试PHP提前输出响应

function getCurTime(d) {

var time = d.getFullYear() + "-" +(d.getMonth()+1) + "-" + d.getDate() + " " + d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds();

return time;

}

function getOther(url) {

$.ajax({

url: url,

success:function(data){

console.log(data);

console.log(getCurTime(new Date()));

}

});

}

console.log(getCurTime(new Date()));

$.ajax({

url: "http://www.test.com/test/1.php",

success:function(data){

console.log(data);

console.log(getCurTime(new Date()));

getOther("http://www.test.com/test/2.php");

}

});

浏览器首先通过ajax请求a.php,该php文件执行是比较耗时的,要等到a.php的响应后继续请求b.php文件。如果a.php不提前输出响应,ajax必须等待响应后才能去请求b.php。

上面代码,会在每次请求发送开始和返回时打印出时间。

两个PHP文件:1.php 、 2.php

a.php文件内容:

echo 'success';

sleep(5);

?>

上面代码使用sleep函数模拟耗时任务,耗时5秒。

b.php文件内容:

echo 'this is b.php';

?>

浏览器执行index.html,查看console日志如下:

1460000009886692

console日志

发现第二次请求时间比第一次请求时间迟5秒,正好是a.php文件的执行时间,说明浏览器需要等待a.php文件执行完才能去请求b.php文件。

下面修改a.php文件内容,提前输出响应,代码如下:

if(!function_exists('fastcgi_finish_request')) {

ob_end_flush();

ob_start();

}

echo 'success';

if(!function_exists('fastcgi_finish_request')) {

header("Content-Type: text/html;charset=utf-8");

header("Connection: close");

header('Content-Length: '. ob_get_length());

ob_flush();

flush();

} else {

fastcgi_finish_request();

}

sleep(5);

?>

浏览器执行index.html,查看console日志如下:

1460000009886693?w=559&h=347

console日志

两次请求的时间都一样,表示a.php已经提前输出响应了。

三、session问题

开发web应用程序不可避免的要用到session,修改一下a.php文件内容如下:

session_start();

$_SESSION['uname'] = 'jianshu';

if(!function_exists('fastcgi_finish_request')) {

ob_end_flush();

ob_start();

}

echo 'success';

if(!function_exists('fastcgi_finish_request')) {

header("Content-Type: text/html;charset=utf-8");

header("Connection: close");

header('Content-Length: '. ob_get_length());

ob_flush();

flush();

} else {

fastcgi_finish_request();

}

sleep(5);

?>

修改b.php文件内容如下:

session_start();

echo $_SESSION['uname'];

echo 'this is b.php';

?>

浏览器执行index.html,查看console日志如下:

1460000009886694

console日志

发现第一次和第二次请求时间一样,说明第一次请求已经提前输出响应了,但是第二次请求的响应时间却比请求发送时间迟5秒。第二次请求的是b.php文件,b.php文件内容只有几行代码,为什么会执行5秒才返回。

原因是,session是有锁的。为防止并发的写session数据,PHP自带的的文件保存session数据是加了一个互斥锁。程序执行session_start(),此时当前程序就开始持有锁。程序结束,此时程序自动释放Session的锁。

浏览器第一次请求a.php文件,对session文件进行加锁,虽然提前输出响应,但程序还在执行,session文件的锁还未释放。所以,第二次请求b.php文件时,由于b.php文件的开始就要打开session文件,但session文件的锁还未释放,需要等待a.php执行完,也即要等待5秒。

一般session文件跟SESSION_ID有关,每个SESSION_ID会创建一个session文件,下面可以看到两次请求带过去的SESSION_ID是一样的。

1460000009886695?w=959&h=380

a.php文件请求SESSID

1460000009886696?w=969&h=396

b.php文件请求SESSID

如何解决session文件加锁的问题,可以使用session_write_close函数,该函数的作用就是数据写入session文件并结束会话。

修改a.php文件,在使用session代码后面加上session_write_close函数。

session_start();

$_SESSION['uname'] = 'jianshu';

session_write_close();

............

浏览器执行index.html,查看console日志如下:

1460000009886697?w=492&h=333

console日志

发现没有等待存在了。实际开发中,使用完session,可以加上session_write_close()函数,减少服务器开销。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值