在高并发访问场景下,使用多级多层次的缓存技术,可以更加快速、高效的响应客户端的访问,同时服务端可以承载更大的访问量。在整个【高并发技术系列】中,本篇属于《缓存篇》中的「本机缓存」部分,主要介绍如何通过Guava Cache实现在本地服务缓存数据,避免高频访问分布式缓存和DB数据库。
开发环境准备
1.开发工具:IntelliJ IDEA
2.jdk版本:1.8
3.名词说明:line.68代表代码68行的意思
4.maven依赖
Guava Cache介绍及实践
Guava是google提供的非常强大的核心工程jar包,不单单这次介绍到到cache部分,在序列化,集合、IO等多个地方都有很大的帮助。
在上篇中介绍了如何使用线程缓存threadLocal,那现在介绍下如何使用本地服务器的缓存,多次请求直接可以从本地内存中获取数据,避免频繁调用分布式的redis缓存或者DB数据库,同时也会说下为什么用Guava Cache,而不是简单的使用集合Map或者ConcurrentMap的原因。
1.构建cache对象的示例
大部分工具类在提供实例化对象时候都用到Builder模式,此处也不例外,关于Builder设计模式可以搜索下,此处不做详细解释了。line.42的maximumSize()指定缓存的数据量,因为这个数据是直接缓存在内存中的,若是不限量容易造成OOM; line.44的expireAfterAccess()指定了缓存数据的有效时间,过期会淘汰掉;line. 45的removalListener()指定了删除缓存时候的监听,比如某个key因为过期或者淘汰掉了,那么就会触发自定义的监听器,监听器处理逻辑可以完全自定义,实现指定的接口就可以了;line.46的build()方法入参了一个匿名类,需要实现load()方法,这个地方的作用是当查询的key并不在内存中时候,如何加载这个key,比如通过redis分布式缓存获取,或者执行rpc调用服务,或者读取DB等等。
2.常用的方法
getIfPresent():依据key查询数据,缓存中有则返回数据,缓存中没有则返回null;
getUnchecked():依据key查询数据,缓存中有则返回数据,缓存中没有则执行图 1-1-1中line.48的load()方法加载数据放入缓存,并返回数据。
get():功能类似getUnchecked(),区别是get()会抛异常,若是对异常有特殊要求处理的话在使用上稍微注意下。
refresh():无论缓存中有没有key对应的数据,都强制执行图 1-1-1中line.48的load()方法加载数据放入缓存,返回空,也就是强制刷新缓存都意思,如图1-2-2控制台打印内容,最后输出都时候使用的getIfPresent()不会重新load数据。
asMap():将缓存中的数据以map的形式返回,使用map修改数据时可以直接影响到缓存中的数据,如图1-2-3控制台打印内容。
3.总结
以上是Guava Cache常用到的方法,实用且简单。若是不使用Guava Cache这样的工具类,自定义Map或者ConcurrentMap
会存在一些问题。比如:
多线程访问时候并发问题;
冷数据无法自动清除;
无法控制占用内存;
多线程访问同一个key时候,造成多个线程同时去load数据等等。
曾梦想仗剑走天涯,看尽这人间繁华。昔已逝,然吾心之所向,提三寸锋芒,亦可破碎亘古不变的迷茫!骚年们,加油!!!