CodeIgniter Framework Source Notes (--session) Redis Drive Implementation __session

Source: Internet
Author: User
Tags auth delete key garbage collection md5 codeigniter
Session Mechanism

Session_Start (), The Open (), read () method is invoked. And there is a certain probability of triggering the GC () method.
Session_commit () or session_write_close (), triggers the Write (), close () method.
Session_destory () triggers the Desotry () method. Technical Points

1, drive to implement open, read, write, close, Destory, GC six methods.

Open: Connect redis Database Connect (). The Save_path in the configuration is used to store the host, port, password, database, timeout information for the connection Redis.
Read: Locks the session of this request and then reads (get) corresponding to the contents of the key according to SESSION_ID.
Write: Sets the cache that is valid for $this->_config[' expiration ' (set).
Close: Release the lock and turn off the Redis connection.
destory: Clears the session content of the current request, that is, remove the session_id corresponding key from the Redis.
GC: Because the Redis cache has a lifecycle and is automatically recycled after expiration, we do not need to manually set up the garbage collection mechanism.

2, drive to support session_regenerate_id ().

3, drive to implement session Lock: The lock is stored in the key name $lock_key = $this->_key_prefix. $session _id. ': Lock ' in the cache, given 300 seconds of lifecycle at store time. Each sessionid has a lock. Only one HTTP request exclusive is allowed at a time. CI lock mechanism is more reliable than database-driven practice, the database driver once found that the word Fu Yi is occupied, it directly returns false, and in the Redis Drive, will block and each interval a second to see if the other release lock. specific implementation can refer to the following code

$lock _key = $this->_key_prefix. $session _id. ': Lock ';
$attempt = 0;
do {
    //If the life cycle of the key value is $lock_key has not expired, try to acquire the lock 30 times, the middle interval is one second.
    //So if there is a lock contention here, the maximum current request will block for 30 seconds if
    (($ttl = $this->_redis->ttl ($lock _key)) > 0)
    {sleep
        (1) ;
        Continue;
    }

    The code runs here to show that the lock in another request expires or releases

    //writes the cache of key for $lock_key, 300-Second Life cycle
    if (! $this->_redis->setex ($lock _key, (), Time ())
    {
        //cache write failure for logging
        log_message (' Error ', ' session:error while trying to obtain lock for '. $ This->_key_prefix. $session _id);
        return FALSE;
    }
    Code to run here, create lock success
    $this->_lock_key = $lock _key;
    break;
while (+ + $attempt < 30);

next look at the entire source code

Class Ci_session_redis_driver extends Ci_session_driver implements Sessionhandlerinterface {//phpredis Operation instance pair like Pro

    Tected $_redis;

    Key Name prefix protected $_key_prefix = ' ci_session: ';

    Marks whether the current process obtains the lock protected $_lock_key; ------------------------------------------------------------------------//Get Redis connection information by loading the configuration file and deposit $this->_

        config[' Save_path '] public function __construct (& $params) {parent::__construct ($params); if (Empty ($this->_config[' Save_path ')) {log_message (' error ', ' Session:no redis save path Configu
        Red. '); } elseif (Preg_match (?: tcp://)? ( [^:?] +)(?:\:( \d+))? (\?.  +)? # ', $this->_config[' Save_path '], $matches)) {isset ($matches [3]) OR $matches [3] = ';//Just to Avoid undefined index notices below $this->_config[' save_path '] = Array (' Host ' => $mat CHES[1], ' Port ' => empty ($matches [2])? NULL: $matchES[2], ' Password ' => preg_match (' #auth = ([^\s&]+) # ', $matches [3], $match)? $match [1]: NULL, ' database ' => preg_match (' #database = (\d+) # ', $matches [3], $match)? (int) $match [1]: NULL, ' timeout ' => preg_match (' #timeout = (\d+\.\d+) # ', $matches [3], $match)?

            (float) $match [1]: NULL);
        Preg_match (' #prefix = ([^\s&]+) # ', $matches [3], $match) && $this->_key_prefix = $match [1]; else {log_message (' error ', ' session:invalid redis save path Format: '. $this->_config[' s
        Ave_path ']); } if ($this->_config[' match_ip '] = = TRUE) {$this->_key_prefix. = $_server[' Remote_add
        R ']. ': '; }//------------------------------------------------------------------------//open ()///According to $this->_ config[' Save_path ' information connects to login Redis and selects the Library public function open ($save _path, $name) {if (emptY ($this->_config[' Save_path ')) {return $this->_failure;
        } $redis = new Redis (); Connect () connection if (! $redis->connect ($this->_config[' Save_path ' [' Host '], $this->_config[' Save_path '] [' p Ort '], $this->_config[' Save_path '] [' timeout ']) {log_message (' error ', ' session:unable to connect
        To Redis with the configured settings. '); //auth () Login Verify ElseIf (isset ($this->_config[' Save_path '] [' password ']) &&! $redis->auth ($th is->_config[' Save_path '] [' password ']) {log_message (' error ', ' session:unable to authenticate to R
        Edis instance. '); //select () Select the library that stores the session ElseIf Isset ($this->_config[' save_path ' [' Database ']) &&! $redis-& Gt;select ($this->_config[' Save_path '] [' database ']) {log_message (' error ', ' session:unable to Sele CT Redis database with index '. $this->_config[' Save_path' [' database ']];
            else {$this->_redis = $redis;
        return $this->_success;
    return $this->_failure; }//------------------------------------------------------------------------//Public function read ($sessi
            ON_ID) {//Get lock if (isset ($this->_redis) && $this->_get_lock ($session _id)) {
            Needed by write () to detect session_regenerate_id () calls $this->_session_id = $session _id; Gets all session content for $session_id $session _data = (string) $this->_redis->get ($this->_key_prefix
            . $session _id);
            Generate summary $this->_fingerprint = MD5 ($session _data);
        return $session _data;
    return $this->_failure; //------------------------------------------------------------------------//write Public function write ($se
       ssion_id, $session _data) { if (! isset ($this->_redis)) {return $this->_failure;
        }//was the ID regenerated?  By passing in the $session_id and comparing the $this->_session_id (assigned in the Read function) to the image attribute, the judge is not calling session_regenerate_id () elseif ($session _id!== $this->_session_id) {//release the old SessionID occupied locks while obtaining the new SessionID corresponding lock if (! $this->_release _lock () OR!
            $this->_get_lock ($session _id)) {return $this->_failure;
            } $this->_fingerprint = MD5 (");
        $this->_session_id = $session _id; } if (Isset ($this->_lock_key)) {$this->_redis->settimeout ($this->_lock_key, 300)
            ; if ($this->_fingerprint!== ($fingerprint = MD5 ($session _data)) {//Invoke set setting valid for $this->_ config[' expiration ' cache if ($this->_redis->set ($this->_key_prefix. $session _id, $session _data, $th is->_config[' ExpiRation ']) {$this->_fingerprint = $fingerprint;
                return $this->_success;
            return $this->_failure; Return ($this->_redis->settimeout ($this->_key_prefix. $session _id, $this->_config[' expiration ']) )
                ?
        $this->_success: $this->_failure;
    return $this->_failure; }//------------------------------------------------------------------------//close: Release lock, turn off Redis connection public F
                Unction Close () {if (Isset ($this->_redis)) {try {//If the current connection Redis is a pass if ($this->_redis->ping () = = = ' +pong ') {//delete the lock occupied by the current request.
                    Why not Call $this->_release_lock ()??
                    Isset ($this->_lock_key) && $this->_redis->delete ($this->_lock_key);
             Turn off Redis connection       if ($this->_redis->close () = = = $this->_failure) {return $this
                    ->_failure;  The catch (Redisexception $e) {log_message (' error ')
            ' Session:got redisexception on Close (): '. $e->getmessage ());
            } $this->_redis = NULL;
        return $this->_success;
    return $this->_success; }//------------------------------------------------------------------------//destroy: Empty the session content of the current request, that is: from Redi s to delete session_id corresponding key public function destroy ($session _id) {//Ensure session_destory operation is allowed only if the current request obtains a lock F (isset ($this->_redis, $this->_lock_key)) {//server-side processing: delete $session_id corresponding key if (($result = $this->_redis->delete ($this->_key_prefix. $session _id))!== 1) {log_message (' debug ') , ' Session:redis::d elete (Expected to return 1, got '. Var_export ($result, TRUE). ' instead. ');
            //Client Processing: Deletes the SessionID client cookie $this->_cookie_destroy ();
        return $this->_success;
    return $this->_failure; //------------------------------------------------------------------------//Because Redis has expired recycle function, so we do not need to manually set up garbage collection mechanism
    for PHP to invoke.
        The Public Function gc ($MAXLIFETIME) {//Not necessary, Redis takes care of.
    return directly $this->_success; }//------------------------------------------------------------------------//Session Access lock protected for the current process function _get_lock ($session _id) {//if session_id corresponding key exists, reset the survival time to 300 seconds if ($this->_lock_key = = = $th Is->_key_prefix. $session _id. ': Lock ') {//Set survival time to 300 seconds return $this->_redis->settim
        Eout ($this->_lock_key, 300); }//attempts to obtain a lock on case aThe other request already has it//next attempt to acquire the lock 30 times, so as to prevent other requests from taking up the lock.
        This is more reliable than the database-driven approach, once the database driver found that the word Fu Yi is occupied, it returns false directly $lock _key = $this->_key_prefix. $session _id. ': Lock ';
        $attempt = 0;
            Do {//If the life cycle of the key value is $lock_key has not expired, try to acquire the lock 30 times, the middle interval is one second.
                So if there is a lock contention here, the maximum current request will block for 30 seconds if (($ttl = $this->_redis->ttl ($lock _key)) > 0) {
                Sleep (1);
            Continue //code runs here to indicate that the lock in another request expired or was released/written to the $lock_key cache, the 300-Second Life cycle if (! $this->_redis- >setex ($lock _key, Time ()) {//cache write failure for logging log_message (' Error ', ' Sess
                Ion:error while trying to obtain lock for '. $this->_key_prefix. $session _id);
            return FALSE;
            //code run here, create lock success $this->_lock_key = $lock _key;
        Break

        while (+ + $attempt < 30); If you tasteThe number of tests equals 30, indicating that the lock was not successfully obtained. if ($attempt = =) {log_message (' error '), ' session:unable to obtain lock for '. $this->_key_prefi
            X. $session _id. ' After attempts, aborting. ');
        return FALSE;  }//$ttl log a debug log elseif ($ttl = = = 1) for the 1 expiration date {log_message (' Debug ', ' Session:lock
        For '. $this->_key_prefix. $session _id. ' Had no TTL, overriding. ');
        } $this->_lock = TRUE;
    return TRUE; }//------------------------------------------------------------------------//release lock, delete key value is $this->_lock_key re Dis key value pair protected function _release_lock () {if (Isset ($this->_redis, $this->_lock_key) && $t His->_lock) {//delete the Redis key value of the $this->_lock_key key value pair if (! $this->_redis->delete ($t His->_lock_key)) {log_message (' error ', ' session:error while trying to free lock for '. $th Is->_lock_key);
            return FALSE;
            //Empty the lock key value of the current image $this->_lock_key = NULL;
        Toggles the lock status to False $this->_lock = false;
    return TRUE; }

}
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.