项目中cache应用实践
来源:互联网 发布:手机淘宝能微信支付吗 编辑:程序博客网 时间:2024/05/21 21:27
项目中缓存应用场景分析
- 以前项目采用hibernate的orm模型,memcached来做数据层的二级缓存,效果非常的好。现在互联网项目为充分利用数据库特征都采用mybatis来做orm框架。
- 项目中我们还是有一些缓存使用场景,spring提供了基于service方式拦截的缓存解决方案。org.springframework.cache.cache接口提供一些方法,spring还提供相应的实现类。详情可参考http://jinnianshilongnian.iteye.com/blog/2001040
- 为结合公司项目,决定自己来设计一套自己cache支持组件,采用redis来作为缓存工具,借鉴spring cache模型,使用aop methodInterceptor来实现service方法的缓存机制。
- spring cache设计是可crud操作维护数据缓存。我设计cache只是为减少数据查询,提高响应速度。使用场景适合数据不需要及时准备的展示。
定义CacheManager接口及方法
/** * 缓存管理接口 * * @author my * @Date 2016年8月7日 下午4:11:21 */public interface CacheManager { /** * 得到缓存记录 * @author andrexu * @param key * @return */ public Serializable get(String key); /** * 写入一条记录到缓存中 * @author andrexu * @param key 缓存key * @param result 缓存内容 * @param timeToIdleSeconds 过期时间(单位秒) */ public boolean put(String key, Serializable result, int timeToIdleSeconds); /** * 写入一条记录到缓存中 * @author andrexu * @param key 缓存key * @param result 缓存内容 * @param idleDate 过期日期 */ public boolean put(String key, Serializable result, Date idleDate); /** * 删除一条缓存 * @author andrexu * @param key */ public void remove(String key); /** * 得到缓存数量 * @author andrexu * @return */ public int getSize(); /** * 清空缓存 * @author andrexu */ public void clear();}
主要有get,put,rmove方法,put必须设置失效时间,失效时间点。
redis实现类RedisStrCacheManagerImpl
- redis比mecached作为cache优点,就不在概述。
- key采用String序列化。value选择json序列化相比jdk序列化速度快,体积小,在redisClient软件可以浏览缓存数据。
@Service("redisStrCacheManagerImpl")public class RedisStrCacheManagerImpl implements CacheManager { private final Logger logger = LoggerFactory.getLogger(RedisStrCacheManagerImpl.class); public static final String PREFIX_KEY = "calm:cache:"; private RedisSerializer<String> keySerializer = new StringRedisSerializer(); // private RedisSerializer<Object> valueSerializer = new JdkSerializationRedisSerializer(); private RedisSerializer<Object> valueSerializer =new Jackson2JsonRedisSerializer<Object>(Object.class); @Resource(name = "redisTemplate") private RedisTemplate<Serializable, Object> redisTemplate; @Override public Serializable get(String cachekey) { try { final String cachekey2 = PREFIX_KEY + cachekey; Object valueObject = redisTemplate.execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { byte[] key = keySerializer.serialize(cachekey2); if (connection.exists(key)) { byte[] value = connection.get(key); return valueSerializer.deserialize(value); } return null; } }); if (valueObject == null) { return null; } return (Serializable) valueObject; } catch (Exception e) { logger.error("RedisStrCacheManagerImpl:" + e.getMessage()); } return null; } @Override public boolean put(String key, final Serializable result, final int timeToIdleSeconds) { try { final String cachekey = PREFIX_KEY + key; redisTemplate.execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { byte[] keyArray = keySerializer.serialize(cachekey); connection.set(keyArray, valueSerializer.serialize(result)); //api里失效时间传 毫秒。 connection.pExpire(keyArray, TimeoutUtils.toMillis(timeToIdleSeconds, TimeUnit.SECONDS)); return null; } }); } catch (Exception e) { logger.error("RedisStrCacheManagerImpl put:" + e.getMessage()); } return true; } @Override public boolean put(String key, final Serializable result, Date idleDate) { try { Date currentDate = new Date(); final long expireTime = idleDate.getTime() - currentDate.getTime(); final String cachekey = PREFIX_KEY + key; redisTemplate.execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { byte[] keyArray = keySerializer.serialize(cachekey); connection.set(keyArray, valueSerializer.serialize(result)); connection.pExpire(keyArray, expireTime); return null; } }); } catch (Exception e) { logger.error("RedisStrCacheManagerImpl put:" + e.getMessage()); } return true; } @Override public void remove(String key) { final String cachekey = PREFIX_KEY + key; redisTemplate.execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection connection) throws DataAccessException { byte[] keyArray = keySerializer.serialize(cachekey); connection.del(keyArray); return null; } }); } @Override public int getSize() { if (true) { throw new RuntimeException("not the method"); } return 0; } @Override public void clear() { throw new RuntimeException("not the method"); }
在redisManager浏览cache数据:
MethodInterceptor详解
- 在controller调用service方式时,采用了MethodInterceptor方法来判断是否使用cache,查询cache。
- 根据Service方式上MethodCache注解来判断是否使用cache,及cache失效策略。
- 关注cache的key生成策略,参考了spring的DefaultKeyGenerator类,这里生成策略是className.methodName.arguments[0].arguments[1]的拼接字符串,然后再用Base64转码,参考代码方法getCacheKey
package com.calm.b.common.cache.methodcache;import com.calm.b.common.cache.CacheManager;import com.calm.b.common.cache.CacheUtils;import com.calm.b.common.cache.anno.MethodCache;import com.calm.b.util.Base64Utils;import org.aopalliance.intercept.MethodInterceptor;import org.aopalliance.intercept.MethodInvocation;import org.apache.commons.lang.StringUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import javax.annotation.Resource;import java.io.Serializable;import java.lang.reflect.Method;/** * * @author my * @Date 2016年8月7日 下午4:09:35 */public class MethodCacheInterceptor implements MethodInterceptor { private final Logger logger= LoggerFactory.getLogger(MethodCacheInterceptor.class); @Resource(name="redisStrCacheManagerImpl") private CacheManager memcachedManager; @Value("${enable.method.cache}") private String enableMethodCache; @Override public Object invoke(MethodInvocation invocation) throws Throwable { String realClassName = StringUtils.substringBefore(invocation.getThis().toString(), "@"); Class<?> clazz = Class.forName(realClassName); Method invocationMethod = invocation.getMethod(); Method method = clazz.getMethod(invocationMethod.getName(), invocationMethod.getParameterTypes()); if (method.getReturnType().getName().equals("void")) { return invocation.proceed(); } MethodCache methodCache = method.getAnnotation(MethodCache.class); if (methodCache == null||methodCache.timeToIdleSeconds()<=0||!StringUtils.equals(enableMethodCache,"true")) { return invocation.proceed(); } String targetName = clazz.getName(); String methodName = method.getName(); /** * 根据className,methodName,参数,及忽略参数,生产缓存key */ String cacheKey = getCacheKey(targetName, methodName, invocation.getArguments(), methodCache.ignoreParams()); Serializable cacheResult = memcachedManager.get(cacheKey); if (cacheResult == null) { cacheResult = (Serializable) invocation.proceed(); memcachedManager.put(cacheKey, cacheResult, CacheUtils.calculateExpireDate(methodCache.idleTime(), methodCache.timeToIdleSeconds())); } return cacheResult; } /** * key生成机制:className.methodName.arguments[0].arguments[1]. ... 再进行转码 若argument为领域对象,请重写其hashCode方法 * * @param targetName * @param methodName * @param arguments * @param ignoreParams * @return */ private String getCacheKey(String targetName, String methodName, Object[] arguments, boolean ignoreParams) { StringBuffer sb = new StringBuffer(); sb.append(targetName).append(".").append(methodName); if (!ignoreParams) { if ((arguments != null) && (arguments.length != 0)) { for (int i = 0; i < arguments.length; i++) { sb.append(".").append(arguments[i]); } } } return Base64Utils.encoding(sb.toString()); }}
spring的proxy配置如下,采用spring proxy配置模式。
<!-- redis method cache component--> <bean id="methodCacheInterceptor" lazy-init="false" class="com.calm.b.common.cache.methodcache.MethodCacheInterceptor"/> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <value>*CacheService</value> </property> <property name="interceptorNames"> <list> <value>methodCacheInterceptor</value> </list> </property> </bean>
项目service方法使用:
@Override @MethodCache(timeToIdleSeconds = 300) public List<AuthorizedLogVo> findAuthorizedLogVos(Long tradeId){ // ignore business logic code }
0 0
- 项目中cache应用实践
- .net中Cache的应用
- .net中Cache的应用
- .net中Cache的应用
- 项目管理知识实践应用浅谈
- spring cache 应用Jcache和ehcache3.x 实践
- ASP中CACHE技术的应用
- ASP中CACHE技术的应用
- ASP中CACHE技术的应用
- 网站(B/S开发中)cache应用
- asp中缓存cache技术的应用
- ASP中CACHE技术的应用
- ASP中Cache技术的应用
- 网站(B/S开发中)cache应用
- Web应用开发中无处不在的Cache
- [转].net中Cache的应用
- asp中cache技术的应用
- Cache 应用
- Ubuntu16.04下Python2.7.12从头开始配置TensorFlow
- Java多线程系列--“JUC线程池”03之 线程池原理(二)
- 奇数位上都是奇数或者偶数位上都是偶数---BAT实习内推笔试卷(第一场)
- TCP拥塞控制的实现
- 家教信息服务平台的开发
- 项目中cache应用实践
- Java基础之(三十六)Java异常处理<二>
- 计算机视觉、模式识别、机器学习相关方向资源
- 【Light-oj】-1043 - Triangle Partitioning(数学)
- ELK 日志归集
- Java多线程系列--“JUC线程池”04之 线程池原理(三)
- 集合的子集
- 不得不在脑海里徘徊的站外(第三方)SEO优化策略
- Java多线程系列--“JUC线程池”05之 线程池原理(四)