【SpringBoot应用篇】SpringBoot集成j2cache二级缓存框架
j2cache介绍
j2cache是OSChina(开源中国)目前正在使用的两级缓存框架。
j2cache的两级缓存结构:
- L1: 进程内缓存 caffeine/ehcache
- L2: 集中式缓存 Redis/Memcached
j2cache其实并不是在重复造轮子,而是作资源整合,即将Ehcache、Caffeine、redis、Spring Cache等进行整合。
J2Cache 的使用场景: 由于大量的缓存读取会导致L2的网络成为整个系统的瓶颈,因此L1的目标是降低对L2的读取次数。该缓存框架主要用于集群环境中。单机也可使用,用于避免应用重启导致的ehcache缓存数据丢失。
J2Cache 默认使用 Caffeine 作为一级缓存,使用 Redis 作为二级缓存。
数据读取顺序 -> L1 -> L2 -> DB
j2cache入门使用
pom
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-spring-boot2-starter</artifactId>
<version>2.8.0-release</version>
</dependency>
<dependency>
<groupId>net.oschina.j2cache</groupId>
<artifactId>j2cache-core</artifactId>
<version>2.8.0-release</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
application.yml
server:
port: 9000
redis:
ip: 127.0.0.1
port: 6379
password:
database: 0
spring:
cache:
type: GENERIC
redis:
host: ${redis.ip}
password: ${redis.password}
port: ${redis.port}
database: ${redis.database}
j2cache:
# config-location: /j2cache.properties
open-spring-cache: true
cache-clean-mode: passive
allow-null-values: true
redis-client: lettuce #指定redis客户端使用lettuce,也可以使用Jedis
l2-cache-open: true #开启二级缓存
broadcast: net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy
# broadcast: jgroups
L1: #指定一级缓存提供者为caffeine
provider_class: caffeine
L2: #指定二级缓存提供者为redis
provider_class: net.oschina.j2cache.cache.support.redis.SpringRedisProvider
config_section: lettuce
sync_ttl_to_redis: true
default_cache_null_object: false
serialization: fst
caffeine:
properties: /caffeine.properties # 这个配置文件需要放在项目中
lettuce:
mode: single
namespace:
storage: generic
channel: j2cache
scheme: redis
hosts: ${redis.ip}:${redis.port}
password: ${redis.password}
database: ${redis.database}
sentinelMasterId:
maxTotal: 100
maxIdle: 10
minIdle: 10
timeout: 10000
RedisConfig
@Configuration
public class RedisConfig {
/**
* 设置RedisTemplate规则
* @param redisConnectionFactory
* @return
*/
@Bean(name = "redisTemplate")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(mapper);
template.setValueSerializer(jackson2JsonRedisSerializer);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
//使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(stringRedisSerializer);
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
caffeine.properties
caffeine配置: region
不存在caffeine.properties
配置文件中,使用default区域
- hb(region区域)=100(key数量), 30s(过期时间)
#########################################
# Caffeine configuration
# [name] = size, xxxx[s|m|h|d]
#########################################
default=2000, 2h
hn=50, 2h
hb=100, 30s
CacheTestController
@RestController
@RequestMapping("/cache")
public class CacheTestController {
private String key = "myKey";
private static final String HN_REGION = "hn";
private static final String HB_REGION = "hb";
@Autowired
private CacheChannel cacheChannel;
@GetMapping("/getUser")
public List<String> getUser(String region){
CacheObject cacheObject = cacheChannel.get(region, key);
if(cacheObject.getValue() == null){
//缓存中没有找到,查询数据库获得
List<String> data = new ArrayList<String>();
data.add("李白");
data.add("韩信");
//放入缓存
cacheChannel.set(region,key,data);
return data;
}
return (List<String>) cacheObject.getValue();
}
/**
* 清理指定缓存
*/
@GetMapping("/evict")
public String evict(){
cacheChannel.evict(HN_REGION,key);
return "evict success";
}
/**
* 检测存在那级缓存
*/
@GetMapping("/check")
public String check(){
int check = cacheChannel.check(HN_REGION, key);
return "level:" + check;
}
/**
* 检测缓存数据是否存在
*/
@GetMapping("/exists")
public String exists(){
boolean exists = cacheChannel.exists(HN_REGION, key);
return "exists:" + exists;
}
/**
* 清理指定区域的缓存
*/
@GetMapping("/clear")
public String clear(){
cacheChannel.clear(HN_REGION);
return "clear success";
}
}
具体api:
启动类
@SpringBootApplication
public class J2CacheApplication {
public static void main(String[] args) {
SpringApplication.run(J2CacheApplication.class,args);
}
}
启动项目,访问地址:http://localhost:9000/cache/getUser?region=hb
重启项目,由于j2cache的一级缓存(caffeine)是进程级缓存,重启后一级缓存消失。但是二级缓存(redis)的数据还存在,再次访问上面地址,通过debug断点调试可以看到程序从redis中获取了缓存数据。