一、前言

最近在项目中发现了用到caffeine+redis的地方,感觉写的很好,因此记一波笔记。

caffeine是spring的cache,保存在项目本地。
redis是分布式缓存,保存在redis服务器。

查询数据时,先到caffeine中查,如果没有则查redis,如果还没有再查数据库。

二、主要流程代码样例

1.查询数据库的Java类ExamServiceImpl.java

@Service
public class ExamServiceImpl implements ExamService {

@Autowired
private ExamRepository examRepository;

@Override
public Exam get(Long id) {
  com.google.common.base.Preconditions.checkNotNull(id);
  return examRepository.selectById(id);
}

}

2.查询Redis的Java类ExamServiceProxy.java(继承ExamServiceImpl)

@Service
@Primary
public class ExamServiceProxy extends ExamServiceImpl {
  @Autowired
  private StringRedisTemplate redis;
  @Autowired
  ObjectMapper objectMapper;
  
  @Override
  public Exam get(Long id){
    com.google.common.base.Preconditions.checkNotNull(id);
    //从缓存中取value
    String value = redis.opsForValue().get(String.format("exam:%s",id));
    
    Exam exam = null;
    if(!StringUtils.isBlank(value)){
      try{
        //把value转换成对象
        exam = obejctMapper.readValue(value, Exam.class);
      }catch(Exception e){
        System.out.println(e.getMessage());
      }      
    }
    
    //如果缓存里没有或value错误
    if(exam == null){
      //查数据库
      exam = super.get(id);
      if(exam != null) {
        //如果查数据库有(此时redis为空或value错误),则存入redis
        String key = String.format("exam:%s",id);
        //把对象转换成value字符串
        try{
          String objToStr = objectMapper.writeValueAsString(exam);
          //存入redis,过期时间7天
          redis.opsForValue().set(key,objToStr,7,TimeUnit.DAYS);
        }catch(Exception e){
          System.out.println(e.getMessage());
        }
      }
    }
    //返回结果
    return exam;
  }
  
}

3.查询caffeine的Java类ExamServiceProxy2(继承ExamServiceProxy)

@Service
public class ExamServiceProxy2 extends ExamServiceProxy {
  @Autowired
  private Cache cache;
  @Override
  public Exam get(Long id){
    Preconditions.checkNotNull(id);
    String key = String.format("exam:%s",id);
    //查询caffeine,没有则使用super从redis查
    return cache.get(key, ()-> super.get(id));
  }

  //存caffeine的逻辑是单独写的(应该也能写到get逻辑里,不过没有写)
  @Override
  public List<Exam> listByIds(Collection<Long> ids){
    .....
    cache.put(String.format("exam:%s",exam.getId()),exam);
    .....
  }
}

4.使用样例(使用Qualifier指定即可)

@Autowired
@Qualifier("examServiceProxy2")
private ExamService examService;

三、总结

1.通过继承,实现了先查caffeine缓存、再查redis缓存、最后查数据库的逻辑结构。

2.注意Exam类需要implements Serializable;存入caffeine时,直接存入Exam对象即可;存入redis时,使用了ObjectMapper把Exam对象转换为字符串,然后再存入的。

3.要使用caffeine缓存,可以写一个配置类,然后使用@Autowired注入Cache对象;要使用redis,可以在yml中配置redis的url、账号密码等,然后使用@Autowired注入StringRedisTemplate对象。(当然还有相关jar包等)

4.caffeine配置类样例CacheConfig.java

@Configuration
public class CacheConfig {
  public static final String CACHE_NAME="caffeine";
  
  //参数从properties/yml中读取;冒号后是默认值,没有读取到值,就用冒号后的参数
  @Value("${spring.cache.caffeine.spec:recordStats,maximunSize=300000,expireAfterWrite=10s}")
  private String caffeineSpec;
  
  @Bean("caffeineCacheManager")
  public CaffeineCacheManager getCaffeineCacheManager() {
    CaffeineCacheManager cacheManager = new CaffeineCacheManager();
    cacheManager.setCacheNames(Collections.singleton(CACHE_NAME));
    cacheManager.setCacheSpecification(caffeineSpec);
    cacheManager.setAllowNullValues(true);
    return cacheManager;
  }
  
  @Bean
  public Cache getCaffeineCache(@Qualifier("caffeineCacheManager") CacheManager cacheManager){
    return cacheManager.getCache(CACHE_NAME);
  }
}

相关maven:

<properties>
  <spring.version>5.0.10.RELEASE</spring.version>
</properties>
<dependencies>
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>${spring.version}</version>
 </dependency>
</dependencies>

注入使用方式:

@Autowired
private Cache cache;

5.redis配置样例application.properties

spring.redis.database=1
spring.redis.host=10.123.123.123
spring.redis.port=37652
spring.redis.password=abcdef123456

相关maven:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
</dependency>

启动类Application.java注解:

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 2 * 3600)

注入使用:

@Autowired
private StringRedisTemplate redis;
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐