第十一章:第16节 PHP与MySQL——PHP实现会员管理(十)重写session存储机制(1)

更新于:2018-07-30 17:33:20

PHP默认的存储机制并不适用大项目,流量很大的时候会产生很多的session文件,用文件存储session数据效率上不高。如果一个项目一台服务器无法满足需求,用多台服务器做负载均衡时,就更无法用默认的session存储机制处理了。最好的解决办法是重写PHP的session存储机制。


怎么重写?先看一个函数:


1.png


在session_start()开启之前,运行这个函数session_set_save_handler()等于不再使用PHP默认的session存储机制,而是使用代码重新定义的六个方法或函数来完成session的打开、关闭、读、写、销毁、回收六种操作。


session_set_save_handler有两种传参方式,如果用面向过程的方式重写session存储机制就按第一种方式传参,如果用面向对象的方式重写则按第二种方式传参。


其实这两种方式没什么区别,无非就是传参数时有所不同。


下面我们用面向对象的方式重写session的存储机制:


还记得mysql的memory存储引擎吗?memory表的数据就是存在内存里的,我们用它存储session数据。


1)先定义一个memory存储引擎的session表


1.png


2)开始写session类:


1.png


构造函数里12行告诉PHP使用用户自定义的方法操作session,13行代码相当于注册了6个方法分别操作session。


以下这6个方法必须要写的。


1.png

1.png


注意:往session写入数据,是在脚本运行的最后才写入。如果有用session记录当前时间戳的需求,一定要考虑到差值的问题。


打比方:

写一个限制同一用户频繁访问同一页面的功能(限制两次访问间隔为20秒)。


用户A第一次访问页面B,脚本在开头就执行了往session中写入当前时间戳(脚本不到1秒就执行完了)。

过了10秒钟,用户A第二次访问了页面B(在新标签中打开)。

第二次脚本从session中就获取到第一次访问的时间戳。( 第-次时间戳 - 第二次时间戳 < 20 )。

所以程序就可以拒绝用户的第二次访问请求。


用户A第一次访问页面B,脚本在开头就执行了往session中写入当前时间戳(可脚本花费了60秒才执行完)。

过了10秒钟,用户A第二次访问了页面B(在新标签中打开)

由于第一次访问,脚本还没有执行完。第二次脚本从session中就获取不到第一次访问的时间戳。

所以程序就不会拒绝用户的第二次访问请求。



1.png


3)在入口文件index.php执行session类:


1.png


14行代码实例化session类,这样我们就重写好了session的存储机制。


运行下:


1.png


出错了,这个错误楠神研究了半天,通过打印:


1.png

1.png

通过69行打印对象属性知道原来查询的数据表不是“session”,而是“admin_list”。


注意:


写到这忽然觉着楠神前面的代码出现了点小毛病,下面解释下,可能有些乱,我也是写到这才明白(写程序就这样不可能一开始什么都明明白白)。


1.png

1.png

1.png


出错的地方:使用Db::table('表名')静态方法获取一个Db对象,不应该把它赋值给一个属性变量后再使用。打比方:


程序按顺序执行:


 1)$_sess = Db::table('session');$_sess->select();Db对象里table参数此刻是“session”。


 2)$_admin = Db::table('admin_list');$_admin->select();由于我们的Db类实现了单例模式,$_sess和$_admin的值是同一个Db对象,Db对象里table参数此刻变成“admin_list”。


 3)由于不再执行$_sess = Db::table('session');这一步了,$_sess->select();Db对象里table参数此刻还是“admin_list”。


这就是我们上面出错的主要原因。怎么解决呢?


最简单的解决方法:不要把Db::table('表名')赋值给一个属性变量,在类的其他方法里还是直接使用Db::table('表名')。(楠神已把代码全部修改过来了)


复杂点的方法思路:为每个表单独建一个模型类,类里面有记录表名的属性,继承Db类,用哪个表就实例化那个表的模型类。根据前面面向对象的知识,可试着去写一写,由于改动比较大,我就不去写了。


解决了上面的小毛病,我们的session重写就可以正常实现了。


1.png


本节学习代码》》》