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. 这个日志)

0%