首页 > 数据库 >使用Memcached、Spring AOP构建数据库前端缓存框架

使用Memcached、Spring AOP构建数据库前端缓存框架

时间:2023-07-26 15:31:46浏览次数:55  
标签:缓存 name Spring findUserByName memcached com AOP import Memcached


 

上回说到Memcahed的安装及java客户端的使用(http://my249645546.iteye.com/blog/1420061),现在我们使用memcached、Spring AOP技术来构建一个数据库的缓存框架。

数据库访问可能是很多网站的瓶颈。动不动就连接池耗尽、内存溢出等。前面已经讲到如果我们的网站是一个分布式的大型站点,那么使用memcached实现数据库的前端缓存是个很不错的选择;但如果网站本身足够小只有一个服务器,甚至是vps的那种,不推荐使用memcached,使用Hibernate或者Mybatis框架自带的缓存系统就行了。

 

一、开启memcached服务器端服务

如果已经安装了memcached服务器端程序,请确认服务器端服务已开启。

二、引入jar

1.  alisoft-xplatform-asf-cache-2.5.1.jar

2.  commons-logging-1.0.4.jar

3.  hessian-3.0.1.jar

4.  log4j-1.2.9.jar

5.  stax-api-1.0.1.jar

6.  wstx-asl-2.0.2.jar

三、创建memcached客户端配置文件


01
 <memcached> 
 
  
02
 <!-- name 属性是程序中使用Cache的唯一标识;socketpool 属性将会关联到后面的socketpool配置; --> 
 
  
03
 <client name="mclient_0" compressEnable="true" defaultEncoding="UTF-8" 
 
  
04
 socketpool="pool_0"> 
 
  
05
 <!-- 可选,用来处理出错情况 --> 
 
  
06
 <errorHandler>com.alisoft.xplatform.asf.cache.memcached.MemcachedErrorHandler 
 
  
07
 </errorHandler> 
 
  
08
 </client> 
 
  
09
 
 
  
10
 <!-- 
 
  
11
 name 属性和client 配置中的socketpool 属性相关联。 
 
  
12
 maintSleep属性是后台线程管理SocketIO池的检查间隔时间,如果设置为0,则表明不需要后台线程维护SocketIO线程池,默认需要管理。 
 
  
13
 socketTO 属性是Socket操作超时配置,单位ms。 aliveCheck 
 
  
14
 属性表示在使用Socket以前是否先检查Socket状态。 
 
  
15
 --> 
 
  
16
 <socketpool name="pool_0" maintSleep="5000" socketTO="3000" 
 
  
17
 failover="true" aliveCheck="true" initConn="5" minConn="5" maxConn="250" 
 
  
18
 nagle="false"> 
 
  
19
 <!-- 设置memcache服务端实例地址.多个地址用","隔开 --> 
 
  
20
 <servers>127.0.0.1:11211</servers> 
 
  
21
 <!-- 
 
  
22
 可选配置。表明了上面设置的服务器实例的Load权重. 例如 <weights>3,7</weights> 表示30% load 在 
 
  
23
 10.2.224.36:33001, 70% load 在 10.2.224.46:33001 
 
  
24
  
 
  
25
 <weights>3,7</weights> 
 
  
26
 --> 
 
  
27
 </socketpool> 
 
  
28
 </memcached>

四、创建memcached客户端程序

客户端工具类:


01
 package com.hl.usersmanager.memcached.client; 
 
  
02
 
 
  
03
 import com.alisoft.xplatform.asf.cache.ICacheManager; 
 
  
04
 import com.alisoft.xplatform.asf.cache.IMemcachedCache; 
 
  
05
 import com.alisoft.xplatform.asf.cache.memcached.CacheUtil; 
 
  
06
 import com.alisoft.xplatform.asf.cache.memcached.MemcachedCacheManager; 
 
  
07
 
 
  
08
 public class MemcachedCache { 
 
  
09
 private ICacheManager<IMemcachedCache> manager; 
 
  
10
 private IMemcachedCache cache; 
 
  
11
  
 
  
12
 public MemcachedCache(){ 
 
  
13
 manager = CacheUtil.getCacheManager(IMemcachedCache.class, 
 
  
14
 MemcachedCacheManager.class.getName()); 
 
  
15
 manager.setConfigFile("memcached.xml"); 
 
  
16
 manager.setResponseStatInterval(5*1000); 
 
  
17
 manager.start(); 
 
  
18
 cache = manager.getCache("mclient_0"); 
 
  
19
 } 
 
  
20
  
 
  
21
 /** 
 
  
22
 * 获取缓存接口 
 
  
23
 * @return 
 
  
24
 */ 
 
  
25
 public IMemcachedCache getCache(){ 
 
  
26
 return cache; 
 
  
27
 } 
 
  
28
  
 
  
29
 /** 
 
  
30
 * 数据放入缓存 
 
  
31
 * @param key 
 
  
32
 * @param object 
 
  
33
 */ 
 
  
34
 public void put(String key,Object object){ 
 
  
35
 cache.put(key, object); 
 
  
36
 } 
 
  
37
  
 
  
38
 /** 
 
  
39
 * 从缓存中读取数据 
 
  
40
 * @param key 
 
  
41
 * @return 
 
  
42
 */ 
 
  
43
 public Object get(String key){ 
 
  
44
 return cache.get(key); 
 
  
45
 } 
 
  
46
}


五、使用Spring AOP在数据查询的Service层实现数据缓存及读取

实现数据缓存的过程很简单,就是在Service层查询数据库操作前判断要查询的数据在缓存中是否存在,如果不存在就到数据库中查询,查询完成后将数据放入缓存系统;如果要查询的数据在缓存中已经存在,则直接从缓存中读取,不需要操作数据库。这就大大降低了数据库的连接次数。原理就是这么简单。

但是,如果直接对Service层代码进行修改,就违背了“开放-封闭”原则,也会导致缓存系统的操作代码散落到Service层的各处,不方便代码的管理和维护。所以,Spring AOP华丽登场了。它使用非入侵式的来创建、管理这些缓存操作代码。

关于Spring AOP本身的一些知识,我们这里不做讲述。参考资料:

由于首先要判断查询数据是否存在于缓存系统,如果存在直接从缓存中读取,也就是说Service层的查询代码根本不会执行;另一方面,如果数据在缓存系统中不存在,从数据库查询出的结果,我们需要将其放入缓存系统中。

我们来看Spring AOP的几个装备中哪个适用呢?那就是最强大的环绕通知装备@Around

 

下面以UserService为例,其源代码如下:


01
 package com.hl.usersmanager.service.impl; 
 
  
02
 
 
  
03
 import java.util.List; 
 
  
04
 
 
  
05
 import org.springframework.beans.factory.annotation.Autowired; 
 
  
06
 import org.springframework.stereotype.Service; 
 
  
07
 import org.springframework.transaction.annotation.Transactional; 
 
  
08
 
 
  
09
 import com.hl.usersmanager.dao.IUserMapper; 
 
  
10
 import com.hl.usersmanager.model.Users; 
 
  
11
 import com.hl.usersmanager.service.IUserService; 
 
  
12
 
 
  
13
//使用Service注解 不需要再在配置文件中配置bean
 
  
14
@Service
 
  
15
 public class UserServiceImpl implements IUserService{ 
 
  
16
 @Autowired 
 
  
17
 private IUserMapper userMapper; 
 
  
18
  
 
  
19
 @Override 
 
  
20
 @Transactional 
 
  
21
 public Users findUserByName(String name) { 
 
  
22
 return userMapper.findUserByName(name); 
 
  
23
 } 
 
  
24
 
 
  
25
 …… 
 
  
26
}


  findUserByName主要实现按照用户名查询用户的功能,现在我们使用Spring AOP来实现缓存:


01
 package com.hl.usersmanager.aop.service; 
 
  
02
 
 
  
03
 import org.apache.log4j.Logger; 
 
  
04
 import org.aspectj.lang.ProceedingJoinPoint; 
 
  
05
 import org.aspectj.lang.annotation.Around; 
 
  
06
 import org.aspectj.lang.annotation.Aspect; 
 
  
07
 import org.aspectj.lang.annotation.Pointcut; 
 
  
08
 import org.springframework.beans.factory.annotation.Autowired; 
 
  
09
 
 
  
10
 import com.hl.usersmanager.memcached.client.MemcachedCache; 
 
  
11
 import com.hl.usersmanager.model.Users; 
 
  
12
 
 
  
13
@Aspect
 
  
14
 public class UserServiceInterceptor { 
 
  
15
 public static final Logger log = Logger 
 
  
16
 .getLogger(UserServiceInterceptor.class); 
 
  
17
 
 
  
18
//将缓存客户端工具类 MemcachedCache 织入进来
 
  
19
 @Autowired 
 
  
20
 private MemcachedCache memcachedCache; 
 
  
21
 
 
  
22
 /* 
 
  
23
 * 定义pointcunt 
 
  
24
 */ 
 
  
25
 @Pointcut("execution(* com.hl.usersmanager.service.impl.UserServiceImpl.*(..))") 
 
  
26
 public void aPointcut() { 
 
  
27
 
 
  
28
 } 
 
  
29
 
 
  
30
 /** 
 
  
31
 * 环绕装备 用于拦截查询 如果缓存中有数据,直接从缓存中读取;否则从数据库读取并将结果放入缓存 
 
  
32
 * 
 
  
33
 * @param call 
 
  
34
 * @param name 
 
  
35
 * @return 
 
  
36
 */ 
 
  
37
 @Around("aPointcut()&&args(name)") 
 
  
38
 public Users doFindUserByNameAround(ProceedingJoinPoint call, String name) { 
 
  
39
 Users users = null; 
 
  
40
 if (memcachedCache.getCache().containsKey("findUserByName_" + name)) { 
 
  
41
 users = (Users) memcachedCache.get("findUserByName_" + name); 
 
  
42
 log.debug("从缓存中读取!findUserByName_" + name); 
 
  
43
 } else { 
 
  
44
 try { 
 
  
45
 users = (Users) call.proceed(); 
 
  
46
 if (users != null) { 
 
  
47
 memcachedCache.put("findUserByName_" + name, users); 
 
  
48
 log.debug("缓存装备被执行:findUserByName_" + name); 
 
  
49
 } 
 
  
50
 } catch (Throwable e) { 
 
  
51
 e.printStackTrace(); 
 
  
52
 } 
 
  
53
 } 
 
  
54
 return users; 
 
  
55
 } 
 
  
56
}


 环绕通知装备需要一个ProceedingJoinPoint 类型的参数,它的强大之处在于可以代理一个我们的切入点,指定切入点方法是否执行,或者获取执行后的返回结果!!

 

memcachedCache.getCache().containsKey("findUserByName_" + name)

可以判断缓存中是否有指定的数据。如果有则直接从缓存中读取:

users = (Users) memcachedCache.get("findUserByName_" + name);

否则调用切入点UserServiceImpl的findUserByName方法:

users = (Users) call.proceed();

 call.proceed()表示执行切入点的方法。

 

 

使用Spring AOP以后,整个缓存系统代码看起来 就是这么优雅!UserServiceImpl根本不知道外界发了什么,更不知道外界调用它的findUserByName的时候已经被拦截了!

那天不用缓存系统,只需要将Aop这块的代码去掉即可。

 

当然,我们还需要在Spring 配置文件中注册一个memcached客户端工具类的bean:


1
<!-- MemcachedCache缓存 -->
 
  
2
 <bean id="MemcachedCache"class="com.hl.usersmanager.memcached.client.MemcachedCache"></bean>

标签:缓存,name,Spring,findUserByName,memcached,com,AOP,import,Memcached
From: https://blog.51cto.com/nethub/6856615

相关文章

  • Spring事务的传播行为
    Spring事务的七种传播行为首先举例事务的嵌套:ServiceA{voidmethodA(){ServiceB.methodB();}}ServiceB{voidmethodB(){}}其中ServiceA#methodA(我们称之为外部事务),ServiceB#methodB(我们称之为内部事务)......
  • 【Java面试题】Spring是如何解决循环依赖问题?
    ......
  • Spring 中的 @Cacheable 缓存注解,太好用了!
    1什么是缓存第一个问题,首先要搞明白什么是缓存,缓存的意义是什么。对于普通业务,如果要查询一个数据,一般直接select数据库进行查找。但是在高流量的情况下,直接查找数据库就会成为性能的瓶颈。因为数据库查找的流程是先要从磁盘拿到数据,再刷新到内存,再返回数据。磁盘相比于内存来......
  • SpringBoot中定时任务开启多线程避免多任务堵塞
    场景SpringBoot中定时任务与异步定时任务的实现:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/117083609使用SpringBoot原生方式实现定时任务,已经开启多线程支持,以上是方式之一。除此之外还可通过如下方式。为什么SpringBoot定时任务是单线程的?查看注解@Ena......
  • 一次性打包学透 Spring
    不知从何时开始,Spring这个词开始频繁地出现在Java服务端开发者的日常工作中,很多Java开发者从工作的第一天开始就在使用SpringFramework,甚至有人调侃“不会Spring都不好意思自称是个Java开发者”。之所以出现这种局面,源于Spring是一个极为优秀的一站式集成框架,对Java......
  • Spring Boot 实现文件断点下载,实战来了!
    来源:juejin.cn/post/7026372482110079012前言互联网的连接速度慢且不稳定,有可能由于网络故障导致断开连接。在客户端下载一个大对象时,因网络断开导致上传下载失败的概率就会变得不可忽视。客户端在GET对象请求时通过设置Range头部来告诉接口服务需要从什么位置开始输出对象......
  • spring启动流程 (6完结) springmvc启动流程
    SpringMVC的启动入口在SpringServletContainerInitializer类,它是ServletContainerInitializer实现类(Servlet3.0新特性)。在实现方法中使用WebApplicationInitializer创建ApplicationContext、创建注册DispatcherServlet、初始化ApplicationContext等。SpringMVC已经将大部分的启......
  • 你真正了解Spring的工作原理吗
     Spring  1.1什么是SpringIOC和DI?  ①控制反转(IOC):Spring容器使用了工厂模式为我们创建了所需要的对象,我们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控制反转的思想。②依赖注入(DI):Spring使用JavaBean对象的Set方法或者带参数的构造方法......
  • SpringBoot+Prometheus+Grafana实现系统可视化监控
    场景SpringBoot中集成Actuator实现监控系统运行状态:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/124272494基于以上Actuator实现系统监控,还可采用如下方案。PrometheusPrometheus,是一个开源的系统监控和告警的工具包,其采用Pull方式采集时间序列的度量数据(也......
  • 我开源了团队内部基于SpringBoot Web快速开发的API脚手架v1.6.0更新
    什么是rest-api-spring-boot-starterrest-api-spring-boot-starter适用于SpringBootWebAPI快速构建让开发人员快速构建统一规范的业务RestFullAPI不在去关心一些繁琐。重复工作,而是把重点聚焦到业务。动机每次WebAPI常用功能都需要重新写一遍。或者复制之前的项目代码......