Memcached是一个很好的东西.在分布式内存管理领域给我很有启发性.不过其分布式的处理不是在其服务器端实现,而是在基于客户端的一个中间层上
实现的.这种处理分布式的方式在应付中小型要求上很有实际效果.由于不能维持一个动态的分布式Hash表,因此其在分布式应用上的高度还不够.不过这个方
法提供了一个学习的例子. 文章借鉴了heiyeluren的blog(黑夜路人的开源世界)在这篇帖子上编写了一个session类,并做了修改,生成的新类在文章末尾以文件方式给出.如果有兴趣,可以供参考.同时提供了两个运用的例子,在此向heiyeluren致敬了. 本文内容如下: 1.关于本文档 2.libevent,memcache相关说明 3.搭建memcache分布式环境 4.mem_session类说明 5.两个样例程序说明 6.使用过程中需要注意的问题1.关于本文档 本文档是用来构建存储session的memcache环境,并对session操作类mem_session做了详细说明,提供了两个使用该类的例子. 文档分六个部分: 第一部分.关于本文档,主要介绍文档组织和文档目的 第二部分.libevent,memcache相关说明,主要介绍搭建环境中使用到的软件包. 第三部分.搭建memcache分布式环境,主要诠释怎样搭建分布式memcache环境 第四部分.mem_session类说明,主要分析mem_session类的成员函数和变量,是文档的主要部分和重点所在 第五部分.两个样例程序说明,分析两个使用mem_session类操作session的例子 第六部分.使用过程中需要注意的问题,一些使用看法和问题提交 如果是系统工程师,请参考第一,二,三部分即可;如果是开发工程师,请仔细参考第二,四,五,六部分.如果需要在此基础上进行二次开发,请直接查看源代码.memcached的源代码下载地址: http://www.danga.com/memca...,可以下载到最新版本.2.libevent,memcache相关说明
搭建memcache的环境需要安装配置memcache服务器端和安装memcache客户端.而memcache服务器端是以daemon方式运
行,下面将memcache服务器端简称为memcached,是基于libevent库实现异步io(使用epoll)的.因此在安装
memcached端是还需要安装libevent库,如果系统中不存在的话. memcache的客户端是以php extension方式工作的.也即是php官方认可的软件包编译成php extension.当然,完全可以自己写一个memcache的客户端.具体编写方式网络上有一些相关的文档可以参考. 同时,需要注意的是,memcached部分实现的是内存空间分配和回收,以及存储服务监听和提供.对于分布式的实现,取决于客户端的使用和构造.我们使用的客户端是完全支持分布式的.只是可能会出现某些问题,这个在后面会有详细的描述.3.搭建memcache分布式环境
搭建memcache分布式环境需要三个软件包,目前统一软件包的版本为:libevent-1.1b.tar.gz,memcache-
2.1.0.tgz,memcached-1.1.13.tar.gz.这些在31~34的/home/zhengyu/tools里面都能找到. xplore提供下载:libevent,memcached,memcache下载 按以下三个步骤: 1) 先安装libevent:引用# tar zxvf libevent-1.1b.tar.gz# cd libevent-1.1b# ./configure --prefix=/usr# make >make.log 2>&1# sudo make install >install.log 2>&1 如果没有错误,那么应该是安装成功了,可以通过查看/usr/lib目录看看是否安装成功:引用# ls -al /usr/lib | grep libeventlrwxrwxrwx 1 root root 22 Jan 9 13:34 libevent-1.1b.so.1 -> libevent-1.1b.so.1.0.2-rwxr-xr-x 1 root root 91205 Jan 9 13:34 libevent-1.1b.so.1.0.2-rw-r--r-- 1 root root 121472 Jan 9 13:34 libevent.a-rwxr-xr-x 1 root root 808 Jan 9 13:34 libevent.lalrwxrwxrwx 1 root root 22 Jan 9 13:34 libevent.so -> libevent-1.1b.so.1.0.2 2)再安装memcached:引用# tar zxvf memcached-1.1.13.tar.gz# cd memcached-1.1.13# ./configure --prefix=/usr/local --with-libevent=/usr# make >make.log 2>&1# sudo make install >install.log 2>&1 如果中间出现报错,请仔细检查错误信息,按照错误信息来配置或者增加相应的库或者路径。 安装完成后会把memcached放到 /usr/local/bin/memcached ,我们看以下是否安装了:引用# ls -al /usr/local/bin/mem*-rwxr-xr-x 1 root root 78340 Jan 9 13:42 /usr/local/bin/memcached-rwxr-xr-x 1 root root 80365 Jan 9 13:42 /usr/local/bin/memcached-debug 安装完成之后,必须启动一个memcached的守护进程,提供服务,启动方式如下:引用# /usr/local/bin/memcached -d -m 512 -l 10.68.1.31 -p 11211 -u www 还有其他的参数,具体可以参看memcached的说明文档:引用# /usr/local/bin/memcached -hmemcached 1.1.13-p port number to listen on-l interface to listen on, default is INDRR_ANY-d run as a daemon-r maximize core file limit-u assume identity of (only when run as root)-m max memory to use for items in megabytes, default is 64 MB-M return error on memory exhausted (rather than removing items) ////注意-c max simultaneous connections, default is 1024 ////注意-k lock down all paged memory-v verbose (print errors/warnings while in event loop)-vv very verbose (also print client commands/reponses)-h print this help and exit-i print memcached and libevent license-b run a managed instanced (mnemonic: buckets)-P save PID in , only used with -d option ////pid文件
-d选项是启动一个守护进程,-m是分配给memcache使用的内存数量,单位是MB,-l是监听的服务器IP地址,-p是设置memcache监听
的端口, -u是运行memcache的用户.还有-M,-P,-c参数可以设置.也可以启动多个守护进程,不过端口不能重复。 为了方便管理,在31~34的/home/zhengyu/bin下面有一个启动控制脚本,是为memcache服务的启动,关闭,重启服务的.名字为mem_session.sh,执行方式为:引用# /home/zhengyu/bin/mem_session.sh start|stop|restart [512(-m的参数)] 3)最后安装memcache的php客户端,这个在每个需要用到memcache服务的机器上都需要安装,memcache的php客户端是以php extension的方式安装的.引用# tar zxvf memcache-2.1.0.tgz# cd memcache-2.1.0# /usr/local/php/bin/phpize# ./configure --enable-memcache --with-php-config=/usr/local/php/bin/php-config --with-zlib-dir# make >make.log 2>&1# sudo make install >install.log 2>&1 如果执行过程中没有出错的话,在install.log将回写入一个目录,这个目录即为编译好的extension memcache.so的所在地.引用Installing shared extensions: /usr/local/php/lib/php/extensions/no-debug-non-zts-20020429/ 需要在php.ini文件中相应的位置加入:引用extension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20020429/"extension=memcache.so 或者将路径改变,自己定义.然后在memcache-2.1.0下有个example.php,修改了host,port之后就可以做个简单的测试了.4.mem_session类说明
基于heiyeluren编写session类修改而来的mem_session类当作memcached的客户端,提供给php代码使用,此类是
svn库/data0/vshare/htdocs/include/mem_session.php.使用时只需引用该文件,同时引用
memcached服务器ip列表文件/data0/vshare/conf/memcache_server_ip.php(这个在类中已经完成了). 下面详细讲述mem_session中的变量与方法(目前试用的版本): 1.变量:引用
$server_ip = array();
//在/data0/vshare/conf/memcache_server_ip.php中定义的memcached服务器ip列表 $sess_id = ''; //session id,一个md5串,128位,16个字节 $sess_key_prefix = 'sess_'; //区别别的session而加的session头描述 $sess_expire_time = 3600; //session的生存时长,固定为一个小时.目前版本的类中并没有一个修改它的方法. $cookie_name = '__SessHandler'; //client中对应于该session的cookie名称 $cookie_expire_time = ''; //cookie的生存时长,默认为无限长,目前版本的类中也没有一个修改它的方法. $conn = null; //建立到memcached的连接. $error = ''; //错误字符串,目前版本只是简单的出错提示. 2.方法:引用
函数名
参数返回值 函数描述备注1
is_registered $key(string)
true|false(bool) 判断某个键值是否在SESSION中已经注册(存在),存在返回true
无2 register
$key(string)
true|false(bool)判断某个键值是否在SESSION中已经注册(存在),存在返回true
无3 set
$key,$value
true将键为$key的值设置为$value,不管存在与否都将建立
无4 unregister $key true将键为$key的变量注销掉无5
destroy 无
true
将整个Session变量集注销掉无6 get
$key(string)
false|$value(string) 获取键为$key的值$value无7 get_all
无
$SESSION(array) 获取真个Session变量集无8
get_sid 无
$sess_id(string) 获取当前session的32位id无9
get_mem_config 无
$server_ip(array)
获取当前mem_session服务器的ip地址数组无10 close_sess
无
true 关闭当前mem_session的连接无11
debug 无
$error(string)
获取调试信息,简单的为出错信息的字符串无---------------------------------------------------------------------------------------------------------------------------------12
_get_session $sess_id
$sess_data(array)内部函数,通过$sess_id获取$SESSION无13
_save_session $sess_id
true|false(bool)内部函数,保存$SESSION,并将其键设置为$sess_id
无14 _init_memcache_obj 无 true|false(bool)在构造函数中初始化时调用无5.两个样例程序说明 下面的两个例子,可以通过**访问到. example1:引用include_once("include/mem_session.php"); //引入mem_session类说明$mm = new mem_session();echo "starting mem_session test";$sess_id = $mm->get_sid(); //获取当前会话的idecho "";echo " sessid ".var_dump($sess_id);echo "";$all = $mm->get_all(); //获取当前会话种入的所有变量值var_dump($all);$mm->register('akey','i am akey'); //向当前会话中创建一个变量,如果该变量存在,则不会做任何操作$mm->register('bkey','i am bkey');$mm->register('ckey','i am ckey');$a1
= $mm->get('akey');
//获取当前会话中的单个变量.如果变量不存在,返回false.$b1 = $mm->get('bkey');$c1 = $mm->get('ckey');var_dump($a1);var_dump($b1);var_dump($c1);$mm->set("akey","i am aakey,notice: i am changed."); //设置当前会话中的一个变量,如果变量不存在,则先创建,然后设置.如果存在,则无条件设置$a2 = $mm->get("akey");var_dump($a2);$mm->unregister('ckey');
//从当前会话中注销掉一个变量.如果不存在,不做任何操作.$c2 = $mm->get('ckey');var_dump($c2);$error = $mm->debug(); //获取调试信息var_dump($error);$sess_id = $mm->get_sid();echo "";echo " sessid ".var_dump($sess_id);echo "";$another_all = $mm->_get_session($sess_id); //这个是内部函数,测试之用,极不推荐.var_dump($another_all);$mm->close_sess();
//关闭对mem_session服务器的链接?> example2: 这是一个更详细一点的样例.流程和上面的差不多.引用include_once("include/mem_session.php");$mm = new mem_session();echo "starting mem_session test";$sess_id = $mm->get_sid();echo "";echo " sessid ".$sess_id;echo "";$all = $mm->get_all();print_r($all);?> test the mem_session functions:keyword:value:keyword:keyword:sid:if($_POST["set"] != ""){ if((($key = trim($_POST["set_key"]))!="") && (($value = trim($_POST["set_value"]))!="")) { $mm->set($key,$value); echo "".$key." is set to value: ".$value.""; } else { echo "you must fill all two field at first"; }}if($_POST["get"] != ""){ if(($key = trim($_POST["get_key"]))!="") { if($mm->is_registered($key) === true) { $value = $mm->get($key); echo "".$key." is alread exist with value: ".$value.""; } else { echo "".$key." is not exist."; } } else { echo "you must fill the keyword field at first"; }}if($_POST["test"] != ""){ if(($key = trim($_POST["test_key"]))!="") { if($mm->is_registered($key) === true) { $value = $mm->get($key); echo "".$key." is alread exist with value: ".$value.""; } else { echo "".$key." is not exist."; } } else { echo "you must fill the keyword field at first"; }}if($_POST["sid"] != ""){ if((($key = trim($_POST["sid_key"]))!="") && (strlen($key) == 32)) { $all_sid = $mm->_get_session($key); echo ""; var_dump($all_sid); echo ""; } else { echo "you must fill all two field at first OR fill it normally"; }}$error = $mm->debug();echo "The error is: ".$error."";$mm->close_sess();?>6.使用过程中需要注意的问题
对于session而言,关闭浏览器即将结束一个session.也就是session_id将会被撤消.但是储存在memcached服务器内存中的
key-value并没有消失.需要等到memcached的算法清除掉这些过时的信息.因此大量垃圾信息可能会导致memcache的低命中率.如果条
件允许,你可以通过加大-m参数,也就是加大服务的内存空间,可以增加命中率.
由于memcached的思想是在自定义的客户端使用一个lib包支持分布式,这种思路值得学习.解决了一些问题,诸如一旦出现网络问题,能够确保数据
的写入是正常的.但是却会出现另外的一些问题.如,网络问题导致某个分布式服务器中的一台失去联系之后,到这台机器恢复正常工作的这段时间内,
写入分布式服务器的数据将基本不可以获取.可以通过采用分布式hash表的方式解决这个问题.无疑,这将代价十分昂贵.mem_session类://<?phpdefine("MEMCACHE_SERVER_IP","/data0/vshare/conf/memcache_server_ip.php");class mem_session{ var $server_ip = array(); var $sess_id = ''; var $sess_key_prefix = 'sess_'; var $sess_expire_time = 3600; var $cookie_name = '__SessHandler'; var $cookie_expire_time = ''; var $conn = null; var $error = ''; function mem_session() { //array serverips $this->start(); } function start($expireTime = 0) { $sess_id = $_COOKIE[$this->cookie_name]; if (!$sess_id) { $this->error .= "Just a Notice: The sess_id is not exist at first."; $this->sess_id = $this->_get_id(); $this->cookie_expire_time = ($expireTime > 0) ? time() + $expire_time : 0; setcookie($this->cookie_name, $this->sess_id, $this->cookie_expire_time, "/", ''); $this->_init_memcache_obj(); $_SESSION = array(); $this->_save_session(); } else { $this->sess_id = $sess_id; $this->_init_memcache_obj(); $_SESSION = $this->_get_session($sess_id); } } function is_registered($key) { if (!isset($_SESSION[$key])) { return false; } return true; } function register($key, $value) { if (isset($_SESSION[$key])) { return false; } $_SESSION[$key] = $value; $this->_save_session(); return true; } function set($key, $value) { $_SESSION[$key] = $value; $this->_save_session(); return true; } function unregister($key) { unset($_SESSION[$key]); $this->_save_session(); return true; } function destroy() { $_SESSION = array(); $this->_save_session(); return true; } function get($key) { if (!isset($_SESSION[$key])) { return false; } return $_SESSION[$key]; } function get_all() { return $_SESSION; } function get_sid() { return $this->sess_id; } function get_mem_config() { return $this->server_ip; } function close_sess() { memcache_close($this->conn); } function debug() { return $this->error; }///////////////////////////////////internal functions function _get_id() { return md5(uniqid(microtime())); } function _init_memcache_obj() { include(MEMCACHE_SERVER_IP); $server_num = count($serverips); $this->server_ip = $serverips; $conn_main = memcache_connect($serverips[0],11211); $i=1; while((!$conn_main) && ($i < $server_num)) { $conn_main = memcache_connect($serverips[$i],11211); $i++; } if(($i==$server_num) && (!$conn_main)) { $this->error .= "All servers were bad<br>\n"; $this->conn = $conn_main; return false; } for(;$i<$server_num;$i++) { $ret = memcache_add_server($conn_main,$serverips[$i],11211); if($ret!=true) { $this->error .= " Servers: {$serverips[$i]} is bad<br>\n"; } } $this->conn = $conn_main; return true; } function _get_session($sess_id = '') { $sess_key = $this->_get_sess_key($sess_id); $sess_data = memcache_get($this->conn, $sess_key); if (!is_array($sess_data) && empty($sess_data)) { $this->error .= 'Failed: Session ID '. $sess_key .' session data not exists'; return false; } else { return $sess_data; } } function _save_session($sess_id = '') { $sess_key = $this->_get_sess_key($sess_id); $ret = memcache_set($this->conn, $sess_key , $_SESSION, 0, $this->sess_expire_time); if (!$ret) { $this->error .= 'Failed: Save sessiont data failed, please check memcache server'; return false; } else { return true; } } function _get_sess_key($sess_id = '') { $sess_key = ($sess_id == '') ? $this->sess_key_prefix.$this->sess_id : $this->sess_key_prefix.$sess_id; return $sess_key; } ///////////////////////////////////end}//?>500)this.width=500'>下载文件 (已下载 2 次)点击这里下载文件 |