Redis缓存Session同步的实践方案
最近公司Web服务器换集群方式,集群所带来直接的问题就是session共享。
如果用PHP自带的session处理方式,又要达到一致性,我已知的解决方案是NFS方法,不过担心磁盘性能以及session的处理机制,决定放弃这种方法,最后决定用内存缓存服务器来实现。
公司目前主要缓存的使用已经全部转至Redis下面(主要因为我的极力推荐,呵呵)。所以几简单写了个类实现了对session的操作,后续还要进行优化和扩展,前期没办法呀,公司催得紧呀。。。。
下面把代码贴出来,大家也分享一下了。呵呵。有啥意见也可以提提,别拍砖。呵呵。
/**
*@authorshenjun
*@Createdate2010-10-14
*@todosession机制,存在redis内存中,解决web集群中session共享问题
*/
classSession{
staticprotected$connect=FALSE;
//Redis服务器属性
protected$redis=NULL;
protected$redis_host='192.168.1.107';
protected$redis_port='6379';
//Session属性
protected$sess_id=NULL;
protected$sess_life=300;
protected$sessions=array();
//是否自动保存session,默认为自动保存
protected$auto_save=true;
//判断是否有修改过session中的值
protected$changed=false;
/**
*@todoredis初始化方法,单例入口
*@desc自动判断系统是否带redis,则是否有编译redis的客户端环境
*/
staticpublicfunctionsingleton()
{
if(self::$connect==FALSE)
{
self::$connect=newSession();
}
returnself::$connect;
}
/**
*@todo构造函数
*@desc建立redis连接,取得已有sessionID,并取得所有session的值
*/
protectedfunction__construct()
{
//连接redis数据库
if(class_exists('redis'))
{
$redis=newRedis();
$conn=$redis->connect($this->redis_host,$this->redis_port);
}else{
require_oncedirname(__FILE__).'/PhpRedis.php';
$redis=newPhpRedis($this->redis_host,$this->redis_port);
$conn=$redis->connect();
}
if($conn)
{
$this->redis=$redis;
}else{
trigger_error('无法正常连接缓存服务器!',E_USER_ERROR);
}
$sess_name=$this->GetSessionName();
//取得sessionID
if(isset($_COOKIE[$sess_name])&&!empty($_COOKIE[$sess_name]))
{
$this->sess_id=$_COOKIE[$sess_name];
//如果已经有sessionID则取出其中的值
$this->sessions=(array)json_decode($this->redis->get($this->sess_id));
}else{
$this->sess_id=$this->GetSessionID();
//如果没有cookie则建立cookie
setcookie($sess_name,$this->sess_id);
}
return$this;
}
/**
*@todo取得sessionname
*/
publicfunctionGetSessionName()
{
//sessionname的名称用客户端的IP加上浏览器的信息
$name=$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'];
returnhash('crc32',$name);
}
/**
*@todo取得sessionID
*@returnstring返回sessionID
*/
publicfunctionGetSessionID()
{
if($this->sess_id==null)
{
$id=time().$_SERVER['HTTP_USER_AGENT'];
$this->sess_id=hash('md5',$id);
}
return$this->sess_id;
}
/**
*@todo设置session值
*@desc每次设置的值不会马上写入缓存,不过会记录在内存中,所以写入的值在当次也会有效
*@paramstring$name相当于$_SESSION[$name]这中间的变量
*@paramany$valueSession的值
*/
publicfunctionSet($name,$value)
{
$this->sessions[$name]=$value;
$this->changed=true;
}
publicfunction__call($name,$param)
{
trigger_error(sprintf('您调用了不存的session方法%s!',$name),E_USER_ERROR);
}
publicfunctioninfo()
{
return$this->redis->info();
}
/**
*@todo取得session中所有的字段
*@desc私有方法,不供外部使用
*@returnarraysession中的值,如果空session则为空数组
*/
protectedfunctionGetAll()
{
returncount($this->sessions)>0?$this->sessions:json_decode($this->redis->get($this->sess_id));
}
/**
*@todo取得session中的值
*@desc如果$name为空,则返回全部session,如果不为空则返回对应key的值,如果key不存在,则返回空
*@paramstring$namesession中的key
*@returnarrayorstringSession的值
*/
publicfunctionGet($name='')
{
if(empty($name))
return$this->sessions;
if(isset($this->sessions[$name]))
return$this->sessions[$name];
returnnull;
}
/**
*@todo删除session中的值
*@paramstring$namesession中的key
*@return无返回值
*/
publicfunctionDel($name='')
{
if(empty($name))
$this->sessions=array();
if(isset($this->sessions[$name]))
unset($this->sessions[$name]);
returnfalse;
}
/**
*@todo保存session数据至缓存中
*@return无返回值
*/
publicfunctionSave()
{
if($this->changed===true)
{
$this->redis->set($this->sess_id,json_encode($this->sessions));
//更新过期时间
$this->redis->expire($this->sess_id,$this->sess_life);
//当保存过以后,就设置修改标记为假
$this->changed=false;
}
}
protectedfunctionExpire()
{
$this->redis->expire($this->sess_id,$this->sess_life);
}
/**
*@todo取得session的生命周期
*@desc如果已过期则返回-1
*/
publicfunctionGetExpire()
{
return$this->redis->ttl($this->sess_id);
}
/**
*@todo方法结束时,将session值写入缓存
*/
publicfunction__destruct()
{
$this->auto_save&&$this->Save();
}
}
其实用方法也很简单了。
Session::singleton()->Set('name','shenjun');
echoSession::singleton()->Get();
echoSession::singleton()->Get('name');
echoSession::singleton()->GetExpire();
参考资料:
- http://blog.csdn.net/enough_br/article/details/8572183