对 PHP SESSION 的深刻认识(四)---- 缓存(memcache和redis)存储session

来源:互联网 发布:虚拟主机控制面板源码 编辑:程序博客网 时间:2024/05/22 10:42

本篇博客将带着大家实现使用缓存系统来存储 session 数据,其中会介绍两个缓存系统 :memcache 和 redis。

一、使用 memcache:

如果大家有看过我之前的这篇博客 《memcache 和 memcached 的区别分析》,就会发现,PHP两个扩展中的 memcached 工作的更好,因此这篇博客在使用 memcache 服务时我选择的是 memcached 扩展。

1、使用 memcached 提供的 session 支持实现(最简单的方法)

memcached 提供了一个自定义的 session 处理器可以被用于存储用户session 数据到 memcached 服务端。 一个完全独立的 memcached 实例将会在内部使用,因此如果需要您可以设置一个不同的服务器池。

session 的 key 被存储在前缀 memc.sess.key. 之下,因此, 如果你对session 和通常的缓存使用了同样的服务器池,请注意这一点。 译注:另外一个 session 和通常缓存分离的原因是当通常的缓存占满了memcached 服务端后,可能会导致你的 session 被从缓存中踢除,导致用户莫名的掉线。

上面的话都是引用自 PHP 官方手册:http://php.net/manual/zh/memcached.sessions.php

下面我们通过实例来实现将 session 数据存储到 memcache。

第一步,设置session用memcache来存储:

打开配置文件 php.ini ,修改以下两项:

session.save_handler = memcachedsession.save_path = "127.0.0.1:11211"

重启服务器。

关于修改配置的话,如果你没有修改 php.ini 的权限,或者你只想在当前应用中使用 memcache 来存储 session 的话,可以使用局部的设置:

#在某个php文件中ini_set('session.save_handler','memcached');ini_set('session.save_path','127.0.0.1:11211');

配置好之后我们就可以使用了,非常简单:

第二步,在代码中使用会话:

#test1.php 文件<?php//如果你没有修改配置文件 php.ini,则用下面两行代码ini_set('session.save_handler','memcached');ini_set('session.save_path','127.0.0.1:11211');//开启会话session_start();if(!isset($_SESSION['name'])){    $_SESSION['name'] = 'default';}else{    $_SESSION['name'] = 'lsgogroup';}$_SESSION['age'] = 20;echo session_id();  //获取客户端的sessionId,即 PHPSESSID,后面会用到

在浏览器中打开 test1.php,然后我们在 test2.php 中验证是否操作成功。

#test2.php 文件<?php//如果你没有修改配置文件 php.ini,则用下面两行代码ini_set('session.save_handler','memcached');ini_set('session.save_path','127.0.0.1:11211');//开启会话session_start();var_dump($_SESSION);

在浏览器中打开 test2.php ,返回:

array(2) { ["name"]=> string(7) "default" ["age"]=> int(20) }

我们在 test3.php 中验证 session 是否是存储到了 memcached 中,由于前面说了session 的 key 被存储在前缀 memc.sess.key. 之下,因此当我们要从 memcached 中读取 session数据,我们指定的 key 是 memc.sess.key.sessionId,其中 sessionId 我们在 test1.php 中输出了,直接复制,或者从浏览器中的 cookie 中复制(我这里输出的是 g5ef37mnb7dstkf1kesegbajb7)。

#test3.php 文件#这里假设大家知道 memcached 的一些操作<?php$mem = new memcached();$mem->addServer("127.0.0.1",11211);echo $mem->get('memc.sess.key.g5ef37mnb7dstkf1kesegbajb7');

返回:

name|s:7:"default";age|i:20;

如果刷新 test1.php 页面,test2.php 和 test3.php 的输出如下:

#test2.phparray(2) { ["name"]=> string(9) "lsgogroup" ["age"]=> int(20) }#test3.phpname|s:9:"lsgogroup";age|i:20;

由上面的 test2.php 和 test3.php 的输出中我们成功的将 session 数据存储到 memcached 中。

memcached 对于 session 的支持还有好几个设置,可能大家都会用到,你如说前面的 key 的前缀设置默认是 memc.sess.key.,我们可以通过修改配置文件 php.ini,将配置项改成

memcached.sess_prefix = "zhongjin."

或这样:

ini_set("memcached.sess_prefix","zhongjin.");

那么你在 test3.php 中就可以这样读取 session 数据了:

echo $mem->get('zhongjin.g5ef37mnb7dstkf1kesegbajb7');

更多有用的配置项大家可以参考官方手册 :http://php.net/manual/zh/memcached.configuration.php

第三步,必要了解过期 session 数据的清理

首先,使用 memcached,将 session 数据都存储在内存中,一旦宕机,数据都会丢失,但对于 session 数据来说这其实并不是什么严重的问题。

其次,前面官方手册说道, session 和通常缓存分离的原因是当通常的缓存占满了memcached 服务端后,可能会导致你的 session 被从缓存中踢除,导致用户莫名的掉线。怎么理解?我们要从 memcache 淘汰数据的机制说起。

Memcached主要的cache机制是LRU(最近最少用)算法+超时失效(学过操作系统的同学应该会记忆幽深)。当您存数据到memcached中,可以指定该数据在缓存中可以呆多久。如果memcached的内存不够用了,过期的slabs会优先被替换,接着就轮到最老的未被使用的slabs。

怎样判断session失效了呢?在php.ini中有个Session.cookie_lifetime的选项,这个代表SessionID在客户端Cookie储存的时间,默认值是“0”,代表浏览器一关闭,SessionID就作废,这样不管保存在Memcached中的Session是否还有效(保存在Memcached中的session会利用Memcached的内部机制进行处理,即使session数据没有失效,而由于客户端的SessionID已经失效,所以这个key基本上不会有机会使用了,利用Memcached的LRU原则,如果Memcached的内存不够用了,新的数据就会取代过期以及最老的未被使用的数据),因为SessionID已经失效了,所以在客户端会重新生成一个新的SessionID。

我们再来解释官方手册的问题,通常的缓存是指我们在php中使用memcache来存储数据库查询结果等数据,导致占用大量的分配给 memcache 的内存,这样可能有某个用户的session数据会因此被淘汰掉,而此时用户还在线,就会导致用户意外掉线。所以官方手册上建议将 session 和通常的缓存分离。

2、通过php提供的接口,自己改写 session 的处理函数

通过自定义 session 的处理函数,我们能够更加自如的控制 session 的存取。更加多的细节和思想大家参考我的上一篇博客 《对 PHP SESSION 的深刻认识(三)—- 数据库存储session》,在这里我直接给出实现:

第一步,修改配置,告诉 php 引擎使用我们自己的session处理函数

打开配置文件 php.ini,修改配置项:

session.save_handler = user

同时注释 session.save_path 项(在前面添加 ;).

或者:

#在某个php文件中 ini_set('sesion.save_handler','user');

第二步,编写会话函数

新建 session.inc.php(结构跟session存储在数据库中的代码结构一样),代码如下:

#session.inc.php 文件<?php/** * Created by PhpStorm. * User: lsgozj * File: session.inc.php * Desc: 处理 session 的自定义类 * Date: 16-12-13 * Time: 下午2:45 */class memcachedSession implements SessionHandlerInterface{    private $_mem = null;   //memcached链接句柄    //这些信息应该放在配置文件中。。。。    private $_configs = array(        'host' => '127.0.0.1',     //主机域        'port' => 11211,           //端口        'prefix' => '',              //key前缀        'expire' => 0                //有效时间    );    public function __construct()    {        //默认获取配置文件中的配置        $this->_configs['prefix'] = ini_get('memcached.sess_prefix');        $this->_configs['expire'] = ini_get('session.gc_maxlifetime');    }    //自定义session_start()函数    public static function my_session_start()    {        $sess = new self;        session_set_save_handler($sess);     //注册自定义函数,在php5.4之后,session_set_save_handler()参数直接传SessionHandlerInterface类型的对象即可。        session_start();    }    /**     * session_start() 开始会话后第一个调用的函数,类似于构造函数的作用     * @param string $save_path 默认的保存路径     * @param string $session_name 默认的参数名(PHPSESSID)     * @return bool     */    public function open($save_path, $session_name)    {        $mem = new memcached();        $mem->addServer($this->_configs['host'], $this->_configs['port']);        $this->_mem = $mem;        return true;    }    /**     * 类似于析构函数,在write()之后调用或者session_write_close()函数之调用     * @return bool     */    public function close()    {        $this->_mem = null;        return true;    }    /**     * 读取session信息     * @param string $sessionId 通过该ID(客户端的PHPSESSID)唯一确定对应的session数据     * @return session信息或者空串(没有存储session信息)     */    public function read($sessionId)    {        //根据配置文件获取前缀(当然也可以自定义)        return $this->_mem->get($this->_configs['prefix'] . $sessionId);    }    /**     * 写入或修改session数据     * @param string $sessionId 要写入数据的session对应的id(PHPSESSID)     * @param string $sessionData 要写入的是数据,已经序列化过的     * @return bool     */    public function write($sessionId, $sessionData)    {        return $this->_mem->set($this->_configs['prefix'] . $sessionId, $sessionData, $this->_configs['expire']);    }    /**     * 主动销毁session会话     * @param string $sessionId 要销毁的会话的唯一ID     * @return bool     */    public function destroy($sessionId)    {        return $this->_mem->delete($this->_configs['prefix'] . $sessionId);    }    /**     * 清理会话中的过期数据     * @param int $maxlifetime 有效期(自动读取配置文件 php.ini 中的 session.gc_maxlifetime 配置项)     * @return bool     */    public function gc($maxlifetime)    {        return true;    }}

对以上代码的必要解释:

1、存储时使用的 key 前缀和存储的生命周期读取的是配置文件 php.ini 的设置,如果需要自定义,在构造函数中修改或者直接在 _configs 数组中定义,然后删除构造函数。
2、由于我们在write()函数中设置缓存的时候已经指定生命周期,过期的数据会由 memcache 自动清理,所以后面的 gc() 函数已经没什么意义了,直接 return true 即可。
3、在使用 memcached 存储session数据的时候,有效时间不能超过30天。

我们在 test.php 文件中测试一下是否可用:

#test.php 文件<?phprequire_once('./session.inc.php');memcachedSession::my_session_start();     //开启会话$_SESSION['name'] = 'LSGOZJ';$_SESSION['age'] = 22;var_dump($_SESSION);echo '<br>'.session_id();   //获取SESSIONID,我们后面测试会用到,我现在测试得到的是 g5ef37mnb7dstkf1kesegbajb7

在浏览器中访问 test.php,然后我们在 test1.php 中看看memcached 中是否已经存储了 session 数据:

#test1.php 文件<?php$mem = new memcached();$mem->addServer('127.0.0.1',11211);$prefix = ini_get("memcached.sess_prefix");echo $mem->get($prefix."g5ef37mnb7dstkf1kesegbajb7");

在浏览器打开 test1.php 返回:

name|s:6:"LSGOZJ";age|i:22;

这个结果也能证明,我们已经成功的将 session 数据存储到 memcached 中了。

第三步、简单谈谈session的清理

在上面我们为我们的每一条 session 设置了生命周期,如果到达这个时间该条session还没有被访问的话,那么 memcached 视之为垃圾数据,但是,并不是到期了就把该条 session 数据删除,而是在下一次访问该条 session 数据的时候,检测是否到了有效期,过了有效期才把它从内存中删除。(如果用轮询的办法每时每刻的检测是否到期,那得多耗内存。。。。)

二、使用 redis:

由于使用 redis 和使用 memcache 是差不多的,因此这部分就快速的带过了。

1、使用 redis 提供的 session 支持实现(最简单的方法)

修改配置文件 php.ini:

session.save_handler = redissession.save_path = 'tcp://127.0.0.1:6379'

或是:

#某个php文件ini_set('session.save_handler','redis');ini_set('session.save_path','tcp://127.0.0.1:6379');

2、通过php提供的接口,自己改写 session 的处理函数

关于这一部分,跟上面 memcached 的内容非常像,就只有个别函数比如设置有效时间,redis使用setex()函数,等等跟memcached 不一样,我就不再举例子了。

总结:

1、“对 PHP SESSION 的深刻认识”这个小系列已经算是完成了。如果大家想了解 session 的原理等内容,可以回去看看我前面的几篇文章《对 PHP SESSION 的深刻认识(一)》、《对 PHP SESSION 的深刻认识(二)》 、《对 PHP SESSION 的深刻认识(三)—- 数据库存储session》

2、在写本篇博客和上一篇博客《memcache 和 memcached 的区别分析》之前,我对 memcached、redis 仅停留在简单使用的层面上,只知道这两个东西是个很强大的工具。在查阅了很多资料之后,我对这两个东西有了更深刻的了解。

0 0
原创粉丝点击