一、Memcached 存储 Session
由于 Memcached 是分布式的内存对象缓存系统,因此可以用来实现 Session 同步:把 Web 服务器中的内存组合起来,成为一个“内存池”,不管是哪个服务器产生的 Sessoin 都可以放到这个“内存池”中,其他的 Web 服务器都可以使用。使用 Memcached 来同步 Session 的优点是:不会加大数据库的负担,并且安全性比 Cookie 高,把 Session 放到内存里面,读取速度比其他处理方式要快很多。
自定义使用 Memcached 处理 Session 信息,要使用到用户自定义会话存储函数 session_set_save_handler() ,自定义的 Session 类可以参考 ThinkPHP 3.2.3 full 版中的 Session 类(ThinkPHPLibraryThinkSessionDriverMemcache.class.php)
注:使用的 php 扩展为 memcache 1.4.24,服务器环境为 Windows7 + PHP 5.3.10 + Apache 2.2.21
memcached.class.php
<?php class MemcacheSession { protected static $lifeTime = 3600; protected static $handle = null; public function start(Memcache $mem) { self::$handle = $mem; self::$lifeTime = ini_get('session.gc_maxlifetime'); session_set_save_handler( array(__CLASS__, 'open'), array(__CLASS__, 'close'), array(__CLASS__, 'read'), array(__CLASS__, 'write'), array(__CLASS__, 'destroy'), array(__CLASS__, 'gc') ); session_start(); } /** * 打开Session * @access private * @param string $savePath * @param mixed $sessName */ private static function open($savePath, $sessName) { return true; } /** * 关闭Session * @access public */ public static function close() { self::gc(self::$lifeTime); self::$handle->close(); self::$handle = null; return true; } /** * 读取Session * @access private * @param string $sessID */ private static function read($sessID) { return self::$handle->get($sessID); } /** * 写入Session * @access public * @param string $sessID * @param String $sessData */ public static function write($sessID, $sessData) { return self::$handle->set($sessID, $sessData, 0, self::$lifeTime); } /** * 删除Session * @access private * @param string $sessID */ private static function destroy($sessID) { return self::$handle->delete($sessID); } /** * Session 垃圾回收 * @access private * @param string $sessMaxLifeTime */ private static function gc($sessMaxLifeTime) { return true; } }
使用 memcached.class.php
session.php
<?php header('Content-type:text/html; charset=utf-8'); require 'memcache.class.php'; $mem = new Memcache(); $mem->connect('127.0.0.1', 11211); $memclass = new MemcacheSession(); $memclass->start($mem); $_SESSION['username'] = 'dee'; echo '<a href="get.php" target="_blank">跳转</a>'; echo '<pre>'; print_r($_SESSION); echo session_id(),'<br />';
get.php
<?php require 'memcache.class.php'; $mem = new Memcache(); $mem->connect('127.0.0.1', 11211); $memclass = new MemcacheSession(); $memclass->start($mem); echo '<pre>'; print_r($_SESSION); echo session_id();
二、Memcached 分布式存储 Session
模拟开启两台 Memcached 服务器(127.0.0.1:11211 和 127.0.0.1:11212),引入 Memcached 的 Session 类, 同时修改 php.ini 中的配置
ini_set('session.save_path', 'tcp://127.0.0.1:11211,tcp://127.0.0.1:11212'); ini_set('memcache.hash_strategy', 'consistent');//使用一致性分布式哈希 ini_set('memcache.hash_function','crc32');
此时查看 phpinfo 的信息(该页面需要使用 ini_set 设置配置信息):
设置 Session
cluster_session_set.php
<?php header('Content-type:text/html; charset=utf-8'); require 'memcache.class.php'; ini_set('session.save_path', 'tcp://127.0.0.1:11211,tcp://127.0.0.1:11212'); ini_set('memcache.hash_strategy', 'consistent'); ini_set('memcache.hash_function','crc32'); $mem = new Memcache(); $mem->addServer("127.0.0.1",11211) or die ("Could not add server 11211"); $mem->addServer("127.0.0.1",11212) or die ("Could not add server 11212"); $memclass = new MemcacheSession(); $memclass->start($mem); $_SESSION['username'] = "deathmask"; $_SESSION['level'] = "admin"; echo session_id(); echo '<pre>'; print_r($_SESSION); echo '<a href="cluster_session_get.php" target="_blank">跳转</a>';
输出:
打印 Session
cluster_session_get.php
<?php require 'memcache.class.php'; ini_set('session.save_path', 'tcp://127.0.0.1:11211,tcp://127.0.0.1:11212'); ini_set('memcache.hash_strategy', 'consistent'); ini_set('memcache.hash_function','crc32'); $mem = new Memcache(); $mem->addServer("127.0.0.1",11211) or die ("Could not add server 11211"); $mem->addServer("127.0.0.1",11212) or die ("Could not add server 11212"); $memclass = new MemcacheSession(); $memclass->start($mem); echo session_id(); echo '<pre>'; print_r($_SESSION);
输出:
测试过程:
a. 可以开启多个不同的浏览器,执行 cluster_session_set.php,则会生成多个会话。
注:打开 Memcached 服务器的方式为
-vvv 可以使错误信息或者警告信息在服务器端输出,便于调试
b. 使用 telnet 客户端分别连接两台 Memcached 服务器。
c. 使用浏览器一执行 cluster_session_set.php,生成的 PHPSESSID 是 02mn6pqd7d1vm5iseb3tq5irv2,被分配到了 127.0.0.1:11211 服务器
使用浏览器二执行页面,生成的 PHPSESSID 为 mktiotet1edifq7ttq2nbje480,被分配到了 127.0.0.1:11212 服务器:
三、Memcached 存储 Session 的弊端
① Memcached 把内存分成很多种规格的存储块(chunk),这种方式决定了 Memcached 不能完全利用内存,会产生内存碎片,如果存储块不足,还会产生内存溢出;
② 当 Memcached 集群发生故障(比如内存溢出)或者维护(比如升级、增加或减少服务器)时,用户会无法登录,或者被踢掉线;
③ Memcached 的回收机制(LRU,Least Recently Used 近期最少使用算法)可能会导致用户无缘无故地掉线。
解决方案是可以使用 Memcached + MySQL:
a. 当用户登录时,将 Session “set”到 Memcached,并写入数据库;
b. 在 Session 中增加一个字段,标识 Session 最后写入数据库的时间;
c. 每个页面加载的时候,优先从 Memcached 读取 Session,其次从数据库读取;
d. 每加载 N 页或者 Y 分钟后,再次将 Session 写入数据库;
e. 从数据库中获取过期 Session,优先从 Memcached 中获取最新数据
或者使用 Redeis ,利用 Redis 的持久化来存储 Session。
参考: