对会话有一定了解的都知道,http协议本来是无状态的,为了保证用户登陆态,带出来了session会话的机制。
PHP中的会话
Session会话通常是保存在服务端的一个ticket票据,已证明用户是该会话的归属者,以PHP为例,通常我们可以在php.ini中设置会话的存储位置,亦或是自行定义一个会话的处理类作为会话的管理器,以下简单的介绍了PHP中的会话管理器以及相关函数。
Session管理器
- session_set_save_hanler: 支持files、DB、Memcache、Redis等;
- 会话管理器通常需要实现:
- open(string $savePath, string $sessionName) : 会话打开的时候会被调用
- write(string $sessionId, string $data) : 在会话保存数据时会调用 write 回调函数。
- close: 在 write 回调函数调用之后调用,session_wrire_close()执行后也会被调用;
- read(string $sessionId) : 如果会话中有数据,read 回调函数必须返回将会话数据编码(序列化)后的字符串。
- destroy($sessionId) : 干掉会话,当session_destory()或者session_regenerate_id()执行时候生效。
- gc($lifetime): 为了清理会话中的旧数据,PHP 会不时的调用垃圾收集回调函数。
- create_sid() : 创建会话id。
相关的会话处理函数
结束当前会话以及会话存储: session_write_close/session_commit
默认情况下,无需调用该函数,因为在PHP执行完后会调用session_register_shutdown(),默认关闭会话函数就是session_write_close()
作为会话数据,在任何时间段内,仅允许一个脚本程序进行会话数据写入操作(考虑到数据一致性),此时其他脚本程序无法进行该会话数据操作,因为会话数据被lock住了(互斥访问),此时就出现了session锁问题
了,即一个脚本执行完才,另一个脚本才被执行,即开头遇到的问题;
Session释放
- 会话ID支持COOKIE或者URL传递
- 清除会话
$_SESSION = []
,重置当前整个会话的环境变量数据,session_destory()
,清理整个服务端内部存储的话数据;- 让客户端也做对应的Cookie清理,通过
set_cookie()
发送HTTP头信息给到客户端:set_cookie(session_name(), '' , time()-3600)
session锁模拟
通过a.php的page在处理当前会话,通过sleep(5)模拟一个长时间操作,比如mysql读写(正常情况不应该有这么久)
通过访问b.php,对统一会话进行读取操作,发现被阻塞,直至a.php完成才可以读取成功!
|
|
小结
出现Session锁的本质原因,还是由于脚本的执行时间过长,导致session锁长时间被占用无法被释放。比如,脚本中存在较长耗时的网络调用、大数据查询等,都会导致session锁被占用,若在会话锁未释放之前,存还有一些程序(比如ajax请求)也依赖于该会话开启,就会导致该脚本程序的阻塞,浏览器也一直处于pending
状态。
针对session锁
这块,可以考虑通过使用更快的读写缓存(比如Redis进行统一存储),避免使用文件存储会话信息;同时可以在会话数据设置完后或者信息读取后,主动调用session_write_close
结束会话;
若以Redis作为会话存储管理,在高并发的网站上面,需要充分考虑到作为Redis的会话管理也可能成为瓶颈(频繁的Redis连接、以及读写操作)
另外,理论上是不应该存在同一用户在同一时刻进行高频的进行会话操作;