解決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();


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