为什么要用session共享?
当我们的网站用户访问并发性越来越高时,我们就会用到反代理和服务器集群
但是就会出现一个问题
当用户访问时用户登录时存储的session是存放在服务器的文件中,
但其他服务器没有该用户的session登录信息,那么在理论上用户还得登录一次
当然我们不会允许这种情况发生。
对比啦几种解决方案,决定使用如下解决方案的原理demo:
<?php
#设置session自定义储存
session_set_save_handler(
'open',
'close',
'read',
'write',
'destroy',
'gc'
);
/**
* 连接redis
* @return Redis
*/
function redisInit(){
$redis_obj=new Redis();
$redis_obj->open('127.0.0.1','6379');
return $redis_obj;
}
/**
* open回调的工作方式类似于类中的构造函数,并在打开会话时执行。这是在会话自动启动或使用session_start()
* 手动启动时执行的第一个回调函数。返回值表示TRUE成功,FALSE表示失败。
*/
#开启session的执行方法
function open(){
var_dump(__METHOD__);
echo "<hr>";
return true;
}
/**
* close回调的工作方式类似于类中的析构函数,并在调用会话写回调后执行。调用session_write_close()
* 时也会调用它 。返回值应该是TRUE成功,否则FALSE就是失败。
* @return bool
*/
#关闭session的执行方法
function close(){
var_dump(__METHOD__);
echo "<hr>";
return true;
}
/**
* #该read如果没有数据读取回调必须始终返回会话编码(连载)字符串,或空字符串。
*#当会话启动或调用session_start()时,PHP将在内部调用此回调。在调用此回调之前,PHP将调用该open回调。
*#此回调返回的值必须与最初传递给write回调存储的完全相同的序列化格式。返回的值将由PHP自动反序列化,
*#并用于填充$ _SESSION超全局变量。
*#虽然数据看起来与serialize()相似,但是请注意,session.serialize_handler ini设置中指定的格式是不同的。
* @param $sid
* @return bool|string
*/
#读取session的执行方法 必须返回序列化的内容
function read($sid){
var_dump(__METHOD__);
// return serialize([]);
echo "<hr>";
$redis_key='redis_session:'.$sid;
$redis=redisInit();
$redis_data=$redis->get($redis_key);
// var_dump($redis_data);
if(empty($redis_data)){
return serialize([]);
}else{
#每次访问是更新过期时间
$redis->expire($redis_key,1400);
return $redis_data;
}
}
/**
* #write(string $sessionId, string $data)
* #write当需要保存和关闭会话时, 将调用回调。此回调收到当前会话ID的序列化版本$ _SESSION超全局变量。
*#在session.serialize_handler ini设置中指定了PHP内部使用的序列化方法。
*#传递给该回调的序列化会话数据应根据传递的会话ID存储。检索此数据时,read回调必须返回最初传递给write回调的确切值。
*#当PHP关闭时调用此回调,或者在调用session_write_close()时显式 调用。请注意,执行此函数后,PHP将在内部执行close回调。
* @param $sid
* @param $value
* @return bool
*/
#写入redis数据的执行方法
function write($sid,$value){
var_dump(__METHOD__);
echo "<hr>";
// var_dump(func_get_args());
$redis=redisInit();
if($redis->set('redis_session:'.$sid,$value)){
$redis->expire('redis_session:'.$sid,1440);
return true;
}else{
return false;
}
}
/**
* #destroy($sessionId)
*#当使用session_destroy()或 session_regenerate_id()
*(销毁参数设置为)销毁会话时,将执行此回调TRUE。返回值应该是TRUE成功,否则FALSE就是失败。
* @param $sid
* @return bool
*/
#销毁sessio时执行的方法
function destroy($sid){
var_dump(__METHOD__);
echo "<hr>";
$redis_key='redis_session:'.$sid;
$redis=redisInit();
if($redis->del($redis_key)){
return true;
}else{
return false;
}
}
/**
*#gc($lifetime)
*#PHP定期在内部调用垃圾回收器回调,以清除旧的会话数据。频率由session.gc_probability和session.gc_divisor控制 。
*# 可以在session.gc_maxlifetime中设置传递给此回调的有效期值。返回值应该是TRUE成功,否则FALSE就是失败。
* @return bool
*/
function gc(){
var_dump(__METHOD__);
return true;
}
#开启session
session_start();
//$_SESSION['user_name']='scl';
//session_destroy();
//echo "<hr>";
//var_dump($_SESSION);
demo 实现原理:
当我们存取session时调用会调用 open--> read-->write-->close。
当我们销毁session值时 open-->read-->distroy-->close。
gc方法 会有1000/1 的概率 清理session过期文件,
执行成功返回true 否则换回false。
该demo使用第一种原型:
function session_set_save_handler ($open, $close, $read, $write, $destroy, $gc) {}
另一种原型 SessionHandlerInterface 链接如下: