1.Memcached Client简要介绍
Memcached Client目前有3种:
1.Memcached Client for Java 2.SpyMemcached 3.XMemcached
这三种Client一直存在各种争议:
Memcached Client for Java 比 SpyMemcached更稳定、更早、更广泛;SpyMemcached 比 Memcached Client for Java更高效;XMemcached 比 SpyMemcache并发效果更好。
具体可以参考官方性能对比:
Memcached Client for Java: https://github.com/gwhalin/Memcached-Java-Client/wiki/PERFORMANCE
XMemcached: http://xmemcached.googlecode.com/svn/trunk/benchmark/benchmark.html
2.XMemcached特性
高性能;支持完整的memcached文本协议,二进制协议;支持JMX,可以通过MBean调整性能参数、动态添加/移除server、查看统计等;支持客户端统计;支持memcached节点的动态增减;支持memcached分布:余数分布和一致性哈希分布;更多的性能调整选项。
3.XMemcached简单实现
MemcachedClientBuilder是MemcachedClient核心接口,用来控制Client的构建(build()方法)和关闭(shutdown()方法)。 通过build()方法获得 MemcachedClient
然后就可以通过Memcached进行set、get、replace、delete等Memcached操作了!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
import
static
junit.framework.Assert.*;
import
java.io.IOException;
import
java.util.concurrent.TimeoutException;
import
net.rubyeye.xmemcached.MemcachedClient;
import
net.rubyeye.xmemcached.MemcachedClientBuilder;
import
net.rubyeye.xmemcached.XMemcachedClientBuilder;
import
net.rubyeye.xmemcached.command.BinaryCommandFactory;
import
net.rubyeye.xmemcached.exception.MemcachedException;
import
net.rubyeye.xmemcached.utils.AddrUtil;
import
org.junit.Test;
public
class
MemcachedClientTest {
@Test
public
void
test() {
MemcachedClientBuilder builder =
new
XMemcachedClientBuilder(
//多个服务和权重
AddrUtil.getAddresses(
"192.168.1.110:11211 192.168.1.111:11211"
),
new
int
[] {
1
,
1
});
// 设置连接池大小,即客户端个数
builder.setConnectionPoolSize(
50
);
// 宕机报警
builder.setFailureMode(
true
);
// 使用二进制文件
builder.setCommandFactory(
new
BinaryCommandFactory());
// 使用一致性哈希算法(Consistent Hash Strategy)
builder.setSessionLocator(
new
KetamaMemcachedSessionLocator());
// 使用序列化传输编码
builder.setTranscoder(
new
SerializingTranscoder());
// 进行数据压缩,大于1KB时进行压缩
builder.getTranscoder().setCompressionThreshold(
1024
);
MemcachedClient memcachedClient =
null
;
try
{
memcachedClient = builder.build();
try
{
// 设置/获取
memcachedClient.set(
"aa"
,
36000
,
"set/get"
);
assertEquals(
"set/get"
, memcachedClient.get(
"aa"
));
// 替换
memcachedClient.replace(
"aa"
,
36000
,
"replace"
);
assertEquals(
"replace"
, memcachedClient.get(
"aa"
));
// 移除
memcachedClient.delete(
"aa"
);
assertNull(memcachedClient.get(
"aa"
));
}
catch
(TimeoutException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch
(InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
catch
(MemcachedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
catch
(IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally
{
if
(memcachedClient !=
null
) {
try
{
memcachedClient.shutdown();
}
catch
(IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
|
4.XMemcached与Spring集成 更新数据后原来对应的类模块键过期策略,对缓存键存在数据库中策略的代替
spring-xmemcached.xml注释的部分是可以配置多个memcached服务器的
<!-- 引入Memcached客户端开始--> <bean name="memcachedClientBuilder" class="net.rubyeye.xmemcached.XMemcachedClientBuilder"> <constructor-arg> <list> <bean class="java.net.InetSocketAddress"> <constructor-arg> <value>192.168.100.102</value> </constructor-arg> <constructor-arg> <value>11211</value> </constructor-arg> </bean> <!-- <bean class="java.net.InetSocketAddress"> <constructor-arg> <value>localhost</value> </constructor-arg> <constructor-arg> <value>12001</value> </constructor-arg> </bean> --> </list> </constructor-arg> <constructor-arg> <list> <value>1</value> <!-- <value>2</value> --> </list> </constructor-arg> <property name="connectionPoolSize" value="2"></property> <property name="commandFactory"> <bean class="net.rubyeye.xmemcached.command.TextCommandFactory"></bean> </property> <property name="sessionLocator"> <bean class="net.rubyeye.xmemcached.impl.KetamaMemcachedSessionLocator"></bean> </property> <property name="transcoder"> <bean class="net.rubyeye.xmemcached.transcoders.SerializingTranscoder" /> </property> </bean> <bean name="memcachedClient" factory-bean="memcachedClientBuilder" factory-method="build" destroy-method="shutdown" /> <!-- 引入Memcached客户端结束 -->
<!-- get cache拦截器 --> <bean id="methodCacheInterceptor" class="com.jing.memcached.interceptor.XMemcachedMethodCacheInterceptor"> <property name="memcachedClient"> <ref local="memcachedClient" /> </property> </bean> <bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <property name="advice"> <ref local="methodCacheInterceptor" /> </property> <property name="patterns"> <list> <value>com.jing.*.getAllUser</value> <value>com.jing.*.update.*</value> <value>com.jing.*.add.*</value> </list> </property> </bean> <!--一个ProxyFactoryBean类只能指定一个代理目标。BeanNameAutoProxyCreator类自动代理,可以代理多个目标 --> <bean id="myService" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="*ServiceImpl"/> <!--配置文件中可以使用@Autowired注解实例化的类 --> <property name="interceptorNames"> <list> <value>methodCachePointCut</value> </list> </property> </bean>
拦截器配置:
package com.jing.memcached.interceptor;
import java.lang.reflect.Method;
import net.rubyeye.xmemcached.MemcachedClient;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
import com.jing.memcached.annotation.CacheAdd;
import com.jing.memcached.annotation.CacheRemove;
/**
* @author 静国强
* @date 2015-9-11
* @desc 配置MemCachedClient拦截器
*/
public class XMemcachedMethodCacheInterceptor implements MethodInterceptor,InitializingBean {
private MemcachedClient memcachedClient;
public void setMemcachedClient(MemcachedClient memcachedClient) {
this.memcachedClient = memcachedClient;
}
public void afterPropertiesSet() throws Exception {
Assert.notNull(memcachedClient,
"A cache is required. Use setCache(Cache) to provide one.");
}
public Object invoke(MethodInvocation invocation) throws Throwable {
Method[] methods = invocation.getThis().getClass().getDeclaredMethods();
String targetName = invocation.getThis().getClass().getName();
Method method = invocation.getMethod(); //这种方法不能取到它的注解
String methodName = method.getName();
Object[] arguments = invocation.getArguments();
Object result= null;
Object object = null;
//定义版本号
String prefixValue = null;
//通过方法名获取每个方法模块的版本号
if(null != memcachedClient.get(targetName))
{
prefixValue = memcachedClient.get(targetName).toString();
}
else
{ prefixValue = CacheVersion.getVersion();
memcachedClient.set(targetName,60*60*24*30, prefixValue);
}
String cacheKey = getCacheKey(targetName, methodName, arguments) + prefixValue;
System.out.println(">>>>>>>>>进入拦截:方法"+methodName+">>>>>>cacheKey:"+cacheKey);
for(Method m:methods){ //循环方法,找匹配的方法进行执行 用m才能取到它的注解
System.out.println("aa循环:"+m.getName()+" "+m.isAnnotationPresent(CacheAdd.class) +m.isAnnotationPresent(CacheRemove.class));
if(m.getName().equals(methodName)){
if(m.isAnnotationPresent(CacheAdd.class)){
System.out.println(">>>>>>>>>添加缓存分支");
//添加缓存
synchronized (this) {
object = memcachedClient.get(cacheKey);
if (object == null) {
// 调用实际的方法
result = invocation.proceed();
System.out.println(">>>>>>>>>走数据库得到结果集为:"+result);
memcachedClient.add(cacheKey,60*60*24 ,result ); //单位:秒
}else{
System.out.println(">>>>>>>>>走缓存_得到结果集为:"+object);
}
}
result = memcachedClient.get(cacheKey);
}else
if(m.isAnnotationPresent(CacheRemove.class)){
System.out.println(">>>>>>>>>删除缓存分支");
//执行正常操作
result = invocation.proceed();
//修改后,版本号 。以前的版本号组成的cacheKey就查询不到了 慢慢过期销毁
String version =CacheVersion.getVersion();
memcachedClient.replace(targetName,60*60*24, version);
System.out.println("更新"+targetName+"版本号为:"+version);
}else{
System.out.println(">>>>>>>>>没有缓存操作分支");
result = invocation.proceed();
}
break;//找到这个方法就退出循环
}
}
return result;
}
private String getCacheKey(String targetName, String methodName,
Object[] arguments) {
StringBuffer sb = new StringBuffer();
sb.append(targetName).append(".").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
sb.append(".").append(arguments[i]);
}
}
return sb.toString();
}
}
生成版本号
package com.jing.memcached.interceptor;
import java.util.UUID;
public class CacheVersion {
public static String getVersion()
{
UUID uuid = UUID.randomUUID();
return uuid.toString().toUpperCase();
}
}