最近业务系统后台中发现在同一个浏览器下,打开一个页面出现页面加载速度特别慢半天都显示不出来,在此同时再打开另外一个页面也无法打开(正常情况下新打开的这个页面会秒开)。于是经过一番研究,解决了问题。
在这里先提出几点问题:
1.第一个页面为什么会特别慢?
2.为什么第一个页面打开慢的时候,第二个页面也慢?
3.能不能做到无论第一个页面有多慢,不影响打开其他页面?
逐个问题剖析:
1.经研究第一个页面中存在特别严重的sql慢查询问题,需要优化整改。
2.问题2分析:
- 后台再登陆的状态下才可以操作。
- 后台通过session会话保存身份认证信息。
- 后台所有页面程序在进入入口的时候都进行了session_start()开启回话。
简单了解一下php的session机制:
- 再php的配置文件中,有关于session的配置,用于指定session的存储方式和存储路径。
session.save_handler = memcached
session.save_path = "x.x.x.x:11211"
- session会在客户端上保存一个session_id的cookie信息,每次浏览器请求都会携带cookie信息。通过这种方式实现了跟踪用户的会话。
- session_start()在首次开启回话的时候,执行了上述机制相关的操作,并对当前session进行锁定,同一用户无法在释放锁定前再次进行session_start()操作.
通过对session机制的了解,发现问问题2产生的原因:
由于第一个页面请求比较耗时,php对session进行了较长时间的锁定没有释放,第二个页面进行session_start的时候,只能等待第一个页面释放了session锁定才能session_start成功,否则将一直等待第一个页面执行完成并释放session锁定。
基于上面的分析结论,我们能很快知道问题3的结局办法。
这里直接上解决方案(伪代码)
#--------入口文件index.php--------
<?php
......
session_start(); //开启回话
....... //业务代码
//保存关闭session释放session锁定
session_write_close(); //在进入框架内核(或者耗时业务)前,保存并关闭session,避免因为耗时业务执行时间较长而长时间锁定session。
#进入执行框架
$app = new App();
$app->initConfig($config)
->bootStartup()
->run();
#请求执行结束,保存并关闭session,释放session锁定。
session_write_close();
?>
#-------- 操作session写操作的文件特需要调整 --------
#-------- 我们操作session的方法封装在这里,禁止在程序中直接操作$_SESSION --------
#-------- Response.php --------
<?php
class Response
{
....
#写/更新session的方法
public static function setSession($key, $value){
session_start();
$_SESSION[$key] = $value;
session_write_close();
}
#删除session的方法
public static function delSession($key){
session_start();
unset($_SESSION[$key]);
session_write_close();
}
}
总结上述代码有两个点需要注意:
- 在开始进入耗时程序前先进行session_write_close关闭于保存session操作,避免了session长时间锁定。
- 执行了session_write_close后,session可以读取但无法进行session写操作。所以在对session进行写操作前进行session_start,写操作后再次session_write_close。
关于session_write_close:
官方手册有说明
session_write_close
(PHP 4 >= 4.0.4, PHP 5, PHP 7, PHP 8)
session_write_close — Write session data and end session
session_write_close(): bool
End the current session and store session data.
另外在php7几以上版本中,通过session_start(['read_and_close'=>true])也可以实现相同效果;