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