系统为了提高数据访问速度,先将数据加载到redis缓存中,但是每次从缓存获取数据,要通过网络访问才能获取,效率还是不够逆天快。如果访问量很大,并发很高,性能不够快不说,还容易造成reids负载过高,redis的主机出现各种物理故障。因此,可以在redis前增加本地一级缓存,本地一级缓存和系统应用在同一个JVM内,这样速度最快,redis退居二线当作二级缓存。每次请求先从一级缓存读取数据,一级缓存没有数据,再从二级缓存读取,并同步到一级缓存里,通过redis的消息发布订阅通知其他client机器更新缓存。这同CPU的一级缓存,二级缓存是一个道理。
本文并不涉及spring boot cache的详细使用介绍,需要熟悉spring boot cache基本使用。对于spring boot cache详细使用介绍请上度娘。扩展比较简单,闲话少说,直接上代码。
pom.xml,加入spring boot cache依赖,本地一级缓存使用ehcache3,二级缓存使用redis
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.M7</version> </parent> <groupId>springboot</groupId> <artifactId>springboot-2level-cache</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>javax.cache</groupId> <artifactId>cache-api</artifactId> </dependency> <dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency> <!-- Only used to expose cache metrics --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- Test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project>
appliaction.properties里配置属性
spring.cache.type=redis spring.ext.cache.name=countries spring.ext.cache.redis.topic=cache
本地一级缓存使用ehcache3,ehcache3.xml配置
<config xmlns='http://www.ehcache.org/v3' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jsr107="http://www.ehcache.org/v3/jsr107" xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd"> <cache alias="countries"> <expiry> <ttl unit="seconds">600</ttl> </expiry> <heap unit="entries">200</heap> <jsr107:mbeans enable-statistics="true"/> </cache> </config>
创建LocalRedisCache ,继承RedisCache,这里是实现redis一二级分布式缓存的核心,重载RedisCache的get、put、evict、clean等方法,增加本地一级缓存的读写,增加pub方法,通过redis发布缓存更新消息通知其他 redis clent 更新缓存,增加sub方法,获取缓存更新消息更新缓存。
package org.springframework.data.redis.cache; import org.springframework.cache.Cache; import org.springframework.data.redis.core.RedisOperations; import org.springframework.util.Assert; import java.util.concurrent.Callable; /** * extends RedisCache,增加本地一级缓存,redis作为二级缓存 */ public class LocalRedisCache extends RedisCache { private final Cache localCache;//本地一级缓存 private final RedisOperations redisOperations;//配合topicName,发布缓存更新消息 private final String topicName;//redis topic ,发布缓存更新消息通知其他client更新缓存 /** * Create new {@link RedisCache}. * * @param name must not be {@literal null}. * @param cacheWriter must not be {@literal null}. * @param cacheConfig must not be {@literal null}. * @param localCache must not be {@literal null}. * @param redisOperations must not be {@literal null}. * @param topicName must not be {@literal null}. */ protected LocalRedisCache(String name, RedisCacheWriter cacheWriter, RedisCacheConfiguration cacheConfig, Cache localCache, RedisOperations redisOperations, String topicName) { super(name, cacheWriter, cacheConfig); Assert.notNull(localCache, "localCache must not be null!"); Assert.notNull(redisOperations, "redisOperations must not be null!"); Assert.hasText(topicName, "topicName must not be empty!"); this.localCache = localCache; this.redisOperations = redisOperations; this.topicName = topicName; } @Override public synchronized <T> T get(Object key, Callable<T> valueLoader) { //先读取本地一级缓存 T value = localCache.get(key, valueLoader); if (value == null) { //本地一级缓存不存在,读取redis二级缓存 value = super.get(key, valueLoader); if (value != null) { //redis二级缓存存在,存入本地一级缓存 localCache.put(key, value); //发布缓存更新消息通知其他client更新缓存 pub(new UpdateMessage(key, value, UpdateMessage.Type.PUT)); } } return value; } @Override public void put(Object key, Object value) { super.put(key, value); localCache.put(key, value); pub(new UpdateMessage(key, value, UpdateMessage.Type.PUT)); } @Override public ValueWrapper putIfAbsent(Object key, Object value) { ValueWrapper vw1 = localCache.putIfAbsent(key, value); ValueWrapper vw2 = super.putIfAbsent(key, value); pub(new UpdateMessage(key, value, UpdateMessage.Type.PUTIFABSENT)); return vw1 == null ? vw2 : vw1; } @Override public void evict(Object key) { localCache.evict(key); super.evict(key); pub(new UpdateMessage(key, UpdateMessage.Type.REMOVE)); } @Override public void clear() { localCache.clear(); super.clear(); pub(new UpdateMessage(UpdateMessage.Type.CLEAN)); } @Override public ValueWrapper get(Object key) { ValueWrapper valueWrapper = localCache.get(key); if (valueWrapper == null) { valueWrapper = super.get(key); if (valueWrapper != null) { localCache.put(key, valueWrapper.get()); pub(new UpdateMessage(key, valueWrapper.get(), UpdateMessage.Type.PUT)); } } return valueWrapper; } @Override public <T> T get(Object key, Class<T> type) { T value = localCache.get(key, type); if (value == null) { value = super.get(key, type); if (value != null) { localCache.put(key, value); pub(new UpdateMessage(key, value, UpdateMessage.Type.PUT)); } } return value; } /** * 更新缓存 * * @param updateMessage */ public void sub(final UpdateMessage updateMessage) { if (updateMessage.getType() == UpdateMessage.Type.CLEAN) { //清除所有缓存 localCache.clear(); super.clear(); } else if (updateMessage.getType() == UpdateMessage.Type.PUT) { //更新缓存 localCache.put(updateMessage.getKey(), updateMessage.getValue()); super.put(updateMessage.getKey(), updateMessage.getValue()); } else if (updateMessage.getType() == UpdateMessage.Type.PUTIFABSENT) { //更新缓存 localCache.putIfAbsent(updateMessage.getKey(), updateMessage.getValue()); super.putIfAbsent(updateMessage.getKey(), updateMessage.getValue()); } else if (updateMessage.getType() == UpdateMessage.Type.REMOVE) { //删除缓存 localCache.evict(updateMessage.getKey()); super.evict(updateMessage.getKey()); } } /** * 通知其他 redis clent 更新缓存 * * @param message */ private void pub(final UpdateMessage message) { this.redisOperations.convertAndSend(topicName, message); } }
创建新的缓存管理器,命名为LocalRedisCacheManager,继承了Spring Boot的RedisCacheManager,重载createRedisCache方法。
package org.springframework.data.redis.cache; import org.springframework.cache.Cache; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisOperations; import org.springframework.util.Assert; import java.util.Map; public class LocalRedisCacheManager extends RedisCacheManager { private final RedisConnectionFactory connectionFactory; private final Cache localCache; private final RedisOperations redisOperations; private final String topicName; public LocalRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, RedisConnectionFactory connectionFactory, Cache localCache, RedisOperations redisOperations, String topicName) { super(cacheWriter, defaultCacheConfiguration); this.connectionFactory = connectionFactory; this.localCache = localCache; this.redisOperations = redisOperations; this.topicName = topicName; check(); } public LocalRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, RedisConnectionFactory connectionFactory, Cache localCache, RedisOperations redisOperations, String topicName, String... initialCacheNames) { super(cacheWriter, defaultCacheConfiguration, initialCacheNames); this.connectionFactory = connectionFactory; this.localCache = localCache; this.redisOperations = redisOperations; this.topicName = topicName; check(); } public LocalRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration, Map<String, RedisCacheConfiguration> initialCacheConfigurations, RedisConnectionFactory connectionFactory, Cache localCache, RedisOperations redisOperations, String topicName) { super(cacheWriter, defaultCacheConfiguration, initialCacheConfigurations); this.connectionFactory = connectionFactory; this.localCache = localCache; this.redisOperations = redisOperations; this.topicName = topicName; check(); } @Override protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) { return new LocalRedisCache(name, new DefaultRedisCacheWriter(connectionFactory), cacheConfig != null ? cacheConfig : RedisCacheConfiguration.defaultCacheConfig(), localCache, redisOperations, topicName); } public static LocalRedisCacheManager create(RedisConnectionFactory connectionFactory, Cache localCache, RedisOperations redisOperations, String topicName) { Assert.notNull(localCache, "localCache must not be null"); Assert.notNull(connectionFactory, "connectionFactory must not be null"); Assert.notNull(redisOperations, "redisOperations must not be null"); Assert.notNull(topicName, "topicName must not be null"); return new LocalRedisCacheManager(new DefaultRedisCacheWriter(connectionFactory), RedisCacheConfiguration.defaultCacheConfig(), connectionFactory, localCache, redisOperations, topicName); } private void check() { Assert.notNull(localCache, "localCache must not be null"); Assert.notNull(connectionFactory, "connectionFactory must not be null"); Assert.notNull(redisOperations, "redisOperations must not be null"); Assert.notNull(topicName, "topicName must not be null"); } }
缓存配置实现,需要使用注解 @EnableCaching 打开缓存功能
package org.springframework.data.redis.cache; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.jcache.JCacheCacheManager; import org.springframework.cache.jcache.JCacheManagerFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.listener.PatternTopic; import org.springframework.data.redis.listener.RedisMessageListenerContainer; import org.springframework.data.redis.listener.adapter.MessageListenerAdapter; import java.io.IOException; @Configuration @EnableCaching public class CacheConfig { @Value("${spring.ext.cache.name:countries}") private String localCacheName; @Value("${spring.ext.cache.redis.topic:cache}") private String topicName; @Bean public CacheManager jCacheCacheManager() { return new JCacheCacheManager(jCacheManagerFactoryBean().getObject()); } @Bean public JCacheManagerFactoryBean jCacheManagerFactoryBean() { JCacheManagerFactoryBean jCacheManagerFactoryBean = new JCacheManagerFactoryBean(); Resource resource = new ClassPathResource("ehcache3.xml"); try { jCacheManagerFactoryBean.setCacheManagerUri(resource.getURI()); } catch (IOException e) { throw new RuntimeException(e); } return jCacheManagerFactoryBean; } @Bean public JedisConnectionFactory jedisConnectionFactory() { JedisConnectionFactory factory = new JedisConnectionFactory(); return factory; } @Bean public RedisTemplate redisTemplate(JedisConnectionFactory jedisConnectionFactory) { RedisTemplate redisTemplate = new RedisTemplate(); redisTemplate.setConnectionFactory(jedisConnectionFactory); return redisTemplate; } @Primary @Bean public RedisCacheManager redisCacheManager(JedisConnectionFactory jedisConnectionFactory, RedisTemplate redisTemplate) { RedisCacheManager cacheManager = LocalRedisCacheManager.create(jedisConnectionFactory, jCacheCacheManager().getCache(localCacheName), redisTemplate, topicName); return cacheManager; } @Bean public RedisMessageListenerContainer container(JedisConnectionFactory jedisConnectionFactory, MessageListenerAdapter listenerAdapter) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(jedisConnectionFactory); container.addMessageListener(listenerAdapter, new PatternTopic(topicName)); return container; } @Bean public MessageListenerAdapter listenerAdapter(LocalRedisCacheManager cacheManager, RedisTemplate redisTemplate) { return new MessageListenerAdapter(new MessageListener() { @Override public void onMessage(Message message, byte[] pattern) { byte[] channel = message.getChannel(); byte[] body = message.getBody(); String cacheName = (String) redisTemplate.getStringSerializer().deserialize(channel); LocalRedisCache cache = (LocalRedisCache) cacheManager.getCache(cacheName); if (cache == null) { return; } UpdateMessage updateMessage = (UpdateMessage) redisTemplate.getValueSerializer().deserialize(body); cache.sub( updateMessage); } }); } }
到些,实现基本完成,其他相关简单代码,参考附件。
相关推荐
"Spring Boot+Spring Cache实现两级缓存(Redis+Caffeine)" 知识点一:缓存与两级缓存 缓存是将数据从读取较慢的介质上读取出来放到读取较快的介质上,如磁盘-->内存。平时我们会将数据存储到磁盘上,如:数据库。...
spring boot cache 整合 redis demo (内包含 redis windows 安装包,和redis desktop 桌面 管理工具)
Spring Boot 整合 Redis 实现 Shiro 的分布式 Session 共享 Shiro 是一个优秀的 Java 安全框架,提供了强大的身份验证、授权和会话管理功能。然而,在分布式架构中,Shiro 的会话管理机制需要进行特殊处理,以便...
4. **使用 Redis**:在 Spring Boot 应用中,可以使用 `@Cacheable`、`@CacheEvict` 和 `@CachePut` 等注解来实现方法级别的缓存操作。例如,为一个方法添加缓存: ```java @Service public class UserService { ...
Spring Boot项目利用Redis实现集中式缓存实例是实现高性能、可扩展、高可用性的关键技术之一。本文将介绍如何在Spring Boot项目中利用Redis实现集中式缓存实例,提高系统性能和可扩展性。 1. Spring Boot项目初始化...
"Spring Boot + Shiro + Redis 实现 Session 共享方案二" 1. 概述 本文档旨在介绍如何使用 Spring Boot、Shiro 和 Redis 实现分布式 session 共享,以解决 Web 应用程序的登录 session 统一问题。 2. 相关依赖 ...
本文将详细介绍Spring Boot整合Redis的完整步骤,包括Spring Boot对Redis的支持、添加依赖、配置RedisTemplate和StringRedisTemplate、使用Redis缓存等。 一、Spring Boot对Redis的支持 Spring Boot对Redis的支持...
Spring Cache是Spring框架中的一种缓存机制,它可以将缓存数据存储在Redis中。然而,在某些情况下,我们需要手动清理Redis缓存,以便释放内存空间或更新缓存数据。在本文中,我们将介绍如何使用Spring Cache手动清理...
通过Spring Boot整合Spring Cache及Redis,我们可以实现一个高效、灵活的缓存机制,提高应用程序的性能和效率。 知识点: 1. 如何安装Redis在Windows平台上 2. 如何在Spring Boot工程中配置Redis 3. 如何使用...
spring boot集成redis做为通用缓存的实战demo,帮助大家彻底掌握s-cache-practice
在Spring Boot中,RedisCacheManager是Spring Cache中的一种CacheManager实现,负责缓存数据的存储和读取。RedisCacheManager可以自动提供一个Bean,例如: ``` @Configuration @ConditionalOnClass...
1.38 Spring Boot集成Redis实现缓存机制 1.39 Spring Boot Cache理论篇 1.40 Spring Boot集成EHCache实现缓存机制 1.41 Spring Boot分布式Session状态保存Redis 1.42 Spring Boot Shiro权限管理 1.43 Spring Boot ...
包括使用Spring实现RESTful架构,在Spring Boot框架下使用Redis、MongoDB、ZooKeeper、Elasticsearch等流行技术,使用Spring Session实现系统水平扩展,使用Spring Cache提高系统性能。 2.面对系统模块增加,性能和...
另一方面,当系统模块增加,性能和吞吐量要求增加时,如何平滑地用Spring Boot实现分布式架构,也会在本书后半部分介绍,包括使用Spring实现RESTful架构,在Spring Boot框架下使用Redis、MongoDB、ZooKeeper、...
在Spring Boot项目中,使用Redis作为缓存的存储媒介,可以充分发挥Redis的高性能和高可扩展性。 在本案例中,我们将使用Spring Cache和RedisCache来实现缓存机制。首先,需要在pom.xml文件中添加相应的依赖项,包括...
使用redis和springcache实现数据缓存
Spring Boot 使用 Redis 集群替换 MyBatis 二级缓存 Spring Boot 作为当前流行的 Java Web 框架,提供了许多便捷的功能来快速开发 Web 应用程序。其中,缓存机制是 Spring Boot 中一个非常重要的组件,缓存机制...
MyBatis Plus 是一个基于 MyBatis 的增强工具,提供了许多实用的功能,其中之一就是支持使用 Redis 作为二级缓存。本文将详细介绍如何使用 MyBatis Plus 将 Redis 作为二级缓存。 为什么使用 Redis 作为二级缓存 ...
基于 SpringBoot 从0搭建一个企业级开发项目,基于SpringBoot 的项目,并集成MyBatis-Plus、Redis、Druid、Logback ,并使用 Redis 配置 MyBatis 二级缓存。