Springboot集成jetcache框架
springboot集成jetcache框架
查看官方文档
引入依赖
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>${jetcache.latest.version}</version>
</dependency>
添加配置
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
local:
default:
type: caffeine
keyConvertor: bean:furyKeyConverter
remote:
default:
type: redis
keyConvertor: bean:furyKeyConverter
valueEncoder: bean:furyValueEncoder
valueDecoder: bean:furyValueDecoder
poolConfig:
minIdle: 5
maxIdle: 20
maxTotal: 50
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
启动类添加注解
@EnableMethodCache(basePackages = "com.company.mypackage")
@EnableCreateCacheAnnotation
官方提供的key/value序列化方式为:JAVA/KRYO/KRYO5/FASTJSON2
这里我采用的是新框架fury所以我们需要做一些集成
自定义序列化方式
下载项目源码以后可以看见springboot启动方式会自动加载配置类:com.alicp.jetcache.autoconfigure.JetCacheAutoConfiguration
@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean
public SpringConfigProvider springConfigProvider(
@Autowired ApplicationContext applicationContext,
@Autowired GlobalCacheConfig globalCacheConfig,
@Autowired(required = false) EncoderParser encoderParser,
@Autowired(required = false) KeyConvertorParser keyConvertorParser,
@Autowired(required = false) Consumer<StatInfo> metricsCallback) {
return new JetCacheBaseBeans().springConfigProvider(applicationContext, globalCacheConfig,
encoderParser, keyConvertorParser, metricsCallback);
}
这个类在构建 springConfigProvider 的时候 里面调用的是:com.alicp.jetcache.anno.support.ConfigProvider#doInit
protected void doInit() {
cacheBuilderTemplate = new CacheBuilderTemplate(globalCacheConfig.isPenetrationProtect(),
globalCacheConfig.getLocalCacheBuilders(), globalCacheConfig.getRemoteCacheBuilders());
for (CacheBuilder builder : globalCacheConfig.getLocalCacheBuilders().values()) {
EmbeddedCacheBuilder eb = (EmbeddedCacheBuilder) builder;
if (eb.getConfig().getKeyConvertor() instanceof ParserFunction) {
ParserFunction f = (ParserFunction) eb.getConfig().getKeyConvertor();
eb.setKeyConvertor(parseKeyConvertor(f.getValue()));
}
}
for (CacheBuilder builder : globalCacheConfig.getRemoteCacheBuilders().values()) {
ExternalCacheBuilder eb = (ExternalCacheBuilder) builder;
if (eb.getConfig().getKeyConvertor() instanceof ParserFunction) {
ParserFunction f = (ParserFunction) eb.getConfig().getKeyConvertor();
eb.setKeyConvertor(parseKeyConvertor(f.getValue()));
}
if (eb.getConfig().getValueEncoder() instanceof ParserFunction) {
ParserFunction f = (ParserFunction) eb.getConfig().getValueEncoder();
eb.setValueEncoder(parseValueEncoder(f.getValue()));
}
if (eb.getConfig().getValueDecoder() instanceof ParserFunction) {
ParserFunction f = (ParserFunction) eb.getConfig().getValueDecoder();
eb.setValueDecoder(parseValueDecoder(f.getValue()));
}
}
initCacheMonitorInstallers();
}
在这里面设置了全局的 KeyConvertor,ValueEncoder,ValueDecoder 继续跟踪源码发现:
com.alicp.jetcache.anno.support.DefaultSpringEncoderParser#parseBeanName
static String parseBeanName(String str) {
final String beanPrefix = "bean:";
int len = beanPrefix.length();
if (str != null && str.startsWith(beanPrefix) && str.length() > len) {
return str.substring(len);
} else {
return null;
}
}
我们传入的配置:jetcache.remote.default.keyConvertor 的值如果是以:bean:
开头的我们去掉bean:
前缀然后返回bean的名称交给下面的代码从bean容器中获取该实例对象 com.alicp.jetcache.anno.support.DefaultSpringKeyConvertorParser#parseKeyConvertor
@Override
public Function<Object, Object> parseKeyConvertor(String convertor) {
String beanName = DefaultSpringEncoderParser.parseBeanName(convertor);
if (beanName == null) {
return super.parseKeyConvertor(convertor);
} else {
return (Function<Object, Object>) applicationContext.getBean(beanName);
}
}
如果传入的bean名字不为空,我们就从容器中获取并返回。由此可见我们配置文件中的bean的名称需要添加前缀:
jetcache:
statIntervalMinutes: 15
areaInCacheName: false
local:
default:
type: caffeine
keyConvertor: bean:furyKeyConverter
在spring容器中注入对应的bean
@Configuration
@AutoConfigureBefore(value = {JetCacheAutoConfiguration.class})
public class JetCacheConfig {
@Bean
public FuryKeyConverter furyKeyConverter(){
return new FuryKeyConverter();
}
@Bean
public FuryValueDecoder furyValueDecoder(){
return new FuryValueDecoder(false);
}
@Bean
public FuryValueEncoder furyValueEncoder(){
return new FuryValueEncoder(false);
}
}
FuryKeyConverter
public class FuryKeyConverter implements Function<Object, Object> {
@Override
public Object apply(Object originalKey) {
if (originalKey == null) {
return null;
}
if (originalKey instanceof String) {
return originalKey;
}
return JSON.toJSONString(originalKey);
}
}
这个类我是参考了开源项目的:com.alicp.jetcache.support.Fastjson2KeyConvertor
这个类演变而来的,这就是key的序列化方式所以还是string类型
FuryValueDecoder
public class FuryValueDecoder extends AbstractValueDecoder {
public FuryValueDecoder(boolean useIdentityNumber) {
super(useIdentityNumber);
}
@Override
protected Object doApply(byte[] buffer) throws Exception {
return FuryFactory.getInstance().deserialize(buffer);
}
}
FuryValueEncoder
public class FuryValueEncoder extends AbstractValueEncoder {
public FuryValueEncoder(boolean useIdentityNumber) {
super(useIdentityNumber);
}
@Override
public byte[] apply(Object o) {
return FuryFactory.getInstance().serialize(o);
}
}
在上面value的编解码器中,我使用的是fury工厂来完成的,
FuryFactory
public class FuryFactory {
private static final FuryFactory INSTANCE = new FuryFactory();
private final ThreadPoolFury threadPoolFury;
private FuryFactory(){
this.threadPoolFury = new ThreadPoolFury(classLoader -> {
Fury fury = Fury
.builder()
.withLanguage(Language.JAVA)
.withClassLoader(classLoader)
.withAsyncCompilation(true)
.requireClassRegistration(false)
.build();
return fury;
}, 8, 16, 10, TimeUnit.SECONDS);
}
public static FuryFactory getInstance(){
return INSTANCE;
}
public <T> byte[] serialize(T t) {
return threadPoolFury.serialize(t);
}
public <T> T deserialize(byte[] bytes) {
return (T) threadPoolFury.deserialize(bytes);
}
}
这里定义了一个 ThreadPoolFury 线程池,最多 16个。最少8个,并开启了异步编解码的方式提高效率
然后编写测试代码测试
@Autowired
private CacheManager cacheManager;
@Test
void test9(){
QuickConfig qc = QuickConfig.newBuilder("test:")
.localLimit(1024 * 10)
// two level cache
.cacheType(CacheType.BOTH)
// invalidate local cache in all jvm process after update
.syncLocal(true)
.loader(key-> {
log.debug("load data form database.");
return "local_" + key;
})
.build();
Cache<String, String> userCache = cacheManager.getOrCreateCache(qc);
System.out.println(userCache.get("1"));
System.out.println(userCache.get("2"));
System.out.println(userCache.get("1"));
System.out.println(userCache.get("3"));
System.out.println(userCache.get("2"));
}
第一次执行的日志:
2024-04-18T21:26:44.903+08:00 DEBUG 67843 --- [black-tortoise-platform] [ main] .p.BlackTortoisePlatformApplicationTests : load data form database.
2024-04-18T21:26:44.976+08:00 WARN 67843 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:26:45.145+08:00 INFO 67843 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@6773c073
2024-04-18T21:26:45.145+08:00 WARN 67843 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:26:45.146+08:00 INFO 67843 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@6ca6cc1c
2024-04-18T21:26:45.147+08:00 WARN 67843 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:26:45.148+08:00 INFO 67843 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@75c8855e
2024-04-18T21:26:45.148+08:00 WARN 67843 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:26:45.149+08:00 INFO 67843 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@4d5aeea7
2024-04-18T21:26:45.149+08:00 WARN 67843 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:26:45.150+08:00 INFO 67843 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@3453cf27
2024-04-18T21:26:45.150+08:00 WARN 67843 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:26:45.151+08:00 INFO 67843 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@6ecf239d
2024-04-18T21:26:45.151+08:00 WARN 67843 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:26:45.152+08:00 INFO 67843 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@5f9cb96f
2024-04-18T21:26:45.152+08:00 WARN 67843 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:26:45.152+08:00 INFO 67843 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@45295bed
local_1
2024-04-18T21:26:45.222+08:00 DEBUG 67843 --- [black-tortoise-platform] [ main] .p.BlackTortoisePlatformApplicationTests : load data form database.
local_2
local_1
2024-04-18T21:26:45.242+08:00 DEBUG 67843 --- [black-tortoise-platform] [ main] .p.BlackTortoisePlatformApplicationTests : load data form database.
2024-04-18T21:26:45.248+08:00 INFO 67843 --- [black-tortoise-platform] [-jit-compiler-0] io.fury.codegen.CompileUnit : Generate code for com.alicp.jetcache.CacheValueHolderFuryCodec_1_622488023_532482128 took 33 ms.
local_3
local_2
查看reids:
第二次执行:日志
2024-04-18T21:29:25.772+08:00 WARN 67901 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:29:25.942+08:00 INFO 67901 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@125447a0
2024-04-18T21:29:25.942+08:00 WARN 67901 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:29:25.944+08:00 INFO 67901 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@cede70a
2024-04-18T21:29:25.944+08:00 WARN 67901 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:29:25.945+08:00 INFO 67901 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@6dc8a602
2024-04-18T21:29:25.946+08:00 WARN 67901 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:29:25.948+08:00 INFO 67901 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@2a45c6f0
2024-04-18T21:29:25.948+08:00 WARN 67901 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:29:25.950+08:00 INFO 67901 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@1a9cfd5f
2024-04-18T21:29:25.950+08:00 WARN 67901 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:29:25.951+08:00 INFO 67901 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@792a9cb1
2024-04-18T21:29:25.952+08:00 WARN 67901 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:29:25.953+08:00 INFO 67901 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@51fadd20
2024-04-18T21:29:25.953+08:00 WARN 67901 --- [black-tortoise-platform] [ main] io.fury.config.FuryBuilder : Class registration isn't forced, unknown classes can be deserialized. If the environment isn't secure, please enable class registration by `FuryBuilder#requireClassRegistration(true)` or configure ClassChecker by `ClassResolver#setClassChecker`
2024-04-18T21:29:25.954+08:00 INFO 67901 --- [black-tortoise-platform] [ main] io.fury.Fury : Created new fury io.fury.Fury@550ce3a9
local_1
local_2
local_1
local_3
local_2
可以看出来我们的自定义序列化方式成功了,并且第二次由于程序重启本地缓存中没有数据所以从redis中加载数据,没有执行load
中的方法(所以没有:load data form database.
这个日志)