|
来源:http://imysql.cn,转载请注明作/译者和出处,并且不能用于商业用途,违者必究。
mysql共享
基于mysql的实现方式代码如下: getMessage()); }
if (!defined(’MySQLSession’)) { define(’MySQLSession’, TRUE);
class Usess { static $mSessSavePath; static $mSessName; static $mSessMaxTime; static $mTblSess = ’sessions’; static $mTblSessMap; static $mDb;
// {{{ 初始化构造函数 /** * 构造函数 * * @param string $login_user 登录用户 * @param int $login_type 用户类型 * @param string $login_sess 登录Session值 * @return Esession */ public function __construct() { self::$mSessMaxTime = SESS_LIFTTIME;
self::$mTblSessMap = array( ’sid’ => ’sid’, ’data’ => ’session’, ’last’ => ’flush_dt’, ); } // }}}
/** {{{ sessOpen($pSavePath, $name) * * @param String $pSavePath * @param String $pSessName * * @return Bool TRUE/FALSE */ public function sessOpen($pSavePath = ’’, $pSessName = ’’) { global $gDb;
self::$mDb = $gDb; self::$mSessSavePath = $pSavePath; self::$mSessName = $pSessName;
self::sessGc();
return TRUE; } // }}}
/** {{{ sessClose() * * @param NULL * * @return Bool TRUE/FALSE */ public function sessClose() { return TRUE; } // }}}
/** {{{ sessRead($wSid) * * @param String $wSid * * @return Bool TRUE/FALSE */ public function sessRead($wSid = ’’) { global $db;
$wSql = sprintf("SELECT * FROM `%s`.`%s` WHERE `%s` = ’%s’;", DB_NAME, self::$mTblSess, self::$mTblSessMap[’sid’], $wSid );
//这里一定要用 DB_FETCHMODE_ASSOC,否则取回的数组只能用数字做下标 if (!PEAR::isError($row = self::$mDb->getRow($wSql, null, DB_FETCHMODE_ASSOC))) { //session已经存在了 if (is_array($row) && 1 <= count($row)) { return $row[self::$mTblSessMap[’data’]]; } else { $wSql = sprintf("INSERT INTO `%s`.`%s` VALUES (’%s’, ’’, UNIX_TIMESTAMP(NOW()));", DB_NAME, self::$mTblSess, $wSid );
if (!PEAR::isError(self::$mDb->query($wSql))) { return TRUE; } } }
return FALSE; } // }}}
/** {{{ sessWrite($wSid, $wData) * * @param String $wSid * @param String $wData * * @return Bool TRUE/FALSE */ public function sessWrite($wSid = ’’, $wData = ’’) { $wData = mysql_escape_string($wData);
$wSql = sprintf("UPDATE `%s`.`%s` SET `%s` = ’%s’, `%s` = UNIX_TIMESTAMP(NOW()) WHERE `%s` = ’%s’;", DB_NAME, self::$mTblSess, self::$mTblSessMap[’data’], $wData, self::$mTblSessMap[’last’], self::$mTblSessMap[’sid’], $wSid );
if (!PEAR::isError(self::$mDb->query($wSql))) { return TRUE; }
return FALSE; } // }}}
/** {{{ sessDestroy($wSid) * * @param String $wSid * * @return Bool TRUE/FALSE */ public function sessDestroy($wSid = ’’) { $wSql = sprintf("DELETE FROM `%s`.`%s` WHERE `%s` = ’%s’;", DB_NAME, self::$mTblSess, $wSid );
if (!PEAR::isError(self::$mDb->query($wSql))) { return TRUE; }
return FALSE; } // }}}
/** {{{ sessGc() * * @param NULL * * @return Bool TRUE/FALSE */ public function sessGc() { global $db;
//计算出过期时间 $last = time() - self::$mSessMaxTime;
$wSql = sprintf("DELETE FROM `%s`.`%s` WHERE `%s` < $last;", DB_NAME, self::$mTblSess, self::$mTblSessMap[’last’]);
if (!PEAR::isError(self::$mDb->query($wSql))) { return TRUE; }
return FALSE; } // }}}
/** {{{ initSess() * * @param NULL * * @return Bool TRUE/FALSE */ public function initSess() { $domain = ’’;
//不使用 GET/POST 变量方式 ini_set(’session.use_trans_sid’, 0);
//设置垃圾回收最大生存时间 ini_set(’session.gc_maxlifetime’, SESS_LIFTTIME);
//使用 COOKIE 保存 SESSION ID 的方式 ini_set(’session.use_cookies’, 1); ini_set(’session.cookie_path’, ’/’);
//多主机共享保存 SESSION ID 的 COOKIE ini_set(’session.cookie_domain’, $domain);
//将 session.save_handler 设置为 user,而不是默认的 files session_module_name(’user’);
//定义 SESSION 各项操作所对应的方法名: session_set_save_handler( array(’Usess’, ’sessOpen’), //对应于静态方法 My_Sess::open(),下同。 array(’Usess’, ’sessClose’), array(’Usess’, ’sessRead’), array(’Usess’, ’sessWrite’), array(’Usess’, ’sessDestroy’), array(’Usess’, ’sessGc’) ); session_start();
return TRUE; } // }}}
}//end class
}//end define
$sess = new Usess; $sess->initSess(); ?>
以上代码在 php5.20 中测试通过。
NFS共享
前言,Nio大侠提出了session多服务器共享的问题,原文请见PHP 实现多服务器共享 SESSION 数据。其中,有一种方法就是利用NFS来共享session,如果session量比较大并且所有的session文件都在同一个子目录下的话,那么可能会由此带来很严重的负载问题,甚至导致网站无法使用。本文就是对这个方案做一下详细的解说。 首先,修改 php.ini的 session.save_path 选项,大致如下: session.save_path = "2;/tmp/php_sess" 意为把session存放在 "/tmp/php_sess" 目录下,并且分成 2 级子目录,每级子目录又分别有 16 个子目录。 接下来,假设php的主目录为 /usr/local/server/php/,则新建一个文件 /usr/local/server/php/include/php/ext/session/mod_files.sh,其内容如下: #! /bin/sh # NAME # mod_files.sh - Update of the php-source/ext/session/mod_files.sh # # SYNOPSIS # mod_files.sh basedir depth [numberofsubdirs] # # DESCRIPTION # this script creates the directories tree used by php to store the session files # (see php.ini - ’session.save_path’ option) # # Example: if you want php to store the session files in a directory tree # of 3 levels of depth containing 32 directories in each directory, # first, put the setting bellow in the php.ini file: # # session.save_path = "3;/tmp/session" # # Now create the basedir directory: ’mkdir /tmp/session’ # # Then, call this scrip with the following arguments: # # ./mod_files.sh ./mod_files.sh /tmp/session 3 32 if test "$2" = ""; then echo "usage: $0 basedir depth [numberofsubdirs]" echo "numberofsubdirs: if unset, defaults to 16. if 32, 32 subdirs, if 64, 64 subdirs." exit 1 fi if test "$2" = "0"; then exit 0 fi hash_chars="0 1 2 3 4 5 6 7 8 9 a b c d e f" if [ ! -z $3 ] ; then if test "$3" -a "$3" -eq "32"; then hash_chars="$hash_chars g h i j k l m n o p q r s t u v" if test "$3" -eq "64"; then hash_chars="$hash_chars w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z - ," fi fi fi for i in $hash_chars; do newpath="$1/$i" mkdir $newpath || exit 1 sh $0 $newpath `expr $2 - 1` $3 done 设置为可执行之后,运行以下命令来创建哈希目录: shell>#cd /usr/local/server/php/include/php/ext/session/ shell>#./mod_files.sh /tmp/php_sess 2 16 现在,就开始设置 NFS 共享了。假定有3台主机,ip分别为192.168.0.1(主机名svr1)、192.168.0.2(主机名svr2)、192.168.0.3(主机名svr3),现在让192.168.0.1来提供NFS共享服务,配置 /etc/exports,加入如下内容: /tmp/php_sess/ svr*(rw,no_root_squash) 然后重启 nfs 服务,即可对另外两台主机提供NFS共享了。 在 svr2、svr3 上执行以下命令来挂在NFS: shell>#mkdir /tmp/php_sess shell>#mount svr1:/tmp/php_sess /tmp/php_sess 用NFS来存储session的缺点是,session过期后不能自动清除,必须自己设定回收机制,我们可以利用crontab来定期回收,用用以下shell命令即可: find /tmp/php_sess -mmin +30 | xargs rm -fr 意思是,删除30分钟以前的session文件,具体的时间请大家自己重新设置吧。 最后,在这两个主机上对 php.ini 增加/修改上面提到的内容,然后重启apache即可。
memcache共享
本文是 PHP实现多服务器session共享之NFS共享的延续,不过这次,我采用memcache来存储session了,memcache的安装请看这里: 搭建高效、可靠、稳定的WEB服务器 -- 二、) 安装MySQL、memcache。 接下来,再自定义一套session处理机制,关于session的实现方法我就不再多讲,直接贴程序了。 <?php /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */ //=========================================== // 程序: Memcache-Based Session Class // 功能: 基于Memcache存储的 Session 功能类 // 作者: yejr // 网站: http://imysql.cn // 时间: 2007-01-05 //=========================================== /** * 文件: MemcacheSession.inc.php * 类名: MemcacheSession Class * 功能: 自主实现基于Memcache存储的 Session 功能 * 描述: 这个类就是实现Session的功能,基本上是通过 * 设置客户端的Cookie来保存SessionID, * 然后把用户的数据保存在服务器端,最后通过 * Cookie中的Session Id来确定一个数据是否是用户的, * 然后进行相应的数据操作 * * 本方式适合Memcache内存方式存储Session数据的方式, * 同时如果构建分布式的Memcache服务器, * 能够保存相当多缓存数据,并且适合用户量比较多并发比较大的情况 * * 注意: 本类必须要求PHP安装了Memcache扩展或者必须有Memcache的PHP API * 获取Memcache扩展请访问: http://pecl.php.net */ //设定 SESSION 有效时间,单位是 秒 define(’SESS_LIFTTIME’, 3600); //定义memcache配置信息 define(’MEMCACHE_HOST’, ’localhost’); define(’MEMCACHE_PORT’, ’10000’); if (!defined(’MemcacheSession’)) { define(’MemcacheSession’, TRUE); class MemacheSession { // {{{ 类成员属性定义 static $mSessSavePath; static $mSessName; static $mMemcacheObj; // }}} // {{{ 初始化构造函数 /** * 构造函数 * * @param string $login_user 登录用户 * @param int $login_type 用户类型 * @param string $login_sess 登录Session值 * @return Esession */ public function __construct() { //我的memcache是以php模块的方式编译进去的,可以直接调用 //如果没有,就请自己包含 Memcache-client.php 文件 if (!class_exists(’Memcache’) || !function_exists(’memcache_connect’)) { die(’Fatal Error:Can not load Memcache extension!’); } if (!empty(self::$mMemcacheObj) && is_object(self::$mMemcacheObj)) { return false; } self::$mMemcacheObj = new Memcache; if (!self::$mMemcacheObj->connect(MEMCACHE_HOST , MEMCACHE_PORT)) { die(’Fatal Error: Can not connect to memcache host ’. MEMCACHE_HOST .’:’. MEMCACHE_PORT); } return TRUE; } // }}} /** {{{ sessOpen($pSavePath, $name) * * @param String $pSavePath * @param String $pSessName * * @return Bool TRUE/FALSE */ public function sessOpen($pSavePath = ’’, $pSessName = ’’) { self::$mSessSavePath = $pSavePath; self::$mSessName = $pSessName; return TRUE; } // }}} /** {{{ sessClose() * * @param NULL * * @return Bool TRUE/FALSE */ public function sessClose() { return TRUE; } // }}} /** {{{ sessRead($wSessId) * * @param String $wSessId * * @return Bool TRUE/FALSE */ public function sessRead($wSessId = ’’) { $wData = self::$mMemcacheObj->get($wSessId); //先读数据,如果没有,就初始化一个 if (!empty($wData)) { return $wData; } else { //初始化一条空记录 $ret = self::$mMemcacheObj->set($wSessId, ’’, 0, SESS_LIFTTIME); if (TRUE != $ret) { die("Fatal Error: Session ID $wSessId init failed!"); return FALSE; } return TRUE; } } // }}} /** {{{ sessWrite($wSessId, $wData) * * @param String $wSessId * @param String $wData * * @return Bool TRUE/FALSE */ public function sessWrite($wSessId = ’’, $wData = ’’) { $ret = self::$mMemcacheObj->replace($wSessId, $wData, 0, SESS_LIFTTIME); if (TRUE != $ret) { die("Fatal Error: SessionID $wSessId Save data failed!"); return FALSE; } return TRUE; } // }}} /** {{{ sessDestroy($wSessId) * * @param String $wSessId * * @return Bool TRUE/FALSE */ public function sessDestroy($wSessId = ’’) { self::sessWrite($wSessId); return FALSE; } // }}} /** {{{ sessGc() * * @param NULL * * @return Bool TRUE/FALSE */ public function sessGc() { //无需额外回收,memcache有自己的过期回收机制 return TRUE; } // }}} /** {{{ initSess() * * @param NULL * * @return Bool TRUE/FALSE */ public function initSess() { $domain = ’.imysql.cn’; //不使用 GET/POST 变量方式 ini_set(’session.use_trans_sid’, 0); //设置垃圾回收最大生存时间 ini_set(’session.gc_maxlifetime’, SESS_LIFTTIME); //使用 COOKIE 保存 SESSION ID 的方式 ini_set(’session.use_cookies’, 1); ini_set(’session.cookie_path’, ’/’); //多主机共享保存 SESSION ID 的 COOKIE ini_set(’session.cookie_domain’, $domain); //将 session.save_handler 设置为 user,而不是默认的 files session_module_name(’user’); //定义 SESSION 各项操作所对应的方法名: session_set_save_handler( array(’MemacheSession’, ’sessOpen’), //对应于静态方法 My_Sess::open(),下同。 array(’MemacheSession’, ’sessClose’), array(’MemacheSession’, ’sessRead’), array(’MemacheSession’, ’sessWrite’), array(’MemacheSession’, ’sessDestroy’), array(’MemacheSession’, ’sessGc’) ); session_start(); return TRUE; } // }}} }//end class }//end define $memSess = new MemacheSession; $memSess->initSess(); ?> 然后,在项目程序的头文件中直接包含 MemacheSession.inc.php 即可,并且以前的程序不用做任何改动。 特别感谢:黑夜路人 的 实现基于Memcache存储的Session类。 备注:memcache PECL 未来版本中,可以直接设置 php.ini 来这定自己的 session.save_handler,大致如下: session.save_handler = memcache session.save_path = "tcp://host:port?persistent=1&weight=2&timeout=2&retry_interval=15,tcp://host2:port2"
|