使用Memcached作为全网站分布式缓存

来源:互联网 发布:手机fc2最新域名设置 编辑:程序博客网 时间:2024/05/16 15:40

使用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方法生效:

<?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{@Autowiredprivate MemCachedClient memCachedClient;@Testpublic 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 {@Autowiredprivate 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)){//回ServiceObject 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保存需要的Keypublic String getCacheKey(ProceedingJoinPoint pjp){StringBuilder key = new StringBuilder();//获取包名+类名,比如com.core.serice.product.ProductServiceImpl.productListString packageName = pjp.getTarget().getClass().getName();key.append(packageName);//获取方法名String methodName = pjp.getSignature().getName();key.append(".").append(methodName);//获取参数(可能是多个)Object[] args = pjp.getArgs();//因为参数可能是javabean,所以此处要转为json串才能进行拼接生成keyObjectMapper  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 blocke.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


0 0
原创粉丝点击