西部数码主机 | 阿里云主机| 虚拟主机 | 服务器 | 返回乐道官网
当前位置: 主页 > php教程 > magento教程 >

Magento会话共享改进(快慢两层存储)

时间:2016-04-04 11:24来源: 作者: 点击:
如果使用了服务器集群,那么会话数据共享是必须实现的,因为请求进入的服务器可能不一样,但是它的会话ID是一样的,所以它的会话内容应该向各个应用服务器开放。用很多方法可以

如果使用了服务器集群,那么会话数据共享是必须实现的,因为请求进入的服务器可能不一样,但是它的会话ID是一样的,所以它的会话内容应该向各个应用服务器开放。用很多方法可以实现会话共享,比如可以在数据库存储服务器上划分一个区,然后各个应用服务器都mount这个区,会话都写入这里,理论上就能解决会话共享问题,或者直接保存到后端的数据库,每个应用都链接到后端数据库获取会话内容,这个实现也非常简单,如果数据库服务器压力大,可以进行主从复制读写分离。或者存入文件和存入数据库都不够快,那么可以存入到共享内存中,比如Memecached,不过存入共享内存有一个缺点,共享内存用完可能会产生问题,于是有了一个想法,能不能实现像缓存一样实现两层存储?

所谓两层,简单描述如下:
保存到Memcached中的同时也保存到数据库中,写入时监控共享内存利用率,如果超过伐值,从共享内存中删除这个会话,这时可能释放了共享内存,只保存到了数据库,而取数据时,先到共享内存取,如果没有命中则到数据库取。

经过研究,在Magento中只要重写Mage_Core_Model_Resource_Session类就能实现。

首先在自定义模块的配置文件中重写这个类:

1
2
3
4
5
6
7
8
9
10
11
12
<global>
        <models>
            <mysession>
                <class>Vfeelit_Mysession_Model</class>
            </mysession>
            <core_resource>
                <rewrite>
                     <session>Vfeelit_Mysession_Model_Resource_Session</session>
                </rewrite>
            </core_resource>
        </models>
</global>

然后我们的Vfeelit_Mysession_Model_Resource_Session继承自Mage_Core_Model_Resource_Session。在构造函数中引入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function __construct()
{
    $mySession = Mage::getConfig()->getNode(self::XML_NODE_MYSESSION)->asArray();
    
    $prefix = $mySession['session_prefix'];
    if(!empty($prefix)){
        $this->_prefix = $prefix;
    }
 
    $options = $mySession['session_servers'];
    $this->_backend = new Zend_Cache_Backend_Libmemcached($options);
 
    parent::__construct();
}

我这里实例化一个Zend_Cache_Backend_Libmemcached对象(PHP必须安装了Memcached扩展),用它来链接Memcached服务器,把数据保存到共享内存中。我这里还设置了一个key的前缀(因为我自己控制数据存入,所以key就可以自定义了),后面就是调用父类的构造函数,它负责数据库初始化。

要自定义会话的处理,需要使用session_set_save_handler()绑定6个方法,这个已经在Mage_Core_Model_Resource_Session中实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public function setSaveHandler()
{
    if ($this->hasConnection()) {
        session_set_save_handler(
            array($this, 'open'),
            array($this, 'close'),
            array($this, 'read'),
            array($this, 'write'),
            array($this, 'destroy'),
            array($this, 'gc')
        );
    } else {
        session_save_path(Mage::getBaseDir('session'));
    }
    return $this;
}  

看起来,我们不需要动这个方法。我们需要重载的方法是read和write和destory(gc方法直接使用继承过来的即可)。

首先看read重载方法的实现:

1
2
3
4
5
6
7
8
9
10
11
12
public function read($sessId)
{
    if($this->_backend){
        $id = $this->_prefix.$sessId;
        $data = $this->_backend->load($id);
    }
    if(!$data){
        return parent::read($sessId);
    }else{
        return $data;
    }
}

实际上非常简单,从共享内存中取数据,如果没有取到(可能是共享服务器down了或被存满了),就去数据库里取,从数据库取数据的逻辑Magento本身已经实现。

然后看write方法:

1
2
3
4
5
6
7
8
public function write($sessId, $sessData)
{
    if($this->_backend){
        $id = $this->_prefix.$sessId;
        $this->_save($sessData, $id, array(), $this->getLifeTime());
    }
    return parent::write($sessId, $sessData);
}

这个方法同时保存到共享内存和数据库,这个是read方法无法读取到数据时可以从数据库里读取到的保证。这里我实现了一个_save方法抽象了保存的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private function _save($data, $id, $tags = array(), $specificLifetime = false)
{
    $usage = $this->_getFastFillingPercentage('saving');
    $boolFast = true;
 
    if($specificLifetime){
        $lifetime = $specificLifetime;
    }else{
        $lifetime = $this->getLifetime();
    }
 
    //$preparedData = $this->_prepareData($data, $lifetime);
 
    if ( 85 >= $usage) {
        $boolFast = $this->_backend->save($data, $id, array(), time()+$lifetime);
    } else {
        $boolFast = $this->_backend->remove($id);
        if (!$boolFast && !$this->_backend->test($id)) {
            // some backends return false on remove() even if the key never existed. (and it won't if fast is full)
            // all we care about is that the key doesn't exist now
            $boolFast = true;
        }
 
    }
    return $boolFast;
}

通过_getFastFillingPercentage方法获取共享内存的利用率,当利用率没有超过85,直接保存到共享内存中。如果超过了,就把这个KEY从数据库中踢掉,最后确保已经成功删除。这样,到达伐值后,就释放空间,内容从数据库获取,释放的空间和内存过期的会话释放的空间如果低于了伐值,新的会话数据库继续写入共享内存,从而实现了快慢存储。

从测试来看,完成没有问题。另外一个需要主要的是,会话的共享内存空间最好不要和其它的缓存内存混用,因为会话内容共享内存一定要设置过期时间,方便垃圾数据及时退出,而缓存共享内存就不一定了,可以设置它保存很长时间,甚至直到手动刷新或重启Memcached服务时才被清理,所有缓存空间可能会被快速占用而不释放,那么会话内容可能无法存入,加速会话读取(或共享内存)的美梦就会泡汤。

这里的自己写的代码不多,但是要非常清楚的内容就非常多,比如会话机制的原理,共享内存Memcached工作原理,Magento的类重写,Magento对会话的封装过程等。

(责任编辑:好模板)
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------