如何扩展Spring Cache实现支持多级缓存

为什么多级缓存

缓存的引入是现在大部分系统所必须考虑的

redis 作为常用中间件,虽然我们一般业务系统(毕竟业务量有限)不会遇到如下图 在随着 data-size 的增大和数据结构的复杂的造成性能下降,但网络 IO 消耗会成为整个调用链路中不可忽视的部分。尤其在 微服务架构中,一次调用往往会涉及多次调用 例如pig oauth2.0 的 client 认证

Caffeine 来自未来的本地内存缓存,性能比如常见的内存缓存实现性能高出不少详细对比

综合所述:我们需要构建 L1 Caffeine JVM 级别缓存 , L2 Redis 缓存。

设计难点

目前大部分应用缓存都是基于 Spring Cache 实现,基于注解(annotation)的缓存(cache)技术,存在的问题如下:

  • Spring Cache 仅支持 单一的缓存来源,即:只能选择 Redis 实现或者 Caffeine 实现,并不能同时使用。
  • 数据一致性:各层缓存之间的数据一致性问题,如应用层缓存和分布式缓存之前的数据一致性问题。
  • 缓存过期:Spring Cache 不支持主动的过期策略

业务流程

如何使用

引入依赖

<dependency>
  <groupId>com.pig4cloud.plugin</groupId>
  <artifactId>multilevel-cache-spring-boot-starter</artifactId>
  <version>0.0.1</version>
</dependency>

开启缓存支持

@EnableCaching
public class App {
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

目标接口声明 Spring Cache 注解

@Cacheable(value = "get",key = "#key")
@GetMapping("/get")
public String get(String key){
  return "success";
}

性能比较

为保证性能 redis 在 127.0.0.1 环路安装

  • OS: macOS Mojave
  • CPU: 2.3 GHz Intel Core i5
  • RAM: 8 GB 2133 MHz LPDDR3
  • JVM: corretto_11.jdk
Benchmark Mode Cnt Score Units
多级实现 thrpt 2 2716.074 ops/s
默认 redis thrpt 2 1373.476 ops/s

代码原理

自定义 CacheManager 多级缓存实现

public class RedisCaffeineCacheManager implements CacheManager {

	@Override
	public Cache getCache(String name) {
		Cache cache = cacheMap.get(name);
		if (cache != null) {
			return cache;
		}
		cache = new RedisCaffeineCache(name, stringKeyRedisTemplate, caffeineCache(), cacheConfigProperties);
		Cache oldCache = cacheMap.putIfAbsent(name, cache);
		log.debug("create cache instance, the cache name is : {}", name);
		return oldCache == null ? cache : oldCache;
	}
}

多级读取、过期策略实现

public class RedisCaffeineCache extends AbstractValueAdaptingCache {
	protected Object lookup(Object key) {
		Object cacheKey = getKey(key);

  // 1. 先调用 caffeine 查询是否存在指定的值
		Object value = caffeineCache.getIfPresent(key);
		if (value != null) {
			log.debug("get cache from caffeine, the key is : {}", cacheKey);
			return value;
		}

  // 2. 调用 redis 查询在指定的值
		value = stringKeyRedisTemplate.opsForValue().get(cacheKey);

		if (value != null) {
			log.debug("get cache from redis and put in caffeine, the key is : {}", cacheKey);
			caffeineCache.put(key, value);
		}
		return value;
	}
}

过期策略,所有更新操作都基于 redis pub/sub 消息机制更新

public class RedisCaffeineCache extends AbstractValueAdaptingCache {
	@Override
	public void put(Object key, Object value) {
		push(new CacheMessage(this.name, key));
	}

	@Override
	public ValueWrapper putIfAbsent(Object key, Object value) {
				push(new CacheMessage(this.name, key));
	}

	@Override
	public void evict(Object key) {
		push(new CacheMessage(this.name, key));
	}

	@Override
	public void clear() {
		push(new CacheMessage(this.name, null));
	}

	private void push(CacheMessage message) {
		stringKeyRedisTemplate.convertAndSend(topic, message);
	}
}

MessageListener 删除指定 Caffeine 的指定值

public class CacheMessageListener implements MessageListener {

	private final RedisTemplate<Object, Object> redisTemplate;

	private final RedisCaffeineCacheManager redisCaffeineCacheManager;

	@Override
	public void onMessage(Message message, byte[] pattern) {
		CacheMessage cacheMessage = (CacheMessage) redisTemplate.getValueSerializer().deserialize(message.getBody());
				cacheMessage.getCacheName(), cacheMessage.getKey());
		redisCaffeineCacheManager.clearLocal(cacheMessage.getCacheName(), cacheMessage.getKey());
	}
}

https://github.com/pig-mesh/multilevel-cache-spring-boot-starter

https://gitee.com/log4j/pig

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Cache手动清理Redis缓存

    这篇文章主要介绍了Spring Cache手动清理Redis缓存,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 注册cacheRedisTemplate 将 cache 的 RedisTemplate 注册为Bean @Bean(name = "cacheRedisTemplate") public RedisTemplate cacheRedisTemplate(@Qualifier("jedisConnectionFac

  • springboot使用GuavaCache做简单缓存处理的方法

    问题背景 实际项目碰到一个上游服务商接口有10秒的查询限制(同个账号). 项目中有一个需求是要实时统计一些数据,一个应用下可能有多个相同的账号.由于服务商接口的限制,当批量查询时,可能出现同一个账号第一次查询有数据,但第二次查询无数据的情况. 解决方案 基于以上问题,提出用缓存的过期时间来解决. 这时,可用Redis和Guava Cache来解决: 当批量查询时,同一个账号第一次查询有数据则缓存并设置过期时间10s, 后续查询时直接从缓存中取,没有再从服务商查询. 最终采用Guava Cache

  • Spring Boot集成Ehcache缓存解决方式

    本次内容主要介绍基于Ehcache 3.0来快速实现Spring Boot应用程序的数据缓存功能.在Spring Boot应用程序中,我们可以通过Spring Caching来快速搞定数据缓存. 接下来我们将介绍如何在三步之内搞定 Spring Boot 缓存. 1. 创建一个Spring Boot工程 你所创建的Spring Boot应用程序的maven依赖文件至少应该是下面的样子: <?xml version="1.0" encoding="UTF-8"?

  • 详解springboot整合ehcache实现缓存机制

    EhCache 是一个纯Java的进程内缓存框架,具有快速.精干等特点,是Hibernate中默认的CacheProvider. ehcache提供了多种缓存策略,主要分为内存和磁盘两级,所以无需担心容量问题. spring-boot是一个快速的集成框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置. 由于spring-boot无需任何样板化的配置文件,所以spring-boot集成一些其他框架时会有略微的

  • 详解SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)

    引言 ​前两天在写一个实时数据处理的项目,项目要求是 1s 要处理掉 1k 的数据,这时候显然光靠查数据库是不行的,技术选型的时候老大跟我提了一下使用 Layering-Cache 这个开源项目来做缓存框架. ​之间问了一下身边的小伙伴,似乎对这块了解不多.一般也就用用 Redis 来缓存,应该是很少用多级缓存框架来专门性的管理缓存吧. ​趁着这个机会,我多了解了一些关于 SpringBoot 中缓存的相关技术,于是有了这篇文章! 在项目性能需求比较高时,就不能单单依赖数据库访问来获取数据了,必

  • spring boot+spring cache实现两级缓存(redis+caffeine)

    spring boot中集成了spring cache,并有多种缓存方式的实现,如:Redis.Caffeine.JCache.EhCache等等.但如果只用一种缓存,要么会有较大的网络消耗(如Redis),要么就是内存占用太大(如Caffeine这种应用内存缓存).在很多场景下,可以结合起来实现一.二级缓存的方式,能够很大程度提高应用的处理效率. 内容说明: 缓存.两级缓存 spring cache:主要包含spring cache定义的接口方法说明和注解中的属性说明 spring boot

  • SpringBoot加入Guava Cache实现本地缓存代码实例

    这篇文章主要介绍了SpringBoot加入Guava Cache实现本地缓存代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在pom.xml中加入guava依赖 <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version>

  • SpringBoot中Shiro缓存使用Redis、Ehcache的方法

    SpringBoot 中配置redis作为session 缓存器. 让shiro引用 本文是建立在你是使用这shiro基础之上的补充内容 第一种:Redis缓存,将数据存储到redis 并且开启session存入redis中. 引入pom <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifac

  • 如何扩展Spring Cache实现支持多级缓存

    为什么多级缓存 缓存的引入是现在大部分系统所必须考虑的 redis 作为常用中间件,虽然我们一般业务系统(毕竟业务量有限)不会遇到如下图 在随着 data-size 的增大和数据结构的复杂的造成性能下降,但网络 IO 消耗会成为整个调用链路中不可忽视的部分.尤其在 微服务架构中,一次调用往往会涉及多次调用 例如pig oauth2.0 的 client 认证 Caffeine 来自未来的本地内存缓存,性能比如常见的内存缓存实现性能高出不少详细对比. 综合所述:我们需要构建 L1 Caffeine

  • Spring Cache和EhCache实现缓存管理方式

    目录 1.认识 Spring Cache 2.认识 EhCache 3.创建SpringBoot与MyBatis的整合项目 3.1 创建数据表 3.2 创建项目 4.配置EhCache缓存管理器 4.1 创建 ehcache.xml 配置文件 4.2 配置缓存管理器 4.3 开启缓存功能 5.使用EhCache实现缓存管理 5.1 创建实体类(Entity层) 5.2 数据库映射层(Mapper层) 5.3 业务逻辑层(Service层) 5.4 控制器方法(Controller层) 5.5 显

  • springboot集成spring cache缓存示例代码

    本文介绍如何在springboot中使用默认的spring cache, 声明式缓存 Spring 定义 CacheManager 和 Cache 接口用来统一不同的缓存技术.例如 JCache. EhCache. Hazelcast. Guava. Redis 等.在使用 Spring 集成 Cache 的时候,我们需要注册实现的 CacheManager 的 Bean. Spring Boot 为我们自动配置了 JcacheCacheConfiguration. EhCacheCacheCo

  • 深入理解Spring Cache框架

    本文是缓存系列第三篇,前两篇分别介绍了 Guava 和 JetCache. 前两篇我们讲了 Guava 和 JetCache,它们都是缓存的具体实现,今天给大家分析一下 Spring 框架本身对这些缓存具体实现的支持和融合.使用 Spring Cache 将大大的减少我们的Spring项目中缓存使用的复杂度,提高代码可读性.本文将从以下几个方面来认识Spring Cache框架. 背景 SpringCache 产生的背景其实与Spring产生的背景有点类似.由于 Java EE 系统框架臃肿.低

  • Spring Boot集成Redis实现缓存机制(从零开始学Spring Boot)

    本文章牵涉到的技术点比较多:spring Data JPA.Redis.Spring MVC,Spirng Cache,所以在看这篇文章的时候,需要对以上这些技术点有一定的了解或者也可以先看看这篇文章,针对文章中实际的技术点在进一步了解(注意,您需要自己下载Redis Server到您的本地,所以确保您本地的Redis可用,这里还使用了MySQL数据库,当然你也可以内存数据库进行测试).这篇文章会提供对应的Eclipse代码示例,具体大体的分如下几个步骤: (1)新建Java Maven Pro

  • Spring Cache的基本使用与实现原理详解

    Spring Cache 概念 从Spring 3.1版本开始,提供了一种透明的方式来为现有的Spring 应用添加cache,使用起来就像@Transaction一样.在应用层面与后端存储之间,提供了一层抽象,这层抽象目的在于封装各种可插拔的后端存储( Ehcache Guava Redis),最小化因为缓存给现有业务代码带来的侵入. Spring 的缓存技术还具备相当的灵活性.不仅能够使用 SpEL(Spring Expression Language)来定义缓存的 key 和各种 cond

  • Spring Cache整合Redis实现方法详解

    导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>

随机推荐