前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >扩展spring cache 支持缓存多租户及其自动过期

扩展spring cache 支持缓存多租户及其自动过期

作者头像
冷冷
发布2019-05-26 16:06:26
1.5K0
发布2019-05-26 16:06:26
举报
文章被收录于专栏:冷冷冷冷

spring cache 的概念

Spring 支持基于注释(annotation)的缓存(cache)技术,它本质上不是一个具体的缓存实现方案(例如 EHCache 或者 OSCache),而是一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的效果。

  • @Cacheable 使用效果 ,更具 cacheName(value) + 请求入参 (key) 组成保存redis中的key
代码语言:javascript
复制
public class PigxClientDetailsService extends JdbcClientDetailsService {
	@Cacheable(value = SecurityConstants.CLIENT_DETAILS_KEY, key = "#clientId")
	public ClientDetails loadClientByClientId(String clientId) {
		return super.loadClientByClientId(clientId);
	}
}}

多租户下缓存问题分析

  • 默认情况 A租户入参为K1 请求 应用,spring cache 会自动缓存 K1 的值,如果B租户 入参同时为K1 请求应用时,spring cache 还是会自动关联到同一个 Redis K1 上边查询数据。
  • 在多租户下 A/B 租户所请求的K1 并不是同一入参(虽然看起来参数名 参数值都是一样的),更不能返回同一个结果。
  • 默认的spring cache 根据入参来区分 不能满足多租户系统的设计需求,不能实现根据租户隔离。

区分缓存增加租户标识

  • A租户入参为K1 ,spring cache 维护Redis Key 在拼接一个租户信息
  • KEY = cacheName + 入参 + 租户标识
  • 这样A/B 租户请求参数相同时,读取的也是不同的Key 里面的值,避免数据脏读,保证隔离型

重写Spring Cache 的 cacheManager 缓存管理器

  • 从上下文中获取租户ID,重写@Cacheable value 值即可完成,然后注入这个 cacheManager
代码语言:javascript
复制
@Slf4j
public class RedisAutoCacheManager extends RedisCacheManager {
	/**
	 * 从上下文中获取租户ID,重写@Cacheable value 值
	 * @param name
	 * @return
	 */
	@Override
	public Cache getCache(String name) {
		return super.getCache(TenantContextHolder.getTenantId() + StrUtil.COLON + name);
	}
}
  • 为什么要用 StrUtil.COLON 即 ':' 分割 在GUI 工具中,会通过':'的分隔符,进行分组,展示效果会更好

增加 spring cache 的主动过期功能

  • 默认的注解里面没有关于时间的入参,如下图
代码语言:javascript
复制
public @interface Cacheable {

	@AliasFor("cacheNames")
	String[] value() default {};

	@AliasFor("value")
	String[] cacheNames() default {};

	String key() default "";

	String keyGenerator() default "";

	String cacheManager() default "";

	String cacheResolver() default "";

	String condition() default "";

	String unless() default "";

	boolean sync() default false;

}
  • 还是以value作为入口 value = "menu_details#2000" 通过对vaue 追加一个数字 并通过特殊字符分割,作为过期时间入参
代码语言:javascript
复制
@Service
@AllArgsConstructor
public class PigXMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
	private final SysRoleMenuMapper sysRoleMenuMapper;

	@Override
	@Cacheable(value = "menu_details#2000", key = "#roleId  + '_menu'")
	public List<MenuVO> findMenuByRoleId(Integer roleId) {
		return baseMapper.listMenusByRoleId(roleId);
	}
}
  • 重写cachemanager 另个重要的方法 创建缓存的方法,通过截取 value 中设置的过期时间,赋值给你RedisCacheConfiguration
代码语言:javascript
复制
public class RedisAutoCacheManager extends RedisCacheManager {
	private static final String SPLIT_FLAG = "#";
	private static final int CACHE_LENGTH = 2;

	@Override
	protected RedisCache createRedisCache(String name, @Nullable RedisCacheConfiguration cacheConfig) {
		if (StrUtil.isBlank(name) || !name.contains(SPLIT_FLAG)) {
			return super.createRedisCache(name, cacheConfig);
		}

		String[] cacheArray = name.split(SPLIT_FLAG);
		if (cacheArray.length < CACHE_LENGTH) {
			return super.createRedisCache(name, cacheConfig);
		}

		if (cacheConfig != null) {
			long cacheAge = Long.parseLong(cacheArray[1]);
			cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(cacheAge));
		}
		return super.createRedisCache(name, cacheConfig);
	}
}
  • spring cache 操作缓存时 获取到上步设置的ttl 赋值给key
代码语言:javascript
复制
	@Override
	public void put(Object key, @Nullable Object value) {

		Object cacheValue = preProcessCacheValue(value);

		if (!isAllowNullValues() && cacheValue == null) {

			throw new IllegalArgumentException(String.format(
					"Cache '%s' does not allow 'null' values. Avoid storing null via '@Cacheable(unless=\"#result == null\")' or configure RedisCache to allow 'null' via RedisCacheConfiguration.",
					name));
		}

		cacheWriter.put(name, createAndConvertCacheKey(key), serializeCacheValue(cacheValue), cacheConfig.getTtl());
	}

总结

  • 通过对spring cache 的扩展即可实现对缓存 一些透明操作
  • cachemanager 是springcache 对外提供的API 扩展入口
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • spring cache 的概念
  • 多租户下缓存问题分析
  • 区分缓存增加租户标识
  • 重写Spring Cache 的 cacheManager 缓存管理器
  • 增加 spring cache 的主动过期功能
  • 总结
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档