session、cookie、Memcache总结

基于Memcache的 Session数据的多服务器共享(一)

一 相关介绍
1. memcache + memcache的多服务器数据共享 的介绍,请参见http://www.guigui8.com/index.php/archives/206.html

2. session机制:

session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。
当程序需要为某个客户端的请求创建一个session的时候,服务器首先检查这个客户端的请求里是否已包含了一个session标识 - 称为 session id,如果已包含一个session id则说明以前已经为此客户端创建过session,服务器就按照session id把这个 session检索出来使用(如果检索不到,可能会新建一个),如果客户端请求不包含session id,则为此客户端创建一个session并且生成一个与此session相关联的session id,session id的值应该是一个既不会重复,又不容易被找到规律以仿造的字符串,这个 session id将被在本次响应中返回给客户端保存。

保存这个session id的方式可以采用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信息操作建切换。
以下代码,已经过多次实际测试,能达到以上功能需求。先贴下面了:
[php]/**
*=---------------------------------------------------------------------------=
* MemcacheSession.class.php
*=---------------------------------------------------------------------------=
*
* 实现基于Memcache存储的 Session 功能
* (模拟session机制,只是利用memcache将存储媒介换成共享服务器的内存)
*
* 缺点:暂时没有引入不同主域的session共享机制的实现策略。即只支持同主域下的实现。
*
* Copyright(c) 2008 by guigui. All rights reserved.
* @author guigui
* @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;
}
}[/php]

四 应用

1. 本地session存储,与原始session操作方式一样,没有任何改变。如

[php]session_start();
$_SESSION['file_session_info'] = '本地文件保存的session信息'; //本地文件保存的session[/php]

2. memcache共享服务器的session存储

[php]$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();[/php]

3. 分别获取本地和memcache存储的session信息

[php]
$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');
[/php]

 

php中session和cookie的分析

php只使用cookie这点就不用说了,也就是在client端的本地磁盘里保存一个值,然后client发起链接的时候在头文件里会带上这个值

重点是php使用session的情况

第一种情况:浏览器关闭cookie

服务端的代码:
session.php

01<?
02session_start();
03$_SESSION["name"]="wyy";
04$url="hello.php";
05echo "<a href=$url>go</a>";
06print_r($_SESSION);
07echo "<br>";
08echo $_SESSION["name"]."<br>";
09echo session_id();
10?>


hello.php

1<?
2session_start();
3echo $_SESSION["name"];
4$url="session.php";
5echo "<a href=$url>back</a>";
6print_r($_SESSION);
7echo "<br>";
8echo session_id();
9?>



第一步:浏览器第一次向服务器发起链接
1.png
第二步:刷新浏览器
2.png
第三步:点击跳转【不开新窗口】得到不同的又一个phpsessid

由此可见,以上三步都是不同的sessionid,因为cookie的禁用,本地无法保存sessionid,于是浏览器便无法向服务器发送sessionid,每次都等同于开一个新的.

下面用php的session.use_trans_sid来解决浏览器不开启cookie的问题

1、确保php安装的时候带有 --enable-trans-sid 详情查看phpinfo.php页面
3.png
2、php默认没有开启,需要修改php.ini将session.use_trans_sid = 的值改为1
4.png
php.ini中关于session.use_trans_sid的配置

01; trans sid support is disabled by default.
02; Use of trans sid may risk your users security.
03; Use this option with caution.
04; - User may send URL contains active session ID
05; to other person via. email/irc/etc.
06; - URL that contains active session ID may be stored
07; in publically accessible computer.
08; - User may access your site with the same session ID
09; always using URL stored in browser's history or bookmarks.
10session.use_trans_sid = 1



第一次请求,服务器返回phpsessionid
5.png
刷新,返回一个新的phpsessin
6.png
跳转,自动在url上加上了sessionid的值于是在新页面能通过这个id获取到值
7.png
补充:直接复制跳转的地址,在新的firefox窗口和ie窗口打开,也能得到值[这里的值与原来不同是为后来重新截取的图。呵呵]。
11.png

服务器的值都保存php.ini指定目录下面,命名方式为[这里使用的是file存session,后面会讲到在memcache中存放的原理]
-rw------- 1 apache apache 0 Apr 9 05:38 sess_tkevjt5dvil4ld7823d8ea90e5
-rw------- 1 apache apache 0 Apr 9 05:28 sess_tmmkii8hu7kmfgdsbiiaktbki2
-rw------- 1 apache apache 15 Apr 9 05:29 sess_trp1obciu6uudmmitshtc8qtm6
-rw------- 1 apache apache 15 Apr 9 05:36 sess_ulhcgsu918tgjdvq8l1mv5r5k6
-rw------- 1 apache apache 0 Apr 9 05:28 sess_uqp7tmt0o79qvfa0abgnmqsd17
-rw------- 1 apache apache 15 Apr 9 05:38 sess_v47jjudip39jhe63tsc8bcp52
里面是明文的值
-rw------- 1 apache apache 15 Apr 9 06:27 sess_u1lm2mokuefqoe54798isdtjl4
[root@22455 session]# cat sess_u1lm2mokuefqoe54798isdtjl4
name|s:3:"wyy";[root@22455 session]#



第二种情况(1):下面开始浏览器开启cookie的情况
[关闭之前的session.use_trans_sid]


第一步,浏览器请求,服务器返回sessionid,浏览器将sessionid正放于本地cookie

12.png


刷新或打开新firefox窗口,

41.png
会话会保持住。因为浏览器在发送请求的时候会向服务器发送一个phpsessid的cookie值.

另外一点,服务器保存的cookie值是随php.ini中关于session时间设置消亡的,可以设定时间,也可以设定是题否随着浏览器关闭而删除。

第二种情况(2):下面开始浏览器开启cookie的情况[启用session.use_trans_sid]

注意,此时在ie和firefox出现一致的情况,如果开启了cookie,第一次将出来带有sessionid的url,但在上面反复切换链接,以后的 url都不再带有了。因为此时已经有cookie写入了本地,但在本地文件夹里没有找到ie的,firefox连保存路径都没有查到。后经过核实,id是 写入了/user decument /cookie/index.dat,关于index.dat可查看

http://baike.baidu.com/view/1379495.htm

81.png

另一个测试,测试开启cookie情况下,到底是哪种在起作用,

清cookie,开浏览器,第一次访问获得跳转的页面,更改url中的phpsessid值,访问新的页面,发现服务器能获取到正确的sessionid,而不是浏览器地址中的phpsessid

10.png

 

session,cookie,memcache总结

问题总结一下。

         1,原来程序运行在一台服务器上,用session存储一些变量,程序运行没有任何问题。

         2,原程序部署到外网上,外网是三台服务器并行运行,出现session混乱问题。

         3,第一次解决办法:采用memcache解决session问题,需要用的代码如下:

         function memcache_set_data($key,$data)
        {
               $mem = new Memcache;
               $mem->connect("ft89.mc.intra.mobile.sina.cn", 11212);
               $mem->set($key,$data);
        }
       function memcache_get_data($key)
      {
              $mem = new Memcache;
              $mem->connect("ft89.mc.intra.mobile.sina.cn", 11212);
              return $mem->get($key);
      }
      function memcache_delete_data($key)
     {
             $mem = new Memcache;
            $mem->connect("ft89.mc.intra.mobile.sina.cn", 11212);
            $mem->delete($key);
      }

        此时,可以解决问题,但是对程序修改比较大,所有用到session的地方,都需要修改。

 

       4,用cookie代替session,将于客户有关的数据存放在cookie里面,主要获取代码如下:

       $arr = array(
                        'role'=>$role,
                        'business_id'=>$business_id,
                        'business_name'=>$business_name,
                        'zw_name'=>$zw_name,
                        'date'=>$date
                        );                        
       setcookie('info',serialize($arr),time()+36000);//存储cookie数据

       $arr = unserialize(StripSlashes($_COOKIE['info']));//获取cookie数据
       $role = $arr['role'];
       $business_id = $arr['business_id'];
       $business_name = $arr['business_name'];
       $zw_name = $arr['zw_name'];
       $date = $arr['date'];

       因为cookie安全性比较低,并且与浏览器设置联系紧密,所有使用中会出现问题。

       5,最终采用如下方法:

       ini_set('session.save_handler', 'memcache');
       ini_set('session.save_path', 'ft89.mc.intra.mobile.sina.cn:11212');

       用到session文件里面,在session_start之前,写上上面代码,就可以解决session问题。如此,原有程序不用做任何修改。

       6,采用5方法可以有两种实现途径:

       (1)系统级在ini文件里面设置“save_handler”和"save_path"。

       (2)如5,在每个需要的程序代码里面包含这两行代码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值