springboot学习(十一):缓存Guava的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_36553913/article/details/85837164

说明

之前在做项目时,需要在项目启动时将一些数据加载到内存使用,当时的做法是存在了一个map中,每个不同的缓存单独使用一个map,在使用时会在固定的时间间隔重新加载数据,这个时间的设置,是通过一个volatile类型的时间变量作为判断依据,每次在请求数据时会先判断时间是否超时,是则重新加载数据并更新这个变量。但是对这个变量是否需要设置volatile这个关键字很是纠结,通过发帖,查资料,了解了在并发修改变量时,应该要保证变量的安全性和可见性,所以使用该关键字。但是这样的做法始终觉得有问题,首先在并发请求时,会不会造成重复更新,还有在修改map时还会有请求数据的操作,这样也会造成很大的问题,这里有个严重的错误是在存在并发更新的情况下居然没有使用CouncurrentHashMap。基于以上种种问题,寻求一种良好的解决方案,于是学习了guava缓存的使用,通过spring的缓存支持,可以做到异步刷新,过期时间的设置等。在这里记录总结下guava的配置及使用方式。

正文

1.引入依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
	<groupId>com.google.guava</groupId>
 	<artifactId>guava</artifactId>
 	<version>26.0-jre</version>
</dependency>

2.创建配置类

在学习时通过创建guava配置属性类来对缓存属性进行设置,没有通过spring提供的自动配置。
创建GuavaProperties类,通过该类接收属性配置值。

@ConfigurationProperties(prefix = "guava.cache.config")
public class GuavaProperties {

    private long maximumSize;

    private long maximumWeight;

    private long expireAfterAccessDuration;

    private long expireAfterWriteDuration;

    private long refreshDuration;

    private int initialCapacity;

    private int concurrencyLevel;

	....省略getter setter方法
}

同时希望通过cacheLoader进行数据的加载及在数据更新时使用线程池异步重新加载,这里创建GuavaCacheLoader类。

public class GuavaCacheLoader extends CacheLoader<String,String> {

    private final ExecutorService executorService = Executors.newFixedThreadPool(4);

    @Override
    public String load(String s) throws Exception {
        if (s.equals("hello")) {
            Thread.sleep(3000);
            return "world";
        } else if (s.equals("world")) {
            Thread.sleep(5000);
            return "hello";
        }
        return "no value";
    }

    @Override
    public ListenableFuture<String> reload(String key, String oldValue) throws Exception {
        ListenableFutureTask<String> task = ListenableFutureTask.create(new Callable<String>() {
            @Override
            public String call() throws Exception {
                if (key.equals("hello")) {
                    return "nihao";
                } else if (key.equals("world")) {
                    return "shijie";
                }
                return "no value";
            }
        });

        executorService.submit(task);
        return task;
    }
}

这里用线程睡眠来模拟真实加载数据时的耗时。在重新加载数据时对数据的返回值进行了变更。

3.配置Guava

创建配置类GuavaCacheConfig,通过创建CacheManager来管理不同的cache。

@EnableConfigurationProperties(GuavaProperties.class)
@Configuration
@EnableCaching
public class GuavaCacheConfig {

    @Autowired
    private GuavaProperties guavaProperties;

    @Bean
    public CacheBuilder<Object,Object> cacheBuilder(){
        long maximumSize = guavaProperties.getMaximumSize();
        long expireAfterWrite = guavaProperties.getExpireAfterWriteDuration();
        long expireAfterAccess = guavaProperties.getExpireAfterAccessDuration();
        long refreshDuration = guavaProperties.getRefreshDuration();

        if(maximumSize <= 0){
            maximumSize = 1024;
        }
        if(expireAfterAccess <= 0){
            expireAfterAccess = 3600;
        }
        if(expireAfterWrite <= 0){
            expireAfterWrite = 3600;
        }

        if(refreshDuration <= 0){
            refreshDuration = 1800;
        }

        return CacheBuilder.newBuilder().maximumSize(maximumSize)
                .expireAfterWrite(expireAfterWrite,TimeUnit.SECONDS)
                .refreshAfterWrite(refreshDuration,TimeUnit.SECONDS);
    }

    @Bean(name = "guavaCacheLoader")
    public CacheLoader cacheLoader(){
        return new GuavaCacheLoader();
    }

    @Bean
    public CacheManager cacheManager(@Qualifier("cacheBuilder")CacheBuilder cacheBuilder,
                                    @Qualifier("guavaCacheLoader")CacheLoader cacheLoader){
        GuavaCacheManager cacheManager = new GuavaCacheManager();
        cacheManager.setCacheBuilder(cacheBuilder);
        cacheManager.setCacheLoader(cacheLoader);
        return cacheManager;
    }

}

在application.propertiesp配置文件设置配置属性值,这里为了演示,去掉了最后操作数据过期的时长设置。
设置的属性为写入后10s数据过期进行刷新,20s后数据过期重新写入

guava.cache.config.expire-after-write-duration=20
#更新间隔时长
guava.cache.config.refresh-duration=10
guava.cache.config.maximumSize=1024

4.测试

编写测试controller

@RestController
public class HelloController {

    @Autowired
    private CacheManager cacheManager;

    @RequestMapping("hello")
    public String hello(){
        long start = System.currentTimeMillis();
        String value = cacheManager.getCache("hello").get("hello").get().toString();
        long end = System.currentTimeMillis();

        return String.format("value=[%s], wait time : [%d]",value,(end - start));
    }

    @RequestMapping("world")
    public String world(){
        long start = System.currentTimeMillis();
        String value = cacheManager.getCache("world").get("world").get().toString();
        long end = System.currentTimeMillis();

        return String.format("value=[%s], wait time : [%d]",value,(end - start));
    }
}

测试结果

初次请求
在这里插入图片描述

再次请求
在这里插入图片描述

过期刷新
在这里插入图片描述

到这里Guava缓存的使用学习总结已经结束了,这里我只根据之前的项目需要来使用guava,有很多特性,如缓存注释的使用在这里并没有体现。需要的同学可以查询资料进行学习。
在下篇博文中,会针对springboot2所支持的缓存caffeine进行总结学习。spring5后spring官方放弃了guava而使用了更优秀的caffeine缓存,同时spring的缓存支持也有较大的变动,在下篇博文中将会利用caffeine来学习缓存的使用。

参考资料:
http://ifeve.com/google-guava-cachesexplained/
https://blog.csdn.net/a67474506/article/details/52608855

源码地址:
https://github.com/Edenwds/springboot_study/tree/master/guava

猜你喜欢

转载自blog.csdn.net/sinat_36553913/article/details/85837164
今日推荐