使用guava cache的时候,在cache中没有值或者值需要更新的时候,都需要去load,而这个load往往对应从数据库或者远程接口拿数据并缓存下来的操作。在高qps场景的服务中,这个load可能会导致调用链的阻塞,如果阻塞时间长,可能会影响服务,甚至可能拖垮服务,所以要了解哪些地方会阻塞,有没有什么方法能够尽量少的去阻塞。

一.同步load
1.load
load是第一次加载,加载之前cache中没有值。load永远都是同步的,不管是否使用异步进行包装,具体见下面第三部分。

对于同一个key,多次请求只会触发一次加载。比如,线程1访问key1,发现cache中不存在key1,然后触发key1的load。与此同时,在load加载完成之前,线程2,线程3...线程N都访问key1,这些访问不会再触发key1的加载,但是在key1的load加载完成之前,这些请求都会被hang在那里等待。调用链如下:

get:4952, LocalCache$LocalLoadingCache

getOrLoad:3967, LocalCache

get:3963, LocalCache

get:2046, LocalCache$Segment

lockedGetOrLoad:2140, LocalCache$Segment

waitForLoadingValue:2153, LocalCache$Segment

waitForValue:3571, LocalCache$LoadingValueReference

getUninterruptibly:168, Uninterruptibles->这里用future的get方法阻塞在这里,直到第一个触发load操作的线程完成load操作并将结果设置到该future中,这个get方法就会解除阻塞并获取到load到的值。

2.reload
reload是之前cache中有值,需要刷新该值,比如设置了过期时间后,到了缓存过期需要更新的时间,会触发scheduleRefresh去做刷新。手动调用refresh方法的时候也会触发reload。

3.refresh
手动调用了refresh,会导致loadingcache的重新load操作。调用的是Segment中的refresh方法,里面有loadAsync方法,LoadingValueReference的loadFuture中会根据之前cache中是否有值来决定load或者reload。

load永远都是同步的,不管你是否使用异步包装:

reload如果是被异步包装过的,那么就会是异步操作的,否则和load一样也是同步的:

注意:

正如上面描述的,load永远都是阻塞的,是因为虽然这里的方法名是loadAsync,但是如果loadFuture中当前key的value是空的,那么调用loader.load方法的时候,还是同步阻塞的,返回的future是在阻塞操作拿到结果之后才把future返回的,这时future已经有值了。只有reload的时候才是异步操作。

4.refreshAfterWrite
guava cache的所有更新操作都是依靠读写方法触发的,因为其内部没有时钟或者定时任务。比如上一次写之后超过了refresh设置的更新时间,但是之后没有cache的访问了,那么下次get的时候才会触发refresh,这次触发会导致get的阻塞。

调用链路:

{@link com.google.common.cache.LocalCache.Segment#scheduleRefresh}
{@link com.google.common.cache.LocalCache.Segment#loadAsync}
{@link com.google.common.cache.LocalCache.LoadingValueReference#loadFuture}
{@link CacheLoader#reload}

注意:只有当前触发load的get方法会被阻塞!

这里会有检测,如果当前key已经在loading状态,那么refresh直接返回null,不会阻塞。

而loading又需要时间,所以在loading完毕之前,其他get方法拿到的都是旧值。

二.异步load
CacheLoader.asyncReloading

这里的reload方法被包装成了一个future,内部用任务将reload操作包装为异步操作,所以在reload的时候会调用被封装为异步的方法:

这里调用不会被阻塞,所以即使是触发get方法,因为是封装为异步任务,所以也不会被阻塞。

但是load方法不同于reload,它还是阻塞的。

所以这里的异步优化,相对于上面的同步reload,只减少了“一个”线程的阻塞。

三.工程设计上的一些实践和考量
工程上我们在使用guava cache的时候,一般都会使用get方法,这样在key不存在的时候会触发加载,后续的请求访问到这个key的时候就会可以取到缓存的内容了。

这样的使用方式无可厚非,同时也是最直观最能够想到的cache使用方式,但是我们考虑这样几个情况:

高并发场景,同时load的加载需要一定的时间

如果我们忽视这个点,可能会导致服务在启动的时候load很高,同时上游系统可能会发现调用该系统的超时请求量增加。我们假设我们系统一般处理请求需要50ms的时间,单个线程每秒就可以处理20个请求。如果load的时间是50ms,同时这20个请求都访问的同一个key,那么这20个请求,每个请求的的处理时间会从50ms涨到100ms,那么势必会影响该系统的吞吐。

load中有远程api调用依赖

同时,如果load的实现是一个远程调用,无论是读外部接口,还是读数据库,都不是我们自己能控制的,一旦外部接口响应变慢,会导致该cache的get操作都卡住,很可能造成雪崩。

所以首先,如果在load中存在远程调用,一定要设置好超时时间。

其次,如果对于key请求的结果,设计一定的降级策略(如果业务允许的话),比如访问cache的时候使用getIfPresent,如果发现没有值,就使用降级策略的默认值,同时异步去用get或者refresh触发该key的加载过程,这样对于cache做到读写线程分离,让读的操作永远不会阻塞。

如果再追求一点极致的设计的话,线程池异步触发load的操作也可以从读(线程)动作中解耦出来,我们可以使用queue作为中介,这样线程池异步触发load的操作进一步简化为“封装load操作为task,放入queue中”,节省了在读线程中有可能触发的启动线程的动作,同时只需要一个后台线程(池)去这个queue中拿任务处理即可。

==================================

核心类

CacheBuilder:类,缓存构建器。构建缓存的入口,指定缓存配置参数并初始化本地缓存。
CacheBuilder在build方法中,会把前面设置的参数,全部传递给LocalCache,它自己实际不参与任何计算。这种初始化参数的方法值得借鉴,代码简洁易读。
CacheLoader:抽象类。用于从数据源加载数据,定义load、reload、loadAll等操作。
Cache:接口,定义get、put、invalidate等操作,这里只有缓存增删改的操作,没有数据加载的操作。
AbstractCache:抽象类,实现Cache接口。其中批量操作都是循环执行单次行为,而单次行为都没有具体定义。
LoadingCache:接口,继承自Cache。定义get、getUnchecked、getAll等操作,这些操作都会从数据源load数据。
AbstractLoadingCache:抽象类,继承自AbstractCache,实现LoadingCache接口。
LocalCache:类。整个guava cache的核心类,包含了guava cache的数据结构以及基本的缓存的操作方法。
LocalManualCache:LocalCache内部静态类,实现Cache接口。
其内部的增删改缓存操作全部调用成员变量 localCache(LocalCache类型)的相应方法。
LocalLoadingCache:LocalCache内部静态类,继承自LocalManualCache类,实现LoadingCache接口。
其所有操作也是调用成员变量localCache(LocalCache类型)的相应方法。
CacheStats:缓存加载/命中统计信息。

常用方法

V getIfPresent(Object key) 获取缓存中key对应的value,如果缓存没命中,返回null。
V get(K key) throws ExecutionException 获取key对应的value,若缓存中没有,则调用LocalCache的load方法,从数据源中加载,并缓存。
void put(K key, V value) 如果缓存有值,覆盖,否则,新增
void putAll(Map m);循环调用单个的方法
void invalidate(Object key); 删除缓存
void invalidateAll(); 清楚所有的缓存,相当远map的clear操作。
long size(); 获取缓存中元素的大概个数。为什么是大概呢?元素失效之时,并不会实时的更新size,所以这里的size可能会包含失效元素。
CacheStats stats(); 缓存的状态数据,包括(未)命中个数,加载成功/失败个数,总共加载时间,删除个数等。
asMap()方法获得缓存数据的ConcurrentMap快照
cleanUp()清空缓存
refresh(Key) 刷新缓存,即重新取缓存数据,更新缓存
ImmutableMap getAllPresent(Iterable keys) 一次获得多个键的缓存值

参考:https://blog.csdn.net/xxcupid/article/details/92834521

https://blog.csdn.net/u012187218/article/details/79309202

Guava (一)Guava Cache进阶之同步/异步load相关推荐

  1. python进阶04IO的同步异步,阻塞非阻塞

    原创博客地址:python进阶04IO的同步异步,阻塞非阻塞 同步和异步 同步和异步关注的是消息通信机制. 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就得到 ...

  2. java guava eventbus_JAVA | Guava EventBus 使用

    JAVA | Guava EventBus 使用 系列文章目录 Table of Contents [TOC] 前言 EventBus 是 Guava 的事件处理机制,是观察者模式(生产/消费模型)的 ...

  3. 同步异步 阻塞非阻塞

    首先知道同步异步与阻塞非阻塞不是一个层次的概念 "阻塞"与"非阻塞"与"同步"与"异步"不能简单的从字面理解,提供一个从 ...

  4. Python|线程和进程|阻塞|非阻塞|同步|异步|生成器和协程|资源竞争|进程间通信|aiohttp库|daemon属性值详解|语言基础50课:学习(11)

    文章目录 系列目录 原项目地址 第34课:Python中的并发编程-1 线程和进程 多线程编程 使用 Thread 类创建线程对象 继承 Thread 类自定义线程 使用线程池 守护线程 资源竞争 G ...

  5. jq ajax同步异步,详解JQuery中Ajax的同步与异步

    说到ajax同步与异步,可能很多同学对它都不是很了解,接下来我们就Ajax的同步与异步作个简单的介绍.同步:提交请求->等待服务器处理->处理完毕返回这个期间客户端浏览器不能干任何事.异步 ...

  6. 同步/异步,阻塞/非阻塞概念深度解析

    ‍‍ 原文:https://blog.csdn.net/lengxiao1993/article/78154467 更新:极客重生 网络IO作为网络编程核心基础知识,是所以程序员都需要理解掌握的,最近 ...

  7. 简单理解浏览器的event loop 和 JavaScript的同步异步

    为什么JavaScript是单线程的? JavaScript的主要用途是和用户进行交互以及对DOM的操作,为了避免复杂的同步问题(如果多线程,A线程对某DOM添加内容,B线程对它又进行了删除操作,这往 ...

  8. 异步通知是什么意思_一次相亲经历,我彻底搞懂了阻塞非阻塞、同步异步

    看到标题,可能你会想,相亲跟阻塞/非阻塞,同步/异步有啥关系,这个逗b不知道在想什么东西.不要急,且听我慢慢道来 年纪大了,一回家七大姑八大姨就各种催婚,都说要给我介绍女朋友.这不,刚刚门口,我的大姨 ...

  9. 15分钟读懂进程线程、同步异步、阻塞非阻塞、并发并行,太实用了!

    作者:Martin cnblogs.com/mhq-martin/p/9035640.html 基本概念 1 进程和线程 进程(Process): 是Windows系统中的一个基本概念,它包含着一个运 ...

最新文章

  1. linux 物理内存用完了_Linux用户空间与内核空间(理解高端内存)
  2. 数字测图原理与方法的实习日志_【技术】消费级无人机倾斜摄影测量1:500测图方法及精度研究...
  3. Winform Textbox实现滚动条始终在最下面
  4. python 判断是否是润年及函数封装的2种方法
  5. linux虚拟网络设备之,Linux虚拟网络设备
  6. 计算机主机清理步骤,三个方法教你如何正确清理C盘空间
  7. matplot画图控制marker点的个数_专刊主编述评 中药质量标志物(Qmarker):提高中药质量标准及质量控制理论和促进中药产业科学发展...
  8. slk文件转wma_什么是WMA文件(以及如何打开一个文件)?
  9. uniapp语音识别_uniapp语音识别
  10. 关于围棋的基本功(转)
  11. 2018级《程序设计基础(B)II》期末上机考试( 题解)
  12. 云计算机到底是啥来的,啥叫云计算(云计算究竟是什么)
  13. Boxy SVG for Mac版矢量图编辑软件
  14. 名编辑电子杂志大师教程 | 名编辑电子杂志大师如何编辑文本文字?
  15. 长航安庆警方成功破获系列非法经营“笑气”案
  16. 淘宝API接口,商品详情,产品页面信息接口调用展示
  17. mysql一个字符占用多少个字节
  18. 迷宫问题寻找最短路径(BFS)
  19. 科比:向火箭脱帽致敬!
  20. 企业如何选择CRM软件?

热门文章

  1. 指甲之怎样以指甲测健康
  2. Long call,Short call与Long put, Short put的联系与区别
  3. java书签_Java 操作Word书签(一):添加、删除、读取书签
  4. 李振杰:中科红旗的生与死
  5. IPV6大型网络配置实战
  6. DNS域名服务协议和其实现Bind应用
  7. ffmpeg 分辨率 压缩_视频怎么在尽量不损害画质的前提下压缩?
  8. Genesis公链确定亮相Consensus 2022大会
  9. 刚子:走马观花奋达创“芯”发布会
  10. DCIC2021 入门数据比赛(1)