使用Memcached作为全网站分布式缓存
2015-10-30 14:51
585 查看
使用Memcached作为全网站分布式缓存,大体思路就是:
首先搭建一些Memcached服务器,作为全网站的分布式缓存服务器。当Controller层调用以get*开头的service查询方法时,首先进入配置的切面对象,执行环绕通知方法,查看Memcached中是否已经有了此查询方法执行后的结果,如果有数据,则直接返回Controller,如果没有数据,就返回service层执行查询方法,并且把执行结果放入到Memcached中缓存起来。
当Controller层调用以add*,delete*,update*开头的service方法时,因为这些这些方法的执行会导致数据库数据变更,因此要清理受到影响的那部分缓存的数据,以免造成数据延迟,即也要进入在spring中配置的切面对象,执行后置通知方法,清空受影响的部分缓存数据。
其中有个关键点,是把service查询的数据结果放入Memcached时的K怎么设置,为了保证唯一性,可以设定K生成规则如下:
K=包名+类名+方法名+参数(可以多个),其中,如果参数是javabean的话,要先转为json串,再与前面的包名+类名+方法名进行拼接。
其中包名+类名比如:com.core.serice.product.ProductServiceImpl.productList
①首先要在Linux服务器上搭建Memcache服务器,这个可以查看Memcached官方文档
②在java项目中加入memcache-xx.jar,又分为两个包:
com.danga.MemCached与
net.spy.memcached
因为danga包有连接池,更稳定,因此此次使用的是danga包的Memcached
③Spring引入Memcached.xml,在其中进行连接池,切面对象和aop配置,这样就能保证所有对系统所有以get*,add*,delete*,update*开头的service方法生效:
④先写个测试类测试下Memcached
⑤写切面类com.common.web.aop.CacheInterceptor:
⑥接下来实现Memcached 集群的高可用(HA)架构可参考下面的博客:
http://blog.csdn.net/liu251890347/article/details/38414247
首先搭建一些Memcached服务器,作为全网站的分布式缓存服务器。当Controller层调用以get*开头的service查询方法时,首先进入配置的切面对象,执行环绕通知方法,查看Memcached中是否已经有了此查询方法执行后的结果,如果有数据,则直接返回Controller,如果没有数据,就返回service层执行查询方法,并且把执行结果放入到Memcached中缓存起来。
当Controller层调用以add*,delete*,update*开头的service方法时,因为这些这些方法的执行会导致数据库数据变更,因此要清理受到影响的那部分缓存的数据,以免造成数据延迟,即也要进入在spring中配置的切面对象,执行后置通知方法,清空受影响的部分缓存数据。
其中有个关键点,是把service查询的数据结果放入Memcached时的K怎么设置,为了保证唯一性,可以设定K生成规则如下:
K=包名+类名+方法名+参数(可以多个),其中,如果参数是javabean的话,要先转为json串,再与前面的包名+类名+方法名进行拼接。
其中包名+类名比如:com.core.serice.product.ProductServiceImpl.productList
①首先要在Linux服务器上搭建Memcache服务器,这个可以查看Memcached官方文档
②在java项目中加入memcache-xx.jar,又分为两个包:
com.danga.MemCached与
net.spy.memcached
因为danga包有连接池,更稳定,因此此次使用的是danga包的Memcached
③Spring引入Memcached.xml,在其中进行连接池,切面对象和aop配置,这样就能保证所有对系统所有以get*,add*,delete*,update*开头的service方法生效:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- Memcached 配置 --> <bean id="memCachedClient" class="com.danga.MemCached.MemCachedClient"> <constructor-arg> <value>sockIOPool</value> </constructor-arg> </bean> <!-- Memcached连接池 --> <bean id="sockIOPool" class="com.danga.MemCached.SockIOPool" factory-method="getInstance" init-method="initialize" destroy-method="shutDown"> <constructor-arg> <value>sockIOPool</value> </constructor-arg> <property name="servers"> <list> <!-- 此处可以配置多台安装了Memcached的服务器,Memcached自带路由功能,我们不需要知道数据具体存到了哪个数据库 --> <value>192.168.200.149:11211</value> </list> </property> <property name="weights"> <list> <value>1</value> </list> </property> </bean> <!-- 配置切面对象,expiry是切面类里加的时间参数 --> <bean id="cacheInterceptor" class="com.common.web.aop.CacheInterceptor"> <property name="expiry" value="4200000"/> </bean> <!-- Spring Aop 配置 get*配置环绕通知 ,update*、add*、delete*配置后置通知--> <aop:config> <!-- 面 --> <aop:aspect ref="cacheInterceptor"> <!-- 点 --> <aop:around method="doAround" pointcut="execution(* com.core.service.*.*.get*(..))"/> <!-- 变更 --> <aop:after method="doAfter" pointcut="execution(* com.core.service.*.*.update*(..))"/> <aop:after method="doAfter" pointcut="execution(* com.core.service.*.*.add*(..))"/> <aop:after method="doAfter" pointcut="execution(* com.core.service.*.*.delete*(..))"/> </aop:aspect> </aop:config> </beans>
④先写个测试类测试下Memcached
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:application-context.xml") public class MemcachedTest{ @Autowired private MemCachedClient memCachedClient; @Test public void testAdd() throws Exception { Student student = new Student(); student.setName("哈哈"); //存数据 //memCachedClient.set("aa",student); //取数据 Object object = memCachedClient.get("aa"); System.out.println(object); } }
⑤写切面类com.common.web.aop.CacheInterceptor:
/** * 缓存Memcached中数据的切面对象 */ public class CacheInterceptor { @Autowired private MemCachedClient memCachedClient; //缓存时间,单位是秒 private int expiry = 60*60*24*3; //配置环绕通知方法,get*会执行,环绕通知使用ProceedingJoinPoint,其中包含了请求的一些参数,能够获取要执行的包名,类型,方法名,参数之类的数据 public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ //去Memcached中查看有没有此get方法查找的数据 包名+ 类名 + 方法名 + 参数(多个) String cacheKey = getCacheKey(pjp); System.out.println(cacheKey); //如果Memcached连接不上 if(memCachedClient.stats().isEmpty()){ System.out.println("Memcached服务器可能不存在或是连接不上"); //pjp.proceed()此处就是放开拦截,执行service方法查询 return pjp.proceed(); } //如果以前没有缓存过此get方法查询的数据 if(null == memCachedClient.get(cacheKey)){ //回Service Object proceed = pjp.proceed(); //吧数据缓存到Memcached中一份 memCachedClient.set(cacheKey, proceed,expiry); } return memCachedClient.get(cacheKey); } //add*,update*,delete*后置通知,由于数据库数据变更,因此要清理受到影响的那部分缓存的数据。后置通知使用的是JoinPoint,没有了.proceed()方法 public void doAfter(JoinPoint jp){ //获取包名+类名 String packageName = jp.getTarget().getClass().getName(); //以包名+类名开始的缓存数据都清理掉 //Memcached中获取所有key的方法比较复杂,getKeySet(memCachedClient)也是从网上找的 Map<String, Object> keySet = MemCachedUtil.getKeySet(memCachedClient); Set<Entry<String, Object>> entrySet = keySet.entrySet(); //遍历清除 for(Entry<String, Object> entry : entrySet){ if(entry.getKey().startsWith(packageName)){ memCachedClient.delete(entry.getKey()); } } } //使用包名+类名+方法名+参数(多个)的生成策略生成Memcached保存需要的Key public String getCacheKey(ProceedingJoinPoint pjp){ StringBuilder key = new StringBuilder(); //获取包名+类名,比如com.core.serice.product.ProductServiceImpl.productList String packageName = pjp.getTarget().getClass().getName(); key.append(packageName); //获取方法名 String methodName = pjp.getSignature().getName(); key.append(".").append(methodName); //获取参数(可能是多个) Object[] args = pjp.getArgs(); //因为参数可能是javabean,所以此处要转为json串才能进行拼接生成key ObjectMapper om = new ObjectMapper(); om.setSerializationInclusion(Inclusion.NON_NULL); //遍历参数,转json串 for(Object arg : args){ //流 StringWriter str = new StringWriter(); //对象转Json,写入流中 try { om.writeValue(str, arg); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } //参数 key.append(".").append(str); } return key.toString(); } public void setExpiry(int expiry) { this.expiry = expiry; } }
⑥接下来实现Memcached 集群的高可用(HA)架构可参考下面的博客:
http://blog.csdn.net/liu251890347/article/details/38414247
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树
- [原创]java局域网聊天系统