安装配置
首先,编译、安装、配置libevent库,执行如下命令:
1 |
wget https://github.com/downloads/libevent/libevent/libevent-1.4.14b-stable. tar .gz |
2 |
tar xvzf libevent-1.4.14b-stable. tar .gz |
3 |
ln -s /usr/ local /libevent-1.4.14b-stable /usr/ local /libevent |
4 |
cd /usr/ local /libevent |
5 |
./configure |
6 |
make |
7 |
make install |
然后,编译、安装、配置Memcached,执行如下命令行:
1 |
wget http://www.memcached.org/files/memcached-1.4.20. tar .gz |
2 |
tar xvzf memcached-1.4.20. tar .gz |
3 |
ln -s /usr/ local /memcached-1.4.20 /usr/ local /memcached |
4 |
./configure --with-libevent=/usr/ local /libevent/ |
5 |
make |
6 |
make install |
如果没有出错,安装成功。
管理memcached服务
- 启动Memcached
一般情况下,简单地可以使用类似如下形式,启动Memcached服务:
1 |
/usr/local/bin/memcached -d -m 64 -I 20m -u root -l 192.168.4.86 -p 11211 -c 1024 -P /usr/local/memcached/memcached.pid |
上述命令行中,基于上面各个选项,以及其他一些选项的含义,说明如下表所示:
选项 | 含义说明 |
-d | 指定memcached进程作为一个守护进程启动 |
-m <num> | 指定分配给memcached使用的内存,单位是MB |
-u <username> | 运行memcached的用户 |
-l <ip_addr> | 监听的服务器IP地址,如果有多个地址的话,使用逗号分隔,格式可以为“IP地址:端口号”,例如:-l 指定192.168.0.184:19830,192.168.0.195:13542;端口号也可以通过-p选项指定 |
-p <num> | Memcached监听的端口,要保证该端口号未被占用 |
-c <num> | 设置最大运行的并发连接数,默认是1024 |
-R <num> | 为避免客户端饿死(starvation),对连续达到的客户端请求数设置一个限额,如果超过该设置,会选择另一个连接来处理请求,默认为20 |
-k | 设置锁定所有分页的内存,对于大缓存应用场景,谨慎使用该选项 |
-P | 保存memcached进程的pid文件 |
-s <file> | 指定Memcached用于监听的UNIX socket文件 |
-a <perms> | 设置-s选项指定的UNIX socket文件的权限 |
-U <num> | 指定监听UDP的端口,默认11211,0表示关闭 |
-M | 当内存使用超出配置值时,禁止自动清除缓存中的数据项,此时Memcached不可以,直到内存被释放 |
-r | 设置产生core文件大小 |
-f <factor> | 用于计算缓存数据项的内存块大小的乘数因子,默认是1.25 |
-n | 为缓存数据项的key、value、flag设置最小分配字节数,默认是48 |
-C | 禁用CAS |
-h | 显示Memcached版本和摘要信息 |
-v | 输出警告和错误信息 |
-vv | 打印信息比-v更详细:不仅输出警告和错误信息,也输出客户端请求和响应信息 |
-i | 打印libevent和Memcached的licenses信息 |
-t <threads> | 指定用来处理请求的线程数,默认为4 |
-D <char> | 用于统计报告中Key前缀和ID之间的分隔符,默认是冒号“:” |
-L | 尝试使用大内存分页(pages) |
-B <proto> | 指定使用的协议,默认行为是自动协商(autonegotiate),可能使用的选项有auto、ascii、binary。 |
-I <size> | 覆盖默认的STAB页大小,默认是1M |
-F | 禁用flush_all命令 |
-o <options> | 指定逗号分隔的选项,一般用于用于扩展或实验性质的选项 |
- 停止Memcached
可以通过Linux的如下命令查询到Memcached的进程号:
1 |
ps -ef | grep memcached |
然后杀掉Memcached服务进程:
1 |
kill -9 <PID> |
-9表示强制杀掉进程。
Memcached启动以后,可以通过客户端来操作缓存中的数据,我们说明一些常用的客户端,及其使用方法。
Telnet客户端
Telnet客户端可以通过命令行的方式来监控查看Memcached服务器存储数据的情况。例如,Memcached的服务地址为192.168.4.86:11211,可以telnet到该服务端口:
1 |
telnet 192.168.4.86 11211 |
如果连接成功,可以使用如下一些命令:
- stats命令
该命令用于显示服务器信息、统计数据等,结果示例数据(来自www.2cto.com网站),例如:
01 |
STAT pid 22362 //memcache服务器的进程ID www.2cto.com |
02 |
STAT uptime 1469315 //服务器已经运行的秒数 |
03 |
STAT time 1339671194 //服务器当前的unix时间戳 |
04 |
STAT version 1.4.9 //memcache版本 |
05 |
STAT libevent 1.4.9-stable //libevent版本 |
06 |
STAT pointer_size 64 //当前操作系统的指针大小(32位系统一般是32bit,64就是64位操作系统) |
07 |
STAT rusage_user 3695.485200 //进程的累计用户时间 |
08 |
STAT rusage_system 14751.273465 //进程的累计系统时间 |
09 |
STAT curr_connections 69 //服务器当前存储的items数量 |
10 |
STAT total_connections 855430 //从服务器启动以后存储的items总数量 |
11 |
STAT connection_structures 74 //服务器分配的连接构造数 |
12 |
STAT reserved_fds 20 // |
13 |
STAT cmd_get 328806688 //get命令(获取)总请求次数 |
14 |
STAT cmd_set 75441133 //set命令(保存)总请求次数 www.2cto.com |
15 |
STAT cmd_flush 34 //flush命令请求次数 |
16 |
STAT cmd_touch 0 //touch命令请求次数 |
17 |
STAT get_hits 253547177 //总命中次数 |
18 |
STAT get_misses 75259511 //总未命中次数 |
19 |
STAT delete_misses 4 //delete命令未命中次数 |
20 |
STAT delete_hits 565730 //delete命令命中次数 |
21 |
STAT incr_misses 0 //incr命令未命中次数 |
22 |
STAT incr_hits 0 //incr命令命中次数 |
23 |
STAT decr_misses 0 //decr命令未命中次数 |
24 |
STAT decr_hits 0 //decr命令命中次数 |
25 |
STAT cas_misses 0 //cas命令未命中次数 |
26 |
STAT cas_hits 0 //cas命令命中次数 |
27 |
STAT cas_badval 0 //使用擦拭次数 |
28 |
STAT touch_hits 0 //touch命令未命中次数 |
29 |
STAT touch_misses 0 //touch命令命中次数 |
30 |
STAT auth_cmds 0 //认证命令处理的次数 |
31 |
STAT auth_errors 0 //认证失败数目 |
32 |
STAT bytes_read 545701515844 //总读取字节数(请求字节数) |
33 |
STAT bytes_written 1649639749866 //总发送字节数(结果字节数) |
34 |
STAT limit_maxbytes 2147483648 //分配给memcache的内存大小(字节) |
35 |
STAT accepting_conns 1 //服务器是否达到过最大连接(0/1) |
36 |
STAT listen_disabled_num 0 //失效的监听数 |
37 |
STAT threads 4 //当前线程数 |
38 |
STAT conn_yields 14 //连接操作主动放弃数目 |
39 |
STAT hash_power_level 16 // |
40 |
STAT hash_bytes 524288 |
41 |
STAT hash_is_expanding 0 |
42 |
STAT expired_unfetched 30705763 |
43 |
STAT evicted_unfetched 0 |
44 |
STAT bytes 61380700 //当前存储占用的字节数 |
45 |
STAT curr_items 28786 //当前存储的数据总数 |
46 |
STAT total_items 75441133 //启动以来存储的数据总数 |
47 |
STAT evictions 0 //为获取空闲内存而删除的items数(分配给memcache的空间用满后需要删除旧的items来得到空间分配给新的items) |
48 |
STAT reclaimed 39957976 //已过期的数据条目来存储新数据的数目 |
49 |
END |
上面给出了各个统计项的含义说明,不再累述。
stats命令有几个二级子项,说明如下表所示:
命令 | 含义说明 |
stats slabs | 显示各个slab的信息,包括chunk的大小、数目、使用情况等 |
stats items | 显示各个slab中item的数目和最老item的年龄(最后一次访问距离现在的秒数) |
stats detail [on|off|dump] | 设置或者显示详细操作记录; 参数为on,打开详细操作记录; 参数为off,关闭详细操作记录; 参数为dump,显示详细操作记录(每一个键值get、set、hit、del的次数) |
stats malloc | 打印内存分配信息 |
stats sizes | 打印缓存使用信息 |
stats reset | 重置统计信息 |
下面的命令,我们通过表格的形式说明,如下表所示:
命令 | 用法格式 | 含义说明 | 示例 |
get | get <key>*\r\n | 用于获取缓存的数据,键为key。 | get name VALUE name 0 7 shirdrn END |
gets | gets <key>*\r\n | 用于获取缓存的数据,键为一组key。 | gets name hobby VALUE name 1 7 1234567 VALUE hobby 0 25 tenis basketball football END |
set | set <key> <flags> <exptime> <bytes> [noreply]\r\n<value>\r\n | 向缓存中存储数据,不管key对应的值存在与否,都设置key对应的值。 | set name 0 1800 7 shirdrn STORED get name VALUE name 0 7 shirdrn END |
touch | touch <key> <exptime> [noreply]\r\n | 更新缓存中key对应的值的过期时间。 | touch name 1800 |
delete | delete <key> [<time>] [noreply]\r\n | 给定键key,删除缓存中key对应的数据。 | delete name 60 |
add | add <key> <flags> <exptime> <bytes> [noreply]\r\n<value>\r\n | 向缓存中存储数据,只有key对应的值不存在时,才会设置key对应的值。 | add hobby 0 1800 10 basketball STORED get hobby VALUE hobby 0 10 |
replace | replace <key> <flags> <exptime> <bytes> [noreply]\r\n<value>\r\n | 覆盖一个已经存在Key及其对应的Value,替换一定要保证替换后的值的长度原始长度相同,否则replace失败。 | get name VALUE name 0 7 shirdrn END replace name 0 1800 7 youak47 STORED get name VALUE name 0 7 youak47 END |
append | append <key> <flags> <exptime> <bytes> [noreply]\r\n<value>\r\n | 在一个已经存在的数据值(value)上追加,是在数据值的后面追加。 | get hobby VALUE hobby 0 10 basketball END append hobby 0 1800 9 football STORED get hobby VALUE hobby 0 19 basketball football END |
prepend | prepend <key> <flags> <exptime> <bytes> [noreply]\r\n<value>\r\n | 在一个已经存在的数据值(value)上追加,是在数据值的前面追加。 | get hobby VALUE hobby 0 19 basketball football END prepend hobby 0 1800 6 tenis STORED get hobby VALUE hobby 0 25 tenis basketball football END |
incr | incr <key> <value> [noreply]\r\n | 计数命令,可以在原来已经存在的数字上进行累加求和,计算并存储新的数值。 | set active_users 0 1000000 7 1000000 STORED get active_users VALUE active_users 0 7 1000000 END incr active_users 99 1000099 |
decr | decr <key> <value> [noreply]\r\n | 计数命令,可以在原来已经存在的数字上进行减法计算,计算并存储新的数值。 | get active_users VALUE active_users 0 7 1000099 END decr active_users 3456 996643 |
flush_all | flush_all [<time>] [noreply]\r\n | 使缓存中的数据项失效,可选参数是在多少秒后失效。 | flush_all 1800 |
version | version\r\n | 返回Memcached服务器的版本信息。 | version |
quit | quit\r\n | 退出telnet终端。 | quit |
Java客户端
可以使用Java语言编写代码来访问Memcached缓存。目前,可以使用的Java客户端很多,这里简单介绍几个。
- spymemcached客户端
示例代码,如下所示:
01 |
package org.shirdrn.spymemcached; |
02 |
03 |
import net.spy.memcached.AddrUtil; |
04 |
import net.spy.memcached.BinaryConnectionFactory; |
05 |
import net.spy.memcached.MemcachedClient; |
06 |
import net.spy.memcached.internal.OperationFuture; |
07 |
08 |
public class TestSpymemcached { |
09 |
10 |
public static void main(String[] args) throws Exception { |
11 |
String address = "192.168.4.86:11211" ; |
12 |
MemcachedClient client = new MemcachedClient( new BinaryConnectionFactory(), |
13 |
AddrUtil.getAddresses(address)); |
14 |
|
15 |
String key = "magic_words" ; |
16 |
int exp = 3600 ; |
17 |
String o = "hello" ; |
18 |
// set |
19 |
OperationFuture<Boolean> setFuture = client.set(key, exp, o); |
20 |
if (setFuture.get()) { |
21 |
// get |
22 |
System.out.println(client.get(key)); |
23 |
|
24 |
// append |
25 |
client.append(key, " the world!" ); |
26 |
System.out.println(client.get(key)); |
27 |
|
28 |
// prepend |
29 |
client.prepend(key, "Stone, " ); |
30 |
System.out.println(client.get(key)); |
31 |
|
32 |
// replace |
33 |
o = "This is a test for spymemcached." ; |
34 |
OperationFuture<Boolean> replaceFuture = client.replace(key, exp, o); |
35 |
if (replaceFuture.get()) { |
36 |
System.out.println(client.get(key)); |
37 |
|
38 |
// delete |
39 |
client.delete(key); |
40 |
System.out.println(client.get(key)); |
41 |
} |
42 |
} |
43 |
|
44 |
client.shutdown(); |
45 |
} |
46 |
47 |
} |
更多用法,可以参考后面的链接。
- XMemcached客户端
示例代码,如下所示:
001 |
package org.shirdrn.xmemcached; |
002 |
003 |
import java.io.File; |
004 |
import java.io.IOException; |
005 |
import java.io.Serializable; |
006 |
import java.net.InetSocketAddress; |
007 |
import java.util.Arrays; |
008 |
import java.util.List; |
009 |
import java.util.Map; |
010 |
import java.util.concurrent.ExecutorService; |
011 |
import java.util.concurrent.Executors; |
012 |
import java.util.concurrent.TimeoutException; |
013 |
import java.util.concurrent.atomic.AtomicLong; |
014 |
015 |
import net.rubyeye.xmemcached.CASOperation; |
016 |
import net.rubyeye.xmemcached.GetsResponse; |
017 |
import net.rubyeye.xmemcached.MemcachedClient; |
018 |
import net.rubyeye.xmemcached.XMemcachedClientBuilder; |
019 |
import net.rubyeye.xmemcached.command.BinaryCommandFactory; |
020 |
import net.rubyeye.xmemcached.exception.MemcachedException; |
021 |
import net.rubyeye.xmemcached.utils.AddrUtil; |
022 |
023 |
public class UsingXMemcachedClient { |
024 |
025 |
public static void main(String[] args) throws IOException { |
026 |
String servers = "192.168.4.86:11211" ; |
027 |
// build and create a client |
028 |
XMemcachedClientBuilder builder = new XMemcachedClientBuilder( |
029 |
AddrUtil.getAddresses(servers)); |
030 |
builder.setCommandFactory( new BinaryCommandFactory()); |
031 |
final MemcachedClient client = builder.build(); |
032 |
|
033 |
// examples using client to operate |
034 |
final String key = "ghost" ; |
035 |
try { |
036 |
// add |
037 |
client.add(key, 0 , "Ghost wind blows!" ); |
038 |
System.out.println( "add & get: " + client.get(key)); |
039 |
|
040 |
// append |
041 |
client.append(key, " It‘s a lie." ); |
042 |
System.out.println( "append & get: " + client.get(key)); |
043 |
|
044 |
// prepend |
045 |
client.prepend(key, "Who‘s said?! " ); |
046 |
System.out.println( "prepend & get: " + client.get(key)); |
047 |
|
048 |
// replace |
049 |
client.replace(key, 0 , "Everything is nothing!" ); |
050 |
System.out.println( "replace & get: " + client.get(key)); |
051 |
|
052 |
// delete |
053 |
client.delete(key); |
054 |
System.out.println( "delete & get: " + client.get(key)); |
055 |
|
056 |
// gets |
057 |
List<String> keys = Arrays.asList( new String[] { |
058 |
"key1" , "key2" , "key3" |
059 |
}); |
060 |
for (String k : keys) { |
061 |
client.set(k, 3600 , "v:" + System.nanoTime()); |
062 |
} |
063 |
Map<String, GetsResponse<Object>> values = client.gets(keys); |
064 |
for (Map.Entry<String, GetsResponse<Object>> entry : values.entrySet()) { |
065 |
System.out.println( "key=" + entry.getKey() + ", value=" + entry.getValue().getValue()); |
066 |
} |
067 |
|
068 |
// cas |
069 |
final AtomicLong seq = new AtomicLong(System.nanoTime()); |
070 |
ExecutorService pool = Executors.newCachedThreadPool(); |
071 |
for ( int i= 0 ; i< 10 ; i++) { |
072 |
pool.execute( new Runnable() { |
073 |
@Override |
074 |
public void run() { |
075 |
while ( true ) { |
076 |
CacheResult o = new CacheResult(); |
077 |
o.file = new File( "/opt/status/servers.lst" ); |
078 |
o.lastmodified = seq.incrementAndGet(); |
079 |
System.out.println( "#" + Thread.currentThread().getId() + "=>o: " + o); |
080 |
try { |
081 |
client.set(key, 0 , o); |
082 |
Thread.sleep( 100 ); |
083 |
} catch (TimeoutException e) { |
084 |
// TODO Auto-generated catch block |
085 |
e.printStackTrace(); |
086 |
} catch (InterruptedException e) { |
087 |
// TODO Auto-generated catch block |
088 |
e.printStackTrace(); |
089 |
} catch (MemcachedException e) { |
090 |
// TODO Auto-generated catch block |
091 |
e.printStackTrace(); |
092 |
} |
093 |
} |
094 |
} |
095 |
}); |
096 |
} |
097 |
Thread.sleep( 3000 ); |
098 |
for ( int i= 0 ; i< 10 ; i++) { |
099 |
client.cas(key, new CASOperation<CacheResult>() { |
100 |
@Override |
101 |
public int getMaxTries() { |
102 |
return 3 ; |
103 |
} |
104 |
@Override |
105 |
public CacheResult getNewValue( long arg0, CacheResult result) { |
106 |
CacheResult old = result; |
107 |
CacheResult nu = new CacheResult(); |
108 |
nu.file = old.file; |
109 |
nu.lastmodified = seq.incrementAndGet(); |
110 |
System.out.println( "cas: old=" + old + ", new=" + nu); |
111 |
return result; |
112 |
} |
113 |
}); |
114 |
} |
115 |
pool.shutdown(); |
116 |
|
117 |
// flush_all |
118 |
client.flushAll(); |
119 |
|
120 |
// stats |
121 |
List<InetSocketAddress> addresses = AddrUtil.getAddresses(servers); |
122 |
for (InetSocketAddress addr : addresses) { |
123 |
Map<String, String> stats = client.stats(addr); |
124 |
System.out.println(stats); |
125 |
} |
126 |
|
127 |
} catch (TimeoutException e) { |
128 |
e.printStackTrace(); |
129 |
} catch (InterruptedException e) { |
130 |
e.printStackTrace(); |
131 |
} catch (MemcachedException e) { |
132 |
e.printStackTrace(); |
133 |
} |
134 |
|
135 |
synchronized (client) { |
136 |
try { |
137 |
client.wait(); |
138 |
} catch (InterruptedException e) { |
139 |
e.printStackTrace(); |
140 |
} |
141 |
} |
142 |
143 |
} |
144 |
|
145 |
static class CacheResult implements Serializable { |
146 |
private static final long serialVersionUID = 3349686173080590047L; |
147 |
private File file; |
148 |
private long lastmodified; |
149 |
@Override |
150 |
public String toString() { |
151 |
return "file=[" + file + ", lastmodified=" + lastmodified + "]" ; |
152 |
} |
153 |
} |
154 |
155 |
} |
Node.js客户端
Memcached客户端代码的逻辑都非常类似,这里对Node.js简单举例说明,代码如下所示:
01 |
#!/usr/bin/env node |
02 |
03 |
var MemcachedClient = require( ‘memcached‘ ); |
04 |
05 |
// configure memcached client |
06 |
var servers = [ ‘192.168.4.86:11211‘ ]; |
07 |
var client = new MemcachedClient(servers); |
08 |
09 |
// access memcached servers |
10 |
var key = ‘ghost‘ ; |
11 |
12 |
// set |
13 |
var value = ‘Ghost wind blows!‘ ; |
14 |
client.set(key, 0, value, function (err) { |
15 |
var data = ‘key=‘ + key + ‘, value=‘ + value; |
16 |
if (err) { |
17 |
console.error( ‘Fail to set: ‘ + data); |
18 |
} else { |
19 |
console.log( ‘Added: ‘ + data); |
20 |
} |
21 |
}); |
22 |
23 |
// get |
24 |
var valueGot = client.get(key, function (err, data) { |
25 |
var dataGot = ‘key=‘ + key + ‘, valueGot=‘ + data; |
26 |
if (err) { |
27 |
console.error( ‘Fail to get: ‘ + dataGot); |
28 |
} else { |
29 |
console.log( ‘Got: ‘ + dataGot); |
30 |
} |
31 |
}); |
参考链接
- http://www.memcached.org
- https://code.google.com/p/memcached/wiki/NewStart
- http://www.memcached.org/files/memcached-1.4.20.tar.gz
- https://code.google.com/p/memcached/wiki/Clients
- https://github.com/downloads/libevent/
- https://github.com/downloads/libevent/libevent/libevent-1.4.14b-stable.tar.gz
- https://code.google.com/p/spymemcached/
- https://code.google.com/p/xmemcached/
- https://github.com/killme2008/xmemcached
- https://code.google.com/p/memcached/wiki/NewCommands
- http://www.2cto.com/os/201303/193264.html
- http://tech.idv2.com/tag/memcached/
- http://programcreek.com/java-api-examples/index.php?api=net.spy.memcached.MemcachedClient
- http://lzone.de/articles/memcached.htm
- http://blog.elijaa.org/?post/2010/05/21/Memcached-telnet-command-summary
- https://github.com/3rd-Eden/node-memcached
- https://www.npmjs.org/package/memcached
来源: <http://shiyanjun.cn/archives/873.html>
时间: 2024-11-06 12:36:28