解决WEB集群session同步的方案,Redis内存缓存

转自:http://www.sziwap.com/archives/75.html

最近公司WEB服务器换集群方式,集群所带来直接的问题就是session共享。
如果用PHP自带的session处理方式,又要达到一致性,我已知的解决方案是NFS方法,不过担心磁盘性能以及session的处理机制,决定放弃这种方法,最后决定用内存缓存服务器来实现。

公司目前主要缓存的使用已经全部转至Redis下面(主要因为我的极力推荐,呵呵)。所以几简单写了个类实现了对session的操作,后续还要进行优化和扩展,前期没办法呀,公司催得紧呀。。。。

下面把代码贴出来,大家也分享一下了。呵呵。有啥意见也可以提提,别拍砖。呵呵。

/**
 * @author shenjun
 * @Createdate 2010-10-14
 * @todo session机制,存在redis内存中,解决web集群中session共享问题
 */
class Session{

    static protected $connect = FALSE;
    protected $redis = NULL;
    protected $redis_host = '192.168.1.107';
    protected $redis_port = '6379';
    protected $sess_id = NULL ;
    protected $sess_life = 300 ;
    protected $sessions = array () ;
    ##是否自动保存session,默认为自动保存
    protected $auto_save = true ;
    ##判断是否有修改过session 中的值
    protected $changed = false;
    /**
     * @todo redis初始化方法,单例入口
     * @desc 自动判断系统是否带redis,则是否有编译redis的客户端环境
     */
    static public function singleton()
    {
        if ( self::$connect == FALSE )
        {
            self::$connect = new Session();
        }
        return self::$connect;
    }
    /**
     * @todo 构造函数
     * @desc 建立redis连接,取得已有sessionID,并取得所有session的值
     */
    protected function __construct()
    {
        if ( class_exists( 'redis' ) )
        {
            $redis = new Redis (  );
            $conn = $redis->connect( $this->redis_host  , $this->redis_port );
        } else {
            require_once dirname(__FILE__). DIRECTORY_SEPARATOR . 'PhpRedis.php';
            $redis = new PhpRedis ( $this->redis_host  , $this->redis_port );
            $conn = $redis->connect();
        }
        if ( $conn )
        {
            $this->redis = $redis ;
        } else {
            trigger_error( '无法正常连接缓存服务器!' , E_USER_ERROR );
        }
        $sess_name = $this->GetSessionName();
        #取得session ID
        if ( isset( $_COOKIE[ $sess_name ] ) && !empty( $_COOKIE[ $sess_name ] ) )
        {
            $this->sess_id = $_COOKIE[ $sess_name ] ;
            ##如果已经有session ID则取出其中的值
            $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 取得session name
     */
    public function GetSessionName ()
    {
        //sessionname 的名称用客户端的IP加上浏览器的信息
        $name = $_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'];
        return hash ( 'crc32' , $name );
    }

    /**
     * @todo 取得sessionID
     * @return string 返回sessionID
     */
    public function GetSessionID( )
    {
        if ( $this->sess_id == null )
        {
            $id = time().$_SERVER['HTTP_USER_AGENT'];
            $this->sess_id = hash( 'md5' , $id  );
        }
        return $this->sess_id;
    }
    /**
     * @todo 设置session 值
     * @desc 每次设置的值不会马上写入缓存,不过会记录在内存中,所以写入的值在当次也会有效
     * @param string $name 相当于$_SESSION[$name] 这中间的变量
     * @param any $value Session的值
     */
    public function Set ( $name , $value )
    {
        $this->sessions[ $name ] = $value;
        $this->changed = true ;
    }

    public function __call( $name , $param )
    {
        trigger_error( sprintf( '您调用了不存的session方法%s!' , $name ) , E_USER_ERROR );
    }

    public function info()
    {
        return $this->redis->info();
    }
    /**
     * @todo 取得session中所有的字段
     * @desc 私有方法,不供外部使用
     * @return array session中的值,如果空session则为空数组
     */
    protected function GetAll(  )
    {
        return count( $this->sessions ) > 0 ? $this->sessions : json_decode( $this->redis->get( $this->sess_id ) );
    }
    /**
     * @todo 取得session中的值
     * @desc 如果$name 为空,则返回全部session,如果不为空则返回对应key的值,如果key不存在,则返回空
     * @param string $name session中的key
     * @return array or string Session的值
     */
    public function Get( $name = '' )
    {
        if ( empty( $name ) )
            return $this->sessions;
        if ( isset( $this->sessions[ $name ] ) )
            return $this->sessions[ $name ];
        return null ;
    }
    /**
     * @todo 删除session中的值
     * @param string $name session中的key
     * @return 无返回值
     */
    public function Del( $name = '' )
    {
        if ( empty( $name ) )
            $this->sessions = array();
        if ( isset( $this->sessions[ $name ] ) )
            unset ( $this->sessions[ $name ] );
        return false ;
    }
    /**
     * @todo 保存session数据至缓存中
     * @return 无返回值
     */
    public function Save()
    {
        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;
        }
    }
    protected function Expire()
    {
        $this->redis->expire( $this->sess_id , $this->sess_life );
    }
    /**
     * @todo 取得session的生命周期
     * @desc 如果已过期则返回-1
     */
    public function GetExpire()
    {
        return $this->redis->ttl( $this->sess_id );
    }
    /**
     * @todo 方法结束时,将session值写入缓存
     */
    public function __destruct()
    {
        $this->auto_save && $this->Save();
    }
}

其实用方法也很简单了。

Session::singleton()->Set('name','shenjun');
echo Session::singleton()->Get() ;
echo Session::singleton()->Get( 'name' ) ;
echo Session::singleton()->GetExpire();


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章