从零开发短视频电商 缓存Cache实战SimpleCaffeine和Redis多缓存管理器
Posted lakernote
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开发短视频电商 缓存Cache实战SimpleCaffeine和Redis多缓存管理器相关的知识,希望对你有一定的参考价值。
文章目录
SpringBoot集成缓存Cache
1.增加pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
支持以下几种缓存实现,默认自动根据代码依赖判断使用哪种实现,也可以在配置文件中强制指定使用哪种缓存实现。
当然也可以自实现 cacheManager,随便参照simple或者redis如何实现的即可。
2.启用缓存功能
在配置类中添加@EnableCaching
来启用缓存功能
@Configuration
@EnableCaching
public class CachingConfig
默认情况下,如果我们没有明确指定任何其他缓存,会自动根据依赖环境判断,如果上图的依赖都没有,它默认使用ConcurrentHashMap作为底层缓存。即
Simple
的类型,参考代码SimpleCacheConfiguration.java
、ConcurrentMapCacheManager.java
也可以通过配置文件直接指定:
spring:
cache:
type: simple
常见缓存操作
- 使用注解@
Cacheable
等。 - 使用缓存管理器
CacheManager
。
缓存
@Cacheable(value = "user", key = "#id", unless="#result == null")
public User getUser(long id) ...
cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存。必填
key:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合。
keyGenerator:key的生成器;可以自己指定key的生成器的组件id,key/keyGenerator:二选一使用。
cacheManager:当自己配置了CacheManager后可以指定使用哪个缓存管理器,默认使用的是Springboot自动配置的缓存管理器;或者cacheResolver指定获取解析器。
condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存.
condition="#userName.length()>2"
。unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断。
sync:是否开启同步功能,默认不开启。开启后unless属性将不能使用。
getUser()
将首先检查缓存是否存在,不存在则调用实际的方法,最后缓存结果;存在则直接返回缓存结果,跳过调用实际方法。
或者使用缓存管理器。
@Service
public class UserService
@Autowired
CacheManager cacheManager;
public User getUser(long id)
if(cacheManager.containsKey(id))
return cacheManager.get(id);
// lookup address, cache result, and return it
清除缓存
指定删除一个或多个或所有值,以便可以再次将新值加载到缓存中。
@CacheEvict(value="user", allEntries=true)
public User updateUser(long id) ...
allEntries
:是否删除所有值。默认false,默认情况下,只删除关联键下的值。注意,不允许将这个参数设置为true并指定一个键。
beforeInvocation
:默认false,是否应该在调用方法之前发生驱逐。将此属性设置为true,将导致驱逐发生,而不考虑方法的结果(即,是否抛出异常)。 默认值为false,意味着缓存回收操作将在被建议的方法被成功调用后发生(也就是说,只有在调用没有抛出异常的情况下)。
或者使用缓存管理器
@Autowired
CacheManager cacheManager;
public void evictSingleCacheValue(String cacheName, String cacheKey)
cacheManager.getCache(cacheName).evict(cacheKey);
public void evictAllCacheValues(String cacheName)
cacheManager.getCache(cacheName).clear();
public void evictAllCaches()
cacheManager.getCacheNames().stream()
.forEach(cacheName -> cacheManager.getCache(cacheName).clear());
更新缓存
实际我们不用这个,高并发下会导致更新丢失问题或者锁问题。这里仅做介绍哈
@CachePut(value="addresses")
public String getAddress(Customer customer) ...
组合缓存
@Caching(evict =
@CacheEvict("addresses"),
@CacheEvict(value="directory", key="#customer.name") )
public String getAddress(Customer customer) ...
可以使用@Caching
将多个缓存注解组合,使用它来实现我们自己的自定义缓存逻辑。
类缓存配置
使用@CacheConfig
注解,我们可以在类级别将一些缓存配置简化到一个地方。
@CacheConfig(cacheNames="addresses")
public class CustomerDataService
@Cacheable
public String getAddress(Customer customer) ...
SpEL上下文数据
Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:
名称 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root对象 | 当前被调用的方法名 | #root.methodname |
method | root对象 | 当前被调用的方法 | #root.method.name |
target | root对象 | 当前被调用的目标对象实例 | #root.target |
targetClass | root对象 | 当前被调用的目标对象的类 | #root.targetClass |
args | root对象 | 当前被调用的方法的参数列表 | #root.args[0] |
caches | root对象 | 当前方法调用使用的缓存列表 | #root.caches[0].name |
Argument Name | 执行上下文 | 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 | #artsian.id |
result | 执行上下文 | 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) | #result |
注意:
1.当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。 如
@Cacheable(key = "targetClass + methodName +#p0")
2.使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。 如:
@Cacheable(value="users", key="#id")
@Cacheable(value="users", key="#p0")
SpEL提供了多种运算符
类型 | 运算符 |
---|---|
关系 | <,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne |
算术 | +,- ,* ,/,%,^ |
逻辑 | &&,||,!,and,or,not,between,instanceof |
条件 | ?: (ternary),?: (elvis) |
正则表达式 | matches |
其他类型 | ?.,?[…],![…],1,$[…] |
Cache实现之Redis缓存管理器
文档:https://spring.io/projects/spring-data-redis
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
需要引入commons-pool2作为连接池 必须!!!
<!--spring2.0集成redis所需common-pool2-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
版本依赖如下:
使用的JavaRedis客户端是Lettuce因为 Spring Boot 默认使用它。
在application.yaml
文件中配置属性:
spring:
cache:
# 指定使用redis
type: redis
redis:
# reids的连接ip
host: 127.0.0.1
port: 6379
password: laker123
# Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0
database: 0
# 连接超时时间(毫秒)
timeout: 10000ms
# redis client配置,使用lettuce
lettuce:
pool:
# 连接池中的最小空闲连接 默认 0
min-idle: 0
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
max-wait: 1000ms
# 连接池最大连接数(使用负值表示没有限制) 默认 8
max-active: 8
# 连接池中的最大空闲连接 默认 8
max-idle: 8
注意注意注意!!!,这里redis默认的序列化为JDK序列化,所以上面的User
实体类一定要实现序列化public class User implements Serializable
,否则会报java.io.NotSerializableException
异常。
@Cacheable(value = "user", key = "#id", unless="#result == null")
public User getUser(long id) ...
getUser(1)
其对应结果如下图:
这里我们后边会修改其默认的序列化为json格式。
框架会自动生成一个RedisTemplate
实例 ,我们也可以直接使用RedisTemplate
操作缓存。
@Autowired
private RedisTemplate redisTemplate;
public void save(User user)
redisTemplate.opsForValue().set(user.getId(), user);
public User findById(Long id)
return (User)redisTemplate.opsForValue().get(id);
RedisTemplate 是线程安全的哦
默认情况下,Lettuce 会为我们管理序列化和反序列化,我们来自定义配置RedisTemplate。
@Configuration
@EnableCaching
public class CachingConfig
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory)
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setKeySerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
return redisTemplate;
Jackson2JsonRedisSerializer和GenericJackson2JsonRedisSerializer的区别
-
使用Jackson2JsonRedisSerializer需要指明序列化的类Class,可以使用Obejct.class
-
使用GenericJacksonRedisSerializer比Jackson2JsonRedisSerializer效率低,占用内存高。
-
GenericJacksonRedisSerializer反序列化带泛型的数组类会报转换异常,解决办法存储以JSON字符串存储。
-
GenericJacksonRedisSerializer和Jackson2JsonRedisSerializer都是以JSON格式去存储数据,都可以作为Redis的序列化方式。
来自:https://blog.csdn.net/bai_bug/article/details/81222519
redisTemplate.opsForValue().set(id,user);
回归Cache中RedisCacheManager
自定义配置如下:
方式一 RedisCacheConfiguration
@Bean
public RedisCacheConfiguration cacheConfiguration()
return RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(60))
.disableCachingNullValues()
.serializeValuesWith(SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
方式二 RedisCacheManagerBuilderCustomizer
@Bean
public RedisCacheManagerBuilderCustomizer redisCacheManagerBuilderCustomizer()
return (builder) -> builder
.withCacheConfiguration("itemCache",
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)))
.withCacheConfiguration("customerCache",
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(5)));
分别为itemCache
和customerCache
配置了 10 分钟和 5 分钟的 TTL 值。
方式三 CachingConfigurerSupport
扩展CachingConfigurerSupport
类并覆盖cacheManager
() 方法。此方法返回一个 bean,它将成为我们应用程序的默认缓存管理器:
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
@Override
public CacheManager cacheManager() ...
@Override
public CacheResolver cacheResolver() ...
@Override
public KeyGenerator keyGenerator() ...
@Override
public CacheErrorHandler errorHandler() ...
配置多个缓存管理器并动态切换
在某些情况下,我们可能需要在应用程序中使用多个缓存管理器。
配置类中创建两个缓存管理器 bean。配置其中的一个 bean为@Primary
@Configuration
@EnableCaching
public class MultipleCacheManagerConfig
@Bean
@Primary
public CacheManager cacheManager()
CaffeineCacheManager cacheManager = new CaffeineCacheManager("customers", "orders");
cacheManager.setCaffeine(Caffeine.newBuilder()
.initialCapacity(200)
.maximumSize(500)
.weakKeys()
.recordStats());
return cacheManager;
@Bean
public CacheManager alternateCacheManager()
return new ConcurrentMapCacheManager("customerOrders", "orderprice");
现在,Spring Boot 将使用CaffeineCacheManager
作为所有缓存方法的默认值,直到我们为一个方法明确指定了AlternateCacheManager
@Cacheable(cacheNames = "customers")
public Customer getCustomerDetail(Integer customerId)
return customerDetailRepository.getCustomerDetail(customerId);
@Cacheable(cacheNames = "customerOrders", cacheManager = "alternateCacheManager")
public List<Order> getCustomerOrders(Integer customerId)
return customerDetailRepository.getCustomerOrders(customerId);
整体示例
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory)
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
redisTemplate.setKeySerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
return redisTemplate;
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory)
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1)) //设置所有缓存有效时间 为 1个小时。
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer))
.disableCachingNullValues(); // 禁用缓存空值.
RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();
return cacheManager;
Cache实现之Caffeine缓存管理器
CaffeineCacheManager
由spring-boot-starter-cache starter 提供。如果依赖存在Caffeine,它将由 Spring 自动配置, Caffeine是一个用 Java 8 编写的缓存库。
增加依赖Caffeine
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
可以自己定制配置,控制缓存行为的主要配置,例如过期、缓存大小限制等
@Bean
public Caffeine caffeineConfig()
return Caffeine.newBuilder()
.expireAfterWrite(60, TimeUnit.MINUTES);
@Bean
public CacheManager cacheManager(Caffeine caffeine)
CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();
caffeineCacheManager.setCaffeine(caffeine);
return caffeineCacheManager;
参考:
- https://www.baeldung.com/spring-boot-redis-cache
… ↩︎
以上是关于从零开发短视频电商 缓存Cache实战SimpleCaffeine和Redis多缓存管理器的主要内容,如果未能解决你的问题,请参考以下文章
亿级流量电商系统实战视频缓存架构+高可用服务架构+微服务架构
呆萌短视频app定制开发:抖音已经开启“短视频+电商”新模式已经开始了???
动态PHP电商网站伪静态的Nginx反向代理Cache缓存终极设置
亿级流量电商详情页系统实战-缓存架构+高可用服务架构+微服务架构第二版视频教程
2018最新技术Java架构师高并发高性能高可用分布式集群电商缓存性能调优设计模式项目实战视频教程
微软ASP.NET 电商网站开发实战 MVC6 +HTML5 +WCF+WebAPI+NoSQL+mongoDB+Redis+Core视频 代码 面试题