Memcache操作详解及分析
一、 linux 编译 memcached 准备编译环境
在 linux 编译,需要 gcc,make,cmake,autoconf,libtool 等工具
#yum install gcc make cmake autoconf libtool
二、 编译 memcached
memcached 依赖于 libevent 库.libevent是个程序库,它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能封装成统一的接口.因此我们需要先安装 libevent.
分别到 libevent.org 和 memcached.org 下载最新的 stable 版本(稳定版).
先编译 libevent ,再编译 memcached,
编译 memcached 时要指定 libevent 的路径.
过程如下: 假设源码在/usr/local/src 下, 安装在/usr/local 下
# tar zxvf libevent-2.0.21-stable.tar.gz
# cd libevent-2.0.21-stable
# ./configure --prefix=/usr/local/libevent
# 如果出错,读报错信息,查看原因,一般是缺少库
# make && make install
# tar zxvf memcached-1.4.5.tag.gz
# cd memcached-1.4.5
#./configure--prefix=/usr/local/memcached \
--with-libevent=/usr/local/libevent
# make && make install
注意: 在虚拟机下练习编译,一个容易碰到的问题---虚拟机的时间不对,
导致的 gcc 编译过程中,检测时间通不过,一直处于编译过程.
解决:
# date -s ‘yyyy-mm-dd hh:mm:ss’
# clock -w # 把时间写入 cmos
三、 memcached 的启动
# /usr/local/memcached/bin/memcached -m 64 -p 11211 -u nobody -vv
slab class 1: chunk size 96 perslab 10922
slab class 2: chunk size 120 perslab 8738
slab class 3: chunk size 152 perslab 6898
slab class 4: chunk size 192 perslab 5461
....
....
slab class 9: chunk size 600 perslab 1747
slab class 10: chunk size 752 perslab 1394
slab class 39: chunk size 493552 perslab 2
slab class 40: chunk size 616944 perslab 1
slab class 41: chunk size 771184 perslab 1
slab class 42: chunk size 1048576 perslab 1
memcached 后台运行,只需要加-d 选项
# /usr/local/memcached/bin/memcached -m 64 -p 11211 -u nobody -d
查看linux端口状态
#netstat –an //观察11211端口
#netstat –anb //程序监听 那些客户连接服务器
memcached -h 查看帮助
-p <num> tcp port number to listen on (default: 11211) // 监听的端口
-u <num> udp port number to listen on (default: 0, off)
-s <file> unix socket path to listen on (disables network support)
-a <mask> access mask for unix socket, in octal (default 0700)
-l <ip_addr> interface to listen on, default is indrr_any
-d start tell memcached to start
-d restart tell running memcached to do a graceful restart
-d stop|shutdown tell running memcached to shutdown
-d install install memcached service // 把 memcached 注册成服务
-d uninstall uninstall memcached service
-r maximize core file limit
-u <username> assume identity of <username> (only when run as root)
-m <num> max memory to use for items in megabytes, default is 64
mb //分配给 memcached 的最大内存
-m return error on memory exhausted (rather than removing
items)
-c <num> max simultaneous connections, default is 1024 // 最大的连接数
-k lock down all paged memory. note that there is a
limit on how much memory you may lock. trying to
allocate more than that would fail, so be sure you
set the limit correctly for the user you started
the daemon with (not for -u <username> user;
under sh this is done with 'ulimit -s -l num_kb').
-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 <file> save pid in <file>, only used with -d option
-f <factor> chunk size growth factor, default 1.25 //增长因子
-n <bytes> minimum space allocated for key+value+flags, default 48
在 windows 下启动 memcached:
下载解压后,不用安装,通过命令行下进入到 memcached.exe 所在的目录
>memcached -m 64 -p 11211 -vvv
四、memcached 连接的几种方式
php无memcache扩展情况下可以使用原生的socket通信操作,memcached-client.php类库附在本文底部
memcached 客户端与服务器端的通信比较简单,使用的基于文本的协议,而不是二进制协议.
# 格式 telnet host port
# telnet localhost 11211
Trying ::1...
Connected to localhost.
Escape character is '^]'.
五、memcached命令
增: add 往内存增加一行新记录
语法: add key flag expire length 回车
key 给值起一个独特的名字
flag 标志,要求为一个正整数
expire 有效期
length 缓存的长度(字节为单位)
flag意义:memcached 基本文本协议,传输的东西,理解成字符串来存储.序列化成字符串,往出取的时候,自然还要反序列化成 对象/数组/json 格式
- 1:字符串,
- 2:反转成数组
- 3:反序列化对象
expire 设置缓存的有效期,有3种格式
- 1:设置秒数, 从设定开始数,第 n 秒后失效.(直接给秒数,则最大可设为 30360024,超过则失效)
- 2:时间戳, 到指定的时间戳后失效.(如果你希望保持时间超过30天 time()+天数360024 即可;
比如在团购网站,缓存的某团到中午 12:00 失效. add key 0 1379209999 6) - 3: 设为 0. 不自动失效.
注: 有种误会,设为 0,永久有效.错误的.
编译 memcached 时,指定一个最长常量,默认是 30 天.即使设为 0,30 天后也会失效.也有可能等不到 30 天,就会被新数据挤出去.
delete 删除
delete key [time seconds]
删除指定的 key. 如加可选参数 time,则指删除 key,并在删除 key 后的 time 秒内,不允许使用get,add,replace 操作此 key.
replace 替换
replace key flag expire length
get 查询
get key
set 是设置和修改值
用add 时,key 不存在,才能建立此键值.
但对于已经存在的键,可以用 replace 进行替换/更改
repalce,key 存在时,才能修改此键值,不存在,则没改成功.
而 set 想当于有 add replace 两者的功能.
set key flag expire leng 时
如果服务器无此键 ----> 增加的效果
如果服务器有此键 ----> 修改的效果.
incr ,decr 命令:增加/减少值的大小
语法: incr/decr key num
注意:incr,decr 操作是把值理解为 32 位无符号来+-操作的. 值在[0-2^32-1]范围内
应用场景------秒杀功能,
一个人下单,要牵涉数据库读取,写入订单,更改库存,及事务要求, 对于传统型数据库来说,压力是巨大的.可以利用 memcached 的 incr/decr 功能, 在内存存储 count 库存量, 秒杀 1000 台每人抢单主要在内存操作,速度非常快,抢到 count<=1000 的号人,得一个订单号,再去另一个页面慢慢支付
统计命令: stats
stat uptime 4237 持续运行时间
stat time 1370054990
stat version 1.2.6
stat pointer_size 32
stat curr_items 4 当前存储的键个数
stat total_items 13
stat bytes 236
stat curr_connections 3
stat total_connections 4
stat connection_structures 4
stat cmd_get 20
stat cmd_set 16
stat get_hits 13 //查询到数据的次数
stat get_misses 7 // 未查询到数据的次数
stat evictions 0 //LRU释放的对象数目 挤掉对象 可以配置此算法不生效
stat bytes_read 764
stat bytes_written 618
stat limit_maxbytes 67108864
stat threads 1
end
memcache01.gif
memcache02.jpg
命中率是指: (查询到数据的次数/查询总数)100%
13/(13+7) = 60% , 的命中率.
** flush_all 清空所有的存储对象*
Memcache管理后台,MemAdmin
官网下载部署到网站根目录使用即可
memcache03.png六、php相关代码
不便修改 php.ini 文件,处理session存入memcached功能, 我们通过函数可以去修改 php.ini 的配置.
<?php
ini_set("session.save_handler","memcache");
ini_set("session.save_path","tcp://127.0.0.1:9999");
memcache分布式相关的php代码
php存值
<?php
//Memcache分布式用法
$mem=new Memcache;
$mem->addServer('127.0.0.1',11211);
$mem->addServer('127.0.0.1',11212);
$mem->addServer('127.0.0.1',11213);
$mem->addServer('127.0.0.1',11214);
//注意,把key放入那个端口的mem我们不用操心,由$mem对象采用哈希一致性原理来维护.
if($mem->set('key1','hello',MEMCACHE_COMPRESSED,300)){
echo 'add1 ok!';
}
if($mem->set('key2','hello2',MEMCACHE_COMPRESSED,300)){
echo 'add2 ok!';
}
if($mem->set('key3','hello3',MEMCACHE_COMPRESSED,300)){
echo 'add3 ok!';
}
php取值
<?php
//Memcache分布式用法 -> 从多个mem中取出你的key
$mem=new Memcache;
$mem->addServer('127.0.0.1',11211);
$mem->addServer('127.0.0.1',11212);
$mem->addServer('127.0.0.1',11213);
$mem->addServer('127.0.0.1',11214);
$val=$mem->get('key1');
echo '程序通过哈希运算取出分布的值:'.$val;
memcached-client.php类库(无需php扩展memcache)
<?php
//
// +---------------------------------------------------------------------------+
// | memcached client, PHP |
// +---------------------------------------------------------------------------+
// | Copyright (c) 2003 Ryan T. Dean <rtdean@cytherianage.net> |
// | All rights reserved. |
// | |
// | Redistribution and use in source and binary forms, with or without |
// | modification, are permitted provided that the following conditions |
// | are met: |
// | |
// | 1. Redistributions of source code must retain the above copyright |
// | notice, this list of conditions and the following disclaimer. |
// | 2. Redistributions in binary form must reproduce the above copyright |
// | notice, this list of conditions and the following disclaimer in the |
// | documentation and/or other materials provided with the distribution. |
// | |
// | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
// | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
// | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
// | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
// | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
// | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
// +---------------------------------------------------------------------------+
// | Author: Ryan T. Dean <rtdean@cytherianage.net> |
// | Heavily influenced by the Perl memcached client by Brad Fitzpatrick. |
// | Permission granted by Brad Fitzpatrick for relicense of ported Perl |
// | client logic under 2-clause BSD license. |
// +---------------------------------------------------------------------------+
//
// $TCAnet$
//
/**
* This is the PHP client for memcached - a distributed memory cache daemon.
* More information is available at http://www.danga.com/memcached/
*
* Usage example:
*
* require_once 'memcached.php';
*
* $mc = new memcached(array(
* 'servers' => array('127.0.0.1:10000',
* array('192.0.0.1:10010', 2),
* '127.0.0.1:10020'),
* 'debug' => false,
* 'compress_threshold' => 10240, //压缩
* 'persistant' => true)); //长链接/短连接模式
*
* $mc->add('key', array('some', 'array'));
* $mc->replace('key', 'some random string');
* $val = $mc->get('key');
*
* @author Ryan T. Dean <rtdean@cytherianage.net>
* @package memcached-client
* @version 0.1.2
*/
// {{{ requirements
// }}}
// {{{ constants
// {{{ flags
/**
* Flag: indicates data is serialized
*/
define("MEMCACHE_SERIALIZED", 1<<0);
/**
* Flag: indicates data is compressed
*/
define("MEMCACHE_COMPRESSED", 1<<1);
// }}}
/**
* Minimum savings to store data compressed
*/
define("COMPRESSION_SAVINGS", 0.20);
// }}}
// {{{ class memcached
/**
* memcached client class implemented using (p)fsockopen()
*
* @author Ryan T. Dean <rtdean@cytherianage.net>
* @package memcached-client
*/
class memcached
{
// {{{ properties
// {{{ public
/**
* Command statistics
*
* @var array
* @access public
*/
var $stats;
// }}}
// {{{ private
/**
* Cached Sockets that are connected
*
* @var array
* @access private
*/
var $_cache_sock;
/**
* Current debug status; 0 - none to 9 - profiling
*
* @var boolean
* @access private
*/
var $_debug;
/**
* Dead hosts, assoc array, 'host'=>'unixtime when ok to check again'
*
* @var array
* @access private
*/
var $_host_dead;
/**
* Is compression available?
*
* @var boolean
* @access private
*/
var $_have_zlib;
/**
* Do we want to use compression?
*
* @var boolean
* @access private
*/
var $_compress_enable;
/**
* At how many bytes should we compress?
*
* @var interger
* @access private
*/
var $_compress_threshold;
/**
* Are we using persistant links?
*
* @var boolean
* @access private
*/
var $_persistant;
/**
* If only using one server; contains ip:port to connect to
*
* @var string
* @access private
*/
var $_single_sock;
/**
* Array containing ip:port or array(ip:port, weight)
*
* @var array
* @access private
*/
var $_servers;
/**
* Our bit buckets
*
* @var array
* @access private
*/
var $_buckets;
/**
* Total # of bit buckets we have
*
* @var interger
* @access private
*/
var $_bucketcount;
/**
* # of total servers we have
*
* @var interger
* @access private
*/
var $_active;
// }}}
// }}}
// {{{ methods
// {{{ public functions
// {{{ memcached()
/**
* Memcache initializer
*
* @param array $args Associative array of settings
*
* @return mixed
* @access public
*/
function memcached ($args)
{
$this->set_servers($args['servers']);
$this->_debug = $args['debug'];
$this->stats = array();
$this->_compress_threshold = $args['compress_threshold'];
$this->_persistant = isset($args['persistant']) ? $args['persistant'] : false;
$this->_compress_enable = true;
$this->_have_zlib = function_exists("gzcompress");
$this->_cache_sock = array();
$this->_host_dead = array();
}
// }}}
// {{{ add()
/**
* Adds a key/value to the memcache server if one isn't already set with
* that key
*
* @param string $key Key to set with data
* @param mixed $val Value to store
* @param interger $exp (optional) Time to expire data at
*
* @return boolean
* @access public
*/
function add ($key, $val, $exp = 0)
{
return $this->_set('add', $key, $val, $exp);
}
// }}}
// {{{ decr()
/**
* Decriment a value stored on the memcache server
*
* @param string $key Key to decriment
* @param interger $amt (optional) Amount to decriment
*
* @return mixed FALSE on failure, value on success
* @access public
*/
function decr ($key, $amt=1)
{
return $this->_incrdecr('decr', $key, $amt);
}
// }}}
// {{{ delete()
/**
* Deletes a key from the server, optionally after $time
*
* @param string $key Key to delete
* @param interger $time (optional) How long to wait before deleting
*
* @return boolean TRUE on success, FALSE on failure
* @access public
*/
function delete ($key, $time = 0)
{
if (!$this->_active)
return false;
$sock = $this->get_sock($key);
if (!is_resource($sock))
return false;
$key = is_array($key) ? $key[1] : $key;
$this->stats['delete']++;
$cmd = "delete $key $time\r\n";
if(!fwrite($sock, $cmd, strlen($cmd)))
{
$this->_dead_sock($sock);
return false;
}
$res = trim(fgets($sock));
if ($this->_debug)
printf("MemCache: delete %s (%s)\n", $key, $res);
if ($res == "DELETED")
return true;
return false;
}
// }}}
// {{{ disconnect_all()
/**
* Disconnects all connected sockets
*
* @access public
*/
function disconnect_all ()
{
foreach ($this->_cache_sock as $sock)
fclose($sock);
$this->_cache_sock = array();
}
// }}}
// {{{ enable_compress()
/**
* Enable / Disable compression
*
* @param boolean $enable TRUE to enable, FALSE to disable
*
* @access public
*/
function enable_compress ($enable)
{
$this->_compress_enable = $enable;
}
// }}}
// {{{ forget_dead_hosts()
/**
* Forget about all of the dead hosts
*
* @access public
*/
function forget_dead_hosts ()
{
$this->_host_dead = array();
}
// }}}
// {{{ get()
/**
* Retrieves the value associated with the key from the memcache server
*
* @param string $key Key to retrieve
*
* @return mixed
* @access public
*/
function get ($key)
{
if (!$this->_active)
return false;
$sock = $this->get_sock($key);
if (!is_resource($sock))
return false;
$this->stats['get']++;
$cmd = "get $key\r\n";
if (!fwrite($sock, $cmd, strlen($cmd)))
{
$this->_dead_sock($sock);
return false;
}
$val = array();
$this->_load_items($sock, $val);
if ($this->_debug)
foreach ($val as $k => $v)
printf("MemCache: sock %s got %s => %s\r\n", $sock, $k, $v);
return $val[$key];
}
// }}}
// {{{ get_multi()
/**
* Get multiple keys from the server(s)
*
* @param array $keys Keys to retrieve
*
* @return array
* @access public
* get('key1') keys=array('key1','key2');
*/
function get_multi ($keys)
{
if (!$this->_active)
return false;
$this->stats['get_multi']++;
foreach ($keys as $key)
{
$sock = $this->get_sock($key);
if (!is_resource($sock)) continue;
$key = is_array($key) ? $key[1] : $key;
if (!isset($sock_keys[$sock]))
{
$sock_keys[$sock] = array();
$socks[] = $sock;
}
$sock_keys[$sock][] = $key;
}
// Send out the requests
foreach ($socks as $sock)
{
$cmd = "get";
foreach ($sock_keys[$sock] as $key)
{
$cmd .= " ". $key;
}
$cmd .= "\r\n";
if (fwrite($sock, $cmd, strlen($cmd)))
{
$gather[] = $sock;
} else
{
$this->_dead_sock($sock);
}
}
// Parse responses
$val = array();
foreach ($gather as $sock)
{
$this->_load_items($sock, $val);
}
if ($this->_debug)
foreach ($val as $k => $v)
printf("MemCache: got %s => %s\r\n", $k, $v);
return $val;
}
// }}}
// {{{ incr()
/**
* Increments $key (optionally) by $amt
*
* @param string $key Key to increment
* @param interger $amt (optional) amount to increment
*
* @return interger New key value?
* @access public
*/
function incr ($key, $amt=1)
{
return $this->_incrdecr('incr', $key, $amt);
}
// }}}
// {{{ replace()
/**
* Overwrites an existing value for key; only works if key is already set
*
* @param string $key Key to set value as
* @param mixed $value Value to store
* @param interger $exp (optional) Experiation time
*
* @return boolean
* @access public
*/
function replace ($key, $value, $exp=0)
{
return $this->_set('replace', $key, $value, $exp);
}
// }}}
// {{{ run_command()
/**
* Passes through $cmd to the memcache server connected by $sock; returns
* output as an array (null array if no output)
*
* NOTE: due to a possible bug in how PHP reads while using fgets(), each
* line may not be terminated by a \r\n. More specifically, my testing
* has shown that, on FreeBSD at least, each line is terminated only
* with a \n. This is with the PHP flag auto_detect_line_endings set
* to falase (the default).
*
* @param resource $sock Socket to send command on
* @param string $cmd Command to run
*
* @return array Output array
* @access public
*/
function run_command ($sock, $cmd)
{
if (!is_resource($sock))
return array();
if (!fwrite($sock, $cmd, strlen($cmd)))
return array();
while (true)
{
$res = fgets($sock);
$ret[] = $res;
if (preg_match('/^END/', $res))
break;
if (strlen($res) == 0)
break;
}
return $ret;
}
// }}}
// {{{ set()
/**
* Unconditionally sets a key to a given value in the memcache. Returns true
* if set successfully.
*
* @param string $key Key to set value as
* @param mixed $value Value to set
* @param interger $exp (optional) Experiation time
*
* @return boolean TRUE on success
* @access public
*/
function set ($key, $value, $exp=0)
{
return $this->_set('set', $key, $value, $exp);
}
// }}}
// {{{ set_compress_threshold()
/**
* Sets the compression threshold
*
* @param interger $thresh Threshold to compress if larger than
*
* @access public
*/
function set_compress_threshold ($thresh)
{
$this->_compress_threshold = $thresh;
}
// }}}
// {{{ set_debug()
/**
* Sets the debug flag
*
* @param boolean $dbg TRUE for debugging, FALSE otherwise
*
* @access public
*
* @see memcahced::memcached
*/
function set_debug ($dbg)
{
$this->_debug = $dbg;
}
// }}}
// {{{ set_servers()
/**
* Sets the server list to distribute key gets and puts between
*
* @param array $list Array of servers to connect to
*
* @access public
*
* @see memcached::memcached()
*/
function set_servers ($list)
{
$this->_servers = $list;
$this->_active = count($list);
$this->_buckets = null;
$this->_bucketcount = 0;
$this->_single_sock = null;
if ($this->_active == 1)
$this->_single_sock = $this->_servers[0];
}
// }}}
// }}}
// {{{ private methods
// {{{ _close_sock()
/**
* Close the specified socket
*
* @param string $sock Socket to close
*
* @access private
*/
function _close_sock ($sock)
{
$host = array_search($sock, $this->_cache_sock);
fclose($this->_cache_sock[$host]);
unset($this->_cache_sock[$host]);
}
// }}}
// {{{ _connect_sock()
/**
* Connects $sock to $host, timing out after $timeout
*
* @param interger $sock Socket to connect
* @param string $host Host:IP to connect to
* @param float $timeout (optional) Timeout value, defaults to 0.25s
*
* @return boolean
* @access private
*/
function _connect_sock (&$sock, $host, $timeout = 0.25)
{
list ($ip, $port) = explode(":", $host);
if ($this->_persistant == 1)
{
$sock = @pfsockopen($ip, $port, $errno, $errstr, $timeout);
} else
{
$sock = @fsockopen($ip, $port, $errno, $errstr, $timeout);
}
if (!$sock)
return false;
return true;
}
// }}}
// {{{ _dead_sock()
/**
* Marks a host as dead until 30-40 seconds in the future
*
* @param string $sock Socket to mark as dead
*
* @access private
*/
function _dead_sock ($sock)
{
$host = array_search($sock, $this->_cache_sock);
list ($ip, $port) = explode(":", $host);
$this->_host_dead[$ip] = time() + 30 + intval(rand(0, 10));
$this->_host_dead[$host] = $this->_host_dead[$ip];
unset($this->_cache_sock[$host]);
}
// }}}
// {{{ get_sock()
/**
* get_sock
*
* @param string $key Key to retrieve value for;
*
* @return mixed resource on success, false on failure
* @access private
*/
function get_sock ($key)
{
if (!$this->_active)
return false;
if ($this->_single_sock !== null)
return $this->sock_to_host($this->_single_sock);
$hv = is_array($key) ? intval($key[0]) : $this->_hashfunc($key);
if ($this->_buckets === null)
{
foreach ($this->_servers as $v)
{
if (is_array($v))
{
for ($i=0; $i<$v[1]; $i++)
$bu[] = $v[0];
} else
{
$bu[] = $v;
}
}
$this->_buckets = $bu;
$this->_bucketcount = count($bu);
}
$realkey = is_array($key) ? $key[1] : $key;
for ($tries = 0; $tries<20; $tries++)
{
$host = $this->_buckets[$hv % $this->_bucketcount];
$sock = $this->sock_to_host($host);
if (is_resource($sock))
return $sock;
$hv += $this->_hashfunc($tries . $realkey);
}
return false;
}
// }}}
// {{{ _hashfunc()
/**
* Creates a hash interger based on the $key
*
* @param string $key Key to hash
*
* @return interger Hash value
* @access private
*/
function _hashfunc ($key)
{
$hash = 0;
for ($i=0; $i<strlen($key); $i++)
{
$hash = $hash*33 + ord($key[$i]);
}
return $hash;
}
// }}}
// {{{ _incrdecr()
/**
* Perform increment/decriment on $key
*
* @param string $cmd Command to perform
* @param string $key Key to perform it on
* @param interger $amt Amount to adjust
*
* @return interger New value of $key
* @access private
*/
function _incrdecr ($cmd, $key, $amt=1)
{
if (!$this->_active)
return null;
$sock = $this->get_sock($key);
if (!is_resource($sock))
return null;
$key = is_array($key) ? $key[1] : $key;
$this->stats[$cmd]++;
if (!fwrite($sock, "$cmd $key $amt\r\n"))
return $this->_dead_sock($sock);
stream_set_timeout($sock, 1, 0);
$line = fgets($sock);
if (!preg_match('/^(\d+)/', $line, $match))
return null;
return $match[1];
}
// }}}
// {{{ _load_items()
/**
* Load items into $ret from $sock
*
* @param resource $sock Socket to read from
* @param array $ret Returned values
*
* @access private
*/
function _load_items ($sock, &$ret)
{
while (1)
{
$decl = fgets($sock);
if ($decl == "END\r\n")
{
return true;
} elseif (preg_match('/^VALUE (\S+) (\d+) (\d+)\r\n$/', $decl, $match))
{
list($rkey, $flags, $len) = array($match[1], $match[2], $match[3]);
$bneed = $len+2;
$offset = 0;
while ($bneed > 0)
{
$data = fread($sock, $bneed);
$n = strlen($data);
if ($n == 0)
break;
$offset += $n;
$bneed -= $n;
$ret[$rkey] .= $data;
}
if ($offset != $len+2)
{
// Something is borked!
if ($this->_debug)
printf("Something is borked! key %s expecting %d got %d length\n", $rkey, $len+2, $offset);
unset($ret[$rkey]);
$this->_close_sock($sock);
return false;
}
$ret[$rkey] = rtrim($ret[$rkey]);
if ($this->_have_zlib && $flags & MEMCACHE_COMPRESSED)
$ret[$rkey] = gzuncompress($ret[$rkey]);
if ($flags & MEMCACHE_SERIALIZED)
$ret[$rkey] = unserialize($ret[$rkey]);
} else
{
if ($this->_debug)
print("Error parsing memcached response\n");
return 0;
}
}
}
// }}}
// {{{ _set()
/**
* Performs the requested storage operation to the memcache server
*
* @param string $cmd Command to perform
* @param string $key Key to act on
* @param mixed $val What we need to store
* @param interger $exp When it should expire
*
* @return boolean
* @access private
*/
function _set ($cmd, $key, $val, $exp)
{
if (!$this->_active)
return false;
//get_sock就是去获取到memcached服务器连接
$sock = $this->get_sock($key);
if (!is_resource($sock))
return false;
$this->stats[$cmd]++;
$flags = 0;
if (!is_scalar($val))
{
$val = serialize($val);
$flags |= MEMCACHE_SERIALIZED;
if ($this->_debug)
printf("client: serializing data as it is not scalar\n");
}
$len = strlen($val);
if ($this->_have_zlib && $this->_compress_enable &&
$this->_compress_threshold && $len >= $this->_compress_threshold)
{
$c_val = gzcompress($val, 9);
$c_len = strlen($c_val);
if ($c_len < $len*(1 - COMPRESS_SAVINGS))
{
if ($this->_debug)
printf("client: compressing data; was %d bytes is now %d bytes\n", $len, $c_len);
$val = $c_val;
$len = $c_len;
$flags |= MEMCACHE_COMPRESSED;
}
}
if (!fwrite($sock, "$cmd $key $flags $exp $len\r\n$val\r\n"))
return $this->_dead_sock($sock);
$line = trim(fgets($sock));
if ($this->_debug)
{
if ($flags & MEMCACHE_COMPRESSED)
$val = 'compressed data';
printf("MemCache: %s %s => %s (%s)\n", $cmd, $key, $val, $line);
}
if ($line == "STORED")
return true;
return false;
}
// }}}
// {{{ sock_to_host()
/**
* Returns the socket for the host
*
* @param string $host Host:IP to get socket for
*
* @return mixed IO Stream or false
* @access private
*/
function sock_to_host ($host)
{
if (isset($this->_cache_sock[$host]))
return $this->_cache_sock[$host];
$now = time();
list ($ip, $port) = explode (":", $host);
if (isset($this->_host_dead[$host]) && $this->_host_dead[$host] > $now ||
isset($this->_host_dead[$ip]) && $this->_host_dead[$ip] > $now)
return null;
if (!$this->_connect_sock($sock, $host))
return $this->_dead_sock($host);
// Do not buffer writes
stream_set_write_buffer($sock, 0);
$this->_cache_sock[$host] = $sock;
return $this->_cache_sock[$host];
}
// }}}
// }}}
// }}}
}
// }}}
?>