0


从零开发短视频电商 缓存Cache实战Simple、Caffeine和Redis多缓存管理器

文章目录

SpringBoot集成缓存Cache

文档:https://docs.spring.io/spring-boot/docs/2.3.12.RELEASE/reference/html/spring-boot-features.html#boot-features-caching

1.增加pom依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency>

支持以下几种缓存实现,默认自动根据代码依赖判断使用哪种实现,也可以在配置文件中强制指定使用哪种缓存实现。

当然也可以自实现 cacheManager,随便参照simple或者redis如何实现的即可。

2.启用缓存功能

在配置类中添加@

EnableCaching

来启用缓存功能

@Configuration@EnableCachingpublicclassCachingConfig{}

默认情况下,如果我们没有明确指定任何其他缓存,会自动根据依赖环境判断,如果上图的依赖都没有,它默认使用ConcurrentHashMap作为底层缓存。即

Simple

的类型,参考代码

SimpleCacheConfiguration.java

ConcurrentMapCacheManager.java

也可以通过配置文件直接指定:

spring:cache:type: simple

常见缓存操作

  • 使用注解@Cacheable等。
  • 使用缓存管理器CacheManager

缓存

@Cacheable(value ="user", key ="#id", unless="#result == null")publicUsergetUser(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()

将首先检查缓存是否存在,不存在则调用实际的方法,最后缓存结果;存在则直接返回缓存结果,跳过调用实际方法。

或者使用缓存管理器。

@ServicepublicclassUserService{@AutowiredCacheManager cacheManager;publicUsergetUser(long id){if(cacheManager.containsKey(id)){return cacheManager.get(id);}// lookup address, cache result, and return it}}

清除缓存

指定删除一个或多个或所有值,以便可以再次将新值加载到缓存中。

@CacheEvict(value="user", allEntries=true)publicUserupdateUser(long id){...}
allEntries

:是否删除所有值。默认false,默认情况下,只删除关联键下的值。注意,不允许将这个参数设置为true并指定一个键。

beforeInvocation

:默认false,是否应该在调用方法之前发生驱逐。将此属性设置为true,将导致驱逐发生,而不考虑方法的结果(即,是否抛出异常)。

​ 默认值为false,意味着缓存回收操作将在被建议的方法被成功调用后发生(也就是说,只有在调用没有抛出异常的情况下)。

或者使用缓存管理器

@AutowiredCacheManager cacheManager;publicvoidevictSingleCacheValue(String cacheName,String cacheKey){
    cacheManager.getCache(cacheName).evict(cacheKey);}publicvoidevictAllCacheValues(String cacheName){
    cacheManager.getCache(cacheName).clear();}publicvoidevictAllCaches(){
    cacheManager.getCacheNames().stream().forEach(cacheName -> cacheManager.getCache(cacheName).clear());}

更新缓存

实际我们不用这个,高并发下会导致更新丢失问题或者锁问题。这里仅做介绍哈

@CachePut(value="addresses")publicStringgetAddress(Customer customer){...}

组合缓存

@Caching(evict ={@CacheEvict("addresses"),@CacheEvict(value="directory", key="#customer.name")})publicStringgetAddress(Customer customer){...}

可以使用@

Caching

多个缓存注解组合,使用它来实现我们自己的自定义缓存逻辑。

类缓存配置

使用@

CacheConfig

注解,我们可以在类级别将一些缓存配置简化到一个地方

@CacheConfig(cacheNames={"addresses"})publicclassCustomerDataService{@CacheablepublicStringgetAddress(Customer customer){...}

SpEL上下文数据

Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:
名称位置描述示例methodNameroot对象当前被调用的方法名

#root.methodname

methodroot对象当前被调用的方法

#root.method.name

targetroot对象当前被调用的目标对象实例

#root.target

targetClassroot对象当前被调用的目标对象的类

#root.targetClass

argsroot对象当前被调用的方法的参数列表

#root.args[0]

cachesroot对象当前方法调用使用的缓存列表

#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其他类型?.,?[…],![…],,$[…]

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的连接iphost: 127.0.0.1
    port:6379password: laker123
    # Redis默认情况下有16个分片,这里配置具体使用的分片,默认是0database:0# 连接超时时间(毫秒)timeout: 10000ms
    #  redis client配置,使用lettucelettuce:pool:# 连接池中的最小空闲连接 默认 0min-idle:0# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1max-wait: 1000ms
        # 连接池最大连接数(使用负值表示没有限制) 默认 8max-active:8# 连接池中的最大空闲连接 默认 8max-idle:8

注意注意注意!!!,这里redis默认的序列化为JDK序列化,所以上面的

User

实体类一定要实现序列化

public class User implements Serializable

,否则会报

java.io.NotSerializableException

异常。

@Cacheable(value ="user", key ="#id", unless="#result == null")publicUsergetUser(long id){...}
getUser(1)

其对应结果如下图:

这里我们后边会修改其默认的序列化为json格式。

框架会自动生成一个

RedisTemplate

实例 ,我们也可以直接使用

RedisTemplate

操作缓存。

@AutowiredprivateRedisTemplate redisTemplate;publicvoidsave(User user){
    redisTemplate.opsForValue().set(user.getId(), user);}publicUserfindById(Long id){return(User)redisTemplate.opsForValue().get(id);}

RedisTemplate****是线程安全的哦

默认情况下,Lettuce 会为我们管理序列化和反序列化,我们来自定义配置RedisTemplate

@Configuration@EnableCachingpublicclassCachingConfig{@BeanpublicRedisTemplate<Object,Object>redisTemplate(RedisConnectionFactory factory){RedisTemplate<Object,Object> redisTemplate =newRedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer =newGenericJackson2JsonRedisSerializer();
        redisTemplate.setKeySerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(newStringRedisSerializer());
        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

@BeanpublicRedisCacheConfigurationcacheConfiguration(){returnRedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(60)).disableCachingNullValues().serializeValuesWith(SerializationPair.fromSerializer(newGenericJackson2JsonRedisSerializer()));}

方式二 RedisCacheManagerBuilderCustomizer

@BeanpublicRedisCacheManagerBuilderCustomizerredisCacheManagerBuilderCustomizer(){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@EnableCachingpublicclassRedisConfigextendsCachingConfigurerSupport{@OverridepublicCacheManagercacheManager(){...}@OverridepublicCacheResolvercacheResolver(){...}@OverridepublicKeyGeneratorkeyGenerator(){...}@OverridepublicCacheErrorHandlererrorHandler(){...}}

配置多个缓存管理器并动态切换

在某些情况下,我们可能需要在应用程序中使用多个缓存管理器。

配置类中创建两个缓存管理器 bean。配置其中的一个 bean为@

Primary
@Configuration@EnableCachingpublicclassMultipleCacheManagerConfig{@Bean@PrimarypublicCacheManagercacheManager(){CaffeineCacheManager cacheManager =newCaffeineCacheManager("customers","orders");
        cacheManager.setCaffeine(Caffeine.newBuilder().initialCapacity(200).maximumSize(500).weakKeys().recordStats());return cacheManager;}@BeanpublicCacheManageralternateCacheManager(){returnnewConcurrentMapCacheManager("customerOrders","orderprice");}}

现在,Spring Boot 将使用

CaffeineCacheManager

作为所有缓存方法的默认值,直到我们为一个方法明确指定了

AlternateCacheManager
@Cacheable(cacheNames ="customers")publicCustomergetCustomerDetail(Integer customerId){return customerDetailRepository.getCustomerDetail(customerId);}@Cacheable(cacheNames ="customerOrders", cacheManager ="alternateCacheManager")publicList<Order>getCustomerOrders(Integer customerId){return customerDetailRepository.getCustomerOrders(customerId);}

整体示例

@Configuration@EnableCachingpublicclassRedisConfigextendsCachingConfigurerSupport{@BeanpublicRedisTemplate<Object,Object>redisTemplate(RedisConnectionFactory factory){RedisTemplate<Object,Object> redisTemplate =newRedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer =newGenericJackson2JsonRedisSerializer();
        redisTemplate.setKeySerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(newStringRedisSerializer());
        redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);return redisTemplate;}@BeanpublicCacheManagercacheManager(RedisConnectionFactory factory){RedisSerializer<String> redisSerializer =newStringRedisSerializer();GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer =newGenericJackson2JsonRedisSerializer();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>

可以自己定制配置,控制缓存行为的主要配置,例如过期、缓存大小限制等

@BeanpublicCaffeinecaffeineConfig(){returnCaffeine.newBuilder().expireAfterWrite(60,TimeUnit.MINUTES);}@BeanpublicCacheManagercacheManager(Caffeine caffeine){CaffeineCacheManager caffeineCacheManager =newCaffeineCacheManager();
    caffeineCacheManager.setCaffeine(caffeine);return caffeineCacheManager;}

参考:


标签: 缓存 redis 音视频

本文转载自: https://blog.csdn.net/abu935009066/article/details/122369133
版权归原作者 lakernote 所有, 如有侵权,请联系我们删除。

“从零开发短视频电商 缓存Cache实战Simple、Caffeine和Redis多缓存管理器”的评论:

还没有评论