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; }
}