Guava Cache 本地缓存在 Spring Boot 应用中的实践 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hansonwang99
V2EX    Blogger

Guava Cache 本地缓存在 Spring Boot 应用中的实践

  •  
  •   hansonwang99 2019-01-08 07:44:24 +08:00 3018 次点击
    这是一个创建于 2473 天前的主题,其中的信息可能已经有所发展或是发生改变。

    概述

    在如今高并发的互联网应用中,缓存的地位举足轻重,对提升程序性能帮助不小。而 3.x 开始的 Spring 也引入了对 Cache 的支持,那对于如今发展得如火如荼的 Spring Boot 来说自然也是支持缓存特性的。当然 Spring Boot 默认使用的是 code>SimpleCacheConfiguration,即使用 ConcurrentMapCacheManager 来实现的缓存。但本文将讲述如何将 Guava Cache 缓存应用到 Spring Boot 应用中。

    Guava Cache 是一个全内存的本地缓存实现,而且提供了线程安全机制,所以特别适合于代码中已经预料到某些值会被多次调用的场景

    下文就上手来摸一摸它,结合对数据库的操作,我们让 Guava Cache 作为本地缓存来看一下效果!

    注: 本文首发于 My Personal Blog:CodeSheep程序羊,欢迎光临 小站


    准备工作

    • 准备好数据库和数据表并插入相应实验数据( MySQL )

    比如我这里准备了一张用户表,包含几条记录:

    准备好 MySQL 数据库和数据表

    我们将通过模拟数据库的存取操作来看看 Guava Cache 缓存加入后的效果。


    搭建工程:Springboot + MyBatis + MySQL + Guava Cache

    pom.xml 中添加如下依赖:

     <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!--for mybatis--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--for Mysql--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- Spring boot Cache--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!--for guava cache--> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.0.1-jre</version> </dependency> </dependencies> 

    建立 Guava Cache 配置类

    引入 Guava Cache 的配置文件 GuavaCacheConfig

    @Configuration @EnableCaching public class GuavaCacheConfig { @Bean public CacheManager cacheManager() { GuavaCacheManager cacheManager = new GuavaCacheManager(); cacheManager.setCacheBuilder( CacheBuilder.newBuilder(). expireAfterWrite(10, TimeUnit.SECONDS). maximumSize(1000)); return cacheManager; } } 

    Guava Cache 配置十分简洁,比如上面的代码配置缓存存活时间为 10 秒,缓存最大数目为 1000 个


    配置 application.properties

    server.port=82 # Mysql 数据源配置 spring.datasource.url=jdbc:mysql://121.116.23.145:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false spring.datasource.username=root spring.datasource.password=xxxxxx spring.datasource.driver-class-name=com.mysql.jdbc.Driver # mybatis 配置 mybatis.type-aliases-package=cn.codesheep.springbt_guava_cache.entity mybatis.mapper-locatiOns=classpath:mapper/*.xml mybatis.configuration.map-underscore-to-camel-case=true 

    编写数据库操作和 Guava Cache 缓存的业务代码

    • 编写 entity
    public class User { private Long userId; private String userName; private Integer userAge; public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public Integer getUserAge() { return userAge; } public void setUserAge(Integer userAge) { this.userAge = userAge; } } 
    • 编写 mapper
    public interface UserMapper { List<User> getUsers(); int addUser(User user); List<User> getUsersByName( String userName ); } 
    • 编写 service
    @Service public class UserService { @Autowired private UserMapper userMapper; public List<User> getUsers() { return userMapper.getUsers(); } public int addUser( User user ) { return userMapper.addUser(user); } @Cacheable(value = "user", key = "#userName") public List<User> getUsersByName( String userName ) { List<User> users = userMapper.getUsersByName( userName ); System.out.println( "从数据库读取,而非读取缓存!" ); return users; } } 

    看得很明白了,我们在 getUsersByName接口上添加了注解:@Cacheable。这是 缓存的使用注解之一,除此之外常用的还有 @CachePut@CacheEvit,分别简单介绍一下:

    1. @Cacheable:配置在 getUsersByName方法上表示其返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问
    2. @CachePut:配置于方法上时,能够根据参数定义条件来进行缓存,其与 @Cacheable不同的是使用 @CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中,所以主要用于数据新增和修改操作上
    3. @CacheEvict:配置于方法上时,表示从缓存中移除相应数据。
    • 编写 controller
    @RestController public class UserController { @Autowired private UserService userService; @Autowired CacheManager cacheManager; @RequestMapping( value = "/getusersbyname", method = RequestMethod.POST) public List<User> geUsersByName( @RequestBody User user ) { System.out.println( "-------------------------------------------" ); System.out.println("call /getusersbyname"); System.out.println(cacheManager.toString()); List<User> users = userService.getUsersByName( user.getUserName() ); return users; } } 

    改造 Spring Boot 应用主类

    主要是在启动类上通过 @EnableCaching 注解来显式地开启缓存功能

    @SpringBootApplication @MapperScan("cn.codesheep.springbt_guava_cache") @EnableCaching public class SpringbtGuavaCacheApplication { public static void main(String[] args) { SpringApplication.run(SpringbtGuavaCacheApplication.class, args); } } 

    最终完工的整个工程的结构如下:

    完整工程结构


    实际实验

    通过多次向接口 localhost:82/getusersbyname POST 数据来观察效果:

    向接口提交数据

    可以看到缓存的启用和失效时的效果如下所示(上文 Guava Cache 的配置文件中设置了缓存 user 的实效时间为 10s ):

    缓存的启用和失效时的取数据效果

    怎么样,缓存的作用还是很明显的吧!


    后 记

    由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流!



    span class="gray">5 条回复    2019-01-09 13:20:27 +08:00
    dengtongcai
        1
    dengtongcai  
       2019-01-08 08:29:29 +08:00 via iPhone
    支持,前几天刚用这个 guava cache,很爽
    hansonwang99
        2
    hansonwang99  
    OP
       2019-01-08 08:30:40 +08:00 via iPhone
    @dengtongcai 请问你们是用在什么场景呢
    dengtongcai
        3
    dengtongcai  
       2019-01-08 08:41:58 +08:00 via iPhone   1
    @hansonwang99 我用到的是一个图片验证码反馈,打码(返回附带打码 id)id,验证,统一反馈。其中还用到了 removeListener,处理不同的移除情况。本来想用 AOP 静态织入,但是和 lombok 冲突了
    hansonwang99
        4
    hansonwang99  
    OP
       2019-01-08 09:39:39 +08:00
    @dengtongcai 哦哦,学习啦
    peihanw
        5
    peihanw  
       2019-01-09 13:20:27 +08:00
    俺现在 spring-boot 2.x 里用的是 caffeine,pom 引用的包和楼主有些差异:
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    </dependency>
    应用 yml:(网关验证登录 token 是系统签发的,防密钥泄露 token 被伪造)
    spring:
    cache:
    type: caffeine
    cache-names: tokens
    caffeine:
    spec: maximumSize=100000,expireAfterWrite=300s
    代码上差不多。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5531 人在线   最高记录 6679   &nbp;   Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 06:33 PVG 14:33 LAX 23:33 JFK 02:33
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86