一相關介紹
1.memcache + memcache的多伺服器資料共用的介紹,請參見http://www.guigui8.com/index.php/archives/206.html
2.session機制:
session機制是一種伺服器端的機制,伺服器使用一種類似於散列表的結構(也可能就是使用散列表)來儲存資訊。
當程式需要為某個用戶端的請求建立一個session的時候,伺服器首先檢查這個用戶端的請求裡是否已包含了一個session標識- 稱為sessionid,如果已包含一個sessionid則說明以前已經為此用戶端建立過session,伺服器就按照sessionid把這個session檢索出來使用(如果檢索不到,可能會建立一個),如果用戶端請求不包含sessionid,則為此用戶端建立一個session並且產生一個與此session相關聯的sessionid,sessionid的值應該是一個既不會重複,又不容易被找到規律以仿造的字串,這個sessionid將被在本次響應中返回給用戶端儲存。
儲存這個sessionid的方式可以採用cookie,這樣在互動過程中瀏覽器可以自動的按照規則把這個標識發揮給伺服器。
一般這個cookie的名字都是類似於SEEESIONID,而。比如weblogic對於web應用程式產生的cookie,PHPSESSID=ByOK3vjFD75aPnrF3K2HmdnV6QZcEbzWoWiBYEnLerj,它的名字就是PHPSESSID。
二動機
在實際web生產環境中,一個應用系統,往往將不同的業務應用分布到不同伺服器上進行處理。
當跟蹤當前線上使用者資訊時,如果是同一個主網域名稱時,可以用全域cookie處理相關資料的共用問題;如果是在不同主域下,則可以通過觀察者模式的中心話概念解決相應問題,通過這種概念延伸出的解決方案有很多,而今天我所要講的,是前一種,通過memcache的多伺服器資料共用技術來類比session,以進行對當前線上使用者資料的多伺服器共用。
關於多伺服器統一session資訊,要求如下:
1.能夠在memcached規定的幾台伺服器上,儲存session資訊(通過前面介紹的memcache的多伺服器資料共用);
2.能夠象zend定義的session_start()前,通過session_id($sessid)那樣,自訂session_id的值。
3.能方便的在系統運行時,切換memcached儲存的session資訊和 用檔案儲存體的session資訊的操作。
三代碼
實現方式很簡單,通過memcache來類比session機制,只是利用memcache將儲存媒介換成共用伺服器的記憶體,以達到多台分布式部署的伺服器共用session資訊的目的。而調用的介面,與zend提供的session操作函數相區別,所以可以方便的在memcache和檔案的session資訊操作建切換。
以下代碼,已經過多次實際測試,能達到以上功能需求。先貼下面了:
複製代碼 代碼如下:/**
*=---------------------------------------------------------------------------=
* MemcacheSession.class.php
*=---------------------------------------------------------------------------=
*
* 實現基於Memcache儲存的 Session 功能
* (類比session機制,只是利用memcache將儲存媒介換成共用伺服器的記憶體)
*
* 缺點:暫時沒有引入不同主域的session共用機制的實現策略。即只支援同主域下的實現。
*
* Copyright(c) 2008 by guigui. All rights reserved.
* @author guigui <evan_gui@163.com>
* @version $Id: MemcacheSession.class.php, v 1.0 2008/12/22 $
* @package systen
* @link http://www.guigui8.com
*/
/**
* class MemcacheSession
*
* 1. 設定用戶端的Cookie來儲存SessionID
* 2. 把使用者的資料儲存在伺服器端,通過Cookie中的Session Id來確定一個資料是否是使用者的
*/
class MemcacheSession
{
// {{{ 類成員屬性定義
public $memObject = null; //memcache操作物件控點
private $_sessId = '';
private $_sessKeyPrefix = 'sess_';
private $_sessExpireTime = 86400;
private $_cookieDomain = '.guigui8.com'; //全域cookie網域名稱
private $_cookieName = '_PROJECT_MEMCACHE_SESS';
private $_cookieExpireTime = '';
private $_memServers = array('192.168.0.3' => 11211, '192.168.0.4' => 11211);
private $_sessContainer = array(); //目前使用者的session資訊
private static $_instance = null; //本類單例對象
// }}}
/**
* 單例對象擷取的靜態方法。
* (可以順便提供memcache資訊儲存的伺服器參數)
*
* @param string $host - memcache資料存放區的伺服器ip
* @param integer $port - memcache資料存放區的伺服器連接埠號碼
* @param bool $isInit - 是否執行個體化對象的時候啟動Session
*/
public static function getInstance($host='', $port=11211, $isInit = true) {
if (null === self::$_instance) {
self::$_instance = new self($host, $port, $isInit);
}
return self::$_instance;
}
/**
* 建構函式
*
* @param bool $isInit - 是否執行個體化對象的時候啟動Session
*/
private function __construct($host='', $port=11211, $isInit = false){
!empty($host) && $this->_memServers = array(trim($host) => $port);
$isInit && $this->start();
}
/**
*=-----------------------------------------------------------------------=
*=-----------------------------------------------------------------------=
* Public Methods
*=-----------------------------------------------------------------------=
*=-----------------------------------------------------------------------=
*/
/**
* 啟動Session操作
*
* @param int $expireTime - Session失效時間,預設是0,當瀏覽器關閉的時候失效, 該值單位是秒
*/
public function start($expireTime = 0){
$_sessId = $_COOKIE[$this->_cookieName];
if (!$_sessId){
$this->_sessId = $this->_getId();
$this->_cookieExpireTime = ($expireTime > 0) ? time() + $expireTime : 0;
setcookie($this->_cookieName, $this->_sessId, $this->_cookieExpireTime, "/", $this->_cookieDomain);
$this->_initMemcacheObj();
$this->_sessContainer = array();
$this->_saveSession();
} else {
$this->_sessId = $_sessId;
$this->_sessContainer = $this->_getSession($_sessId);
}
}
/**
* setSessId
*
* 自訂的session id,通常沒有必要經過cookie操作處理的(所以省略了cookie記錄session_id)
*
* @param string $sess_id
* @return boolean
*/
public function setSessId($sess_id){
$_sessId = trim($sess_id);
if (!$_sessId){
return false;
} else {
$this->_sessId = $_sessId;
$this->_sessContainer = $this->_getSession($_sessId);
}
}
/**
* 判斷某個Session變數是否註冊
*
* @param string $varName -
* @return bool 存在返回true, 不存在返回false
*/
public function isRegistered($varName){
if (!isset($this->_sessContainer[$varName])){
return false;
}
return true;
}
/**
* 註冊一個Session變數
*
* @param string $varName - 需要註冊成Session的變數名
* @param mixed $varValue - 註冊成Session變數的值
* @return bool - 該變數名已經存在返回false, 註冊成功返回true
*/
public function set($varName, $varValue){
$this->_sessContainer[$varName] = $varValue;
$this->_saveSession();
return true;
}
/**
* 擷取一個登入的Session變數值
*
* @param string $varName - Session變數的名稱
* @return mixed - 不存在的變數返回false, 存在變數返回變數值
*/
public function get($varName){
if (!isset($this->_sessContainer[$varName])){
return false;
}
return $this->_sessContainer[$varName];
}
/**
* 銷毀一個登入的Session變數
*
* @param string $varName - 需要銷毀的Session變數名
* @return bool 銷毀成功返回true
*/
public function delete($varName){
unset($this->_sessContainer[$varName]);
$this->_saveSession();
return true;
}
/**
* 銷毀所有已經註冊的Session變數
*
* @return 銷毀成功返回true
*/
public function destroy(){
$this->_sessContainer = array();
$this->_saveSession();
return true;
}
/**
* 擷取所有Session變數
*
* @return array - 返回所有登入的Session變數值
*/
public function getAll(){
return $this->_sessContainer;
}
/**
* 擷取當前的Session ID
*
* @return string 擷取的SessionID
*/
public function getSid(){
return $this->_sessId;
}
/**
* 擷取Memcache的伺服器資訊
*
* @return array Memcache配置數組資訊
*/
public function getMemServers(){
return $this->_memServers;
}
/**
* 設定Memcache的伺服器資訊
*
* @param string $host - Memcache伺服器的IP
* @param int $port - Memcache伺服器的連接埠
*/
public function setMemServers($arr){
$this->_memServers = $arr;
}
/**
* 添加Memcache伺服器
*
* @param string $host - Memcache伺服器的IP
* @param int $port - Memcache伺服器的連接埠
*/
public function addMemServer($host, $port){
$this->_memServers[trim($host)] = trim($port);
$this->memObject->addServer($host, $port);
}
/**
* 移除Memcache伺服器(注意,這個只是移除配置,並不能實際從memcached的串連池移除)
*
* @param string $host - Memcache伺服器的IP
* @param int $port - Memcache伺服器的連接埠
*/
public function removeMemServer($host){
unset($this->_memServers[trim($host)]);
}
/**
*=-----------------------------------------------------------------------=
*=-----------------------------------------------------------------------=
* Private Methods
*=-----------------------------------------------------------------------=
*=-----------------------------------------------------------------------=
*/
/**
* 產生一個Session ID
*
* @return string 返回一個32位的Session ID
*/
private function _getId(){
return md5(uniqid(microtime()));
}
/**
* 擷取一個儲存在Memcache的Session Key
*
* @param string $_sessId - 是否指定Session ID
* @return string 擷取到的Session Key
*/
private function _getSessKey($_sessId = ''){
$sessKey = ($_sessId == '') ? $this->_sessKeyPrefix.$this->_sessId : $this->_sessKeyPrefix.$_sessId;
return $sessKey;
}
/**
* 檢查儲存Session資料的路徑是否存在
*
* @return bool 成功返回true
*/
private function _initMemcacheObj(){
if (!class_exists('Memcache') || !function_exists('memcache_connect')){
$this->_showMessage('Failed: Memcache extension not install, please from http://pecl.php.net download and install');
}
if ($this->memObject && is_object($this->memObject)){
return true;
}
$this->memObject = new Memcache;
if (!empty($this->_memServers)) {
foreach ($this->_memServers as $_host => $_port) {
$this->memObject->addServer($_host, $_port);
}
}
return true;
}
/**
* 擷取Session檔案中的資料
*
* @param string $_sessId - 需要擷取Session資料的SessionId
* @return unknown
*/
private function _getSession($_sessId = ''){
$this->_initMemcacheObj();
$sessKey = $this->_getSessKey($_sessId);
$sessData = $this->memObject->get($sessKey);
if (!is_array($sessData) || empty($sessData)){
//this must be $_COOKIE['__SessHandler'] error!
return array();
}
return $sessData;
}
/**
* 把當前的Session資料儲存到Memcache
*
* @param string $_sessId - Session ID
* @return 成功返回true
*/
private function _saveSession($_sessId = ''){
$this->_initMemcacheObj();
$sessKey = $this->_getSessKey($_sessId);
if (empty($this->_sessContainer)){
$ret = @$this->memObject->set($sessKey, $this->_sessContainer, false, $this->_sessExpireTime);
}else{
$ret = @$this->memObject->replace($sessKey, $this->_sessContainer, false, $this->_sessExpireTime);
}
if (!$ret){
$this->_showMessage('Failed: Save sessiont data failed, please check memcache server');
}
return true;
}
/**
* 顯示提示資訊
*
* @param string $strMessage - 需要顯示的資訊內容
* @param bool $isFailed - 是否是失敗資訊, 預設是true
*/
private function _showMessage($strMessage, $isFailed = true){
return;
if ($isFailed){
echo ($strMessage);
}
echo $strMessage;
}
四應用
1.本地session儲存,與原始session操作方式一樣,沒有任何改變。如:複製代碼 代碼如下:session_start();
$_SESSION['file_session_info']= '本地檔案儲存的session資訊'; //本地檔案儲存的session
2.memcache共用伺服器的session儲存
複製代碼 代碼如下:$mem= MemcacheSession::getInstance('192.168.0.4', 11211);
$mem->addMemServer('192.168.0.4',11211);
$mem->addMemServer('192.168.0.5',11211);
//如果cookie功能不可用,則根據其他參數傳遞的唯一資訊,設定映射為session_id
if(1) {
$sn= '838ece1033bf7c7468e873e79ba2a3ec';
$mem->setSessId($sn);
}
$mem->set('name','guigui'); //多台memcache伺服器共用的session
$mem->set('addr','wuhan'); //多台memcache伺服器共用的session
//$mem->destroy();
3.分別擷取本地和memcache儲存的session資訊
複製代碼 代碼如下:$addr= $mem->get('addr');
$_MEM_SESSION= $mem->getAll();
echo"<hr>localhost file session:";
var_dump($_SESSION);
echo"<hr>memcache session:";
var_dump($_MEM_SESSION);
//$res= $mem->delete('name');