guava cache大量的WARN日志的问题分析
2019-08-16 10:08:56来源:博客园 阅读 ()
guava cache大量的WARN日志的问题分析
一、问题显现
2019-04-21 11:16:32 [http-nio-4081-exec-2] WARN com.google.common.cache.LocalCache - Exception thrown during refresh com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key BKCIYear0. at com.google.common.cache.LocalCache$Segment.getAndRecordStats(LocalCache.java:2350) at com.google.common.cache.LocalCache$Segment$1.run(LocalCache.java:2331) at com.google.common.util.concurrent.MoreExecutors$DirectExecutor.execute(MoreExecutors.java:457) at com.google.common.util.concurrent.ExecutionList.executeListener(ExecutionList.java:156) at com.google.common.util.concurrent.ExecutionList.add(ExecutionList.java:101) at com.google.common.util.concurrent.AbstractFuture.addListener(AbstractFuture.java:170) at com.google.common.cache.LocalCache$Segment.loadAsync(LocalCache.java:2326) at com.google.common.cache.LocalCache$Segment.refresh(LocalCache.java:2389) at com.google.common.cache.LocalCache$Segment.scheduleRefresh(LocalCache.java:2367) at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2187) at com.google.common.cache.LocalCache.get(LocalCache.java:3937) at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3941) at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4824) at com.kcidea.sushibase.Service.Cache.GoogleLocalCache.getCacheByName(GoogleLocalCache.java:42)
google的这个开发工具里面的缓存是个轻量化的缓存,类似一个HashMap的实现,google在里面加了很多同步异步的操作。使用起来简单,不用额外搭建redis服务,故项目中使用了这个缓存。
有一天生产环境直接假死了,赶紧上服务器排查,发现日志里面有大量的报WARN错误,只要触发cache的get就会报警告,由于cache的触发频率超高,导致了日志磁盘爆满,一天好几个G的日志里面全是WARN的错误。但是在开发环境下根本不触发这个错误,怎么调试都没有进这段代码里面。先暂时停用了缓存,然后开始排查。
二、问题排查
1. 根据报错的堆栈,一点一点往上找,直到找到这一行的时候发现了一些端倪,他想找一个newValue
at com.google.common.cache.LocalCache$Segment.refresh(LocalCache.java:2389)
2. 继续顺着这条线往里面找,直到找到这段代码,为什么要找newValue呢,map需要刷新了,过期了,或者主动触发刷新值了。
if (map.refreshes() && (now - entry.getWriteTime() > map.refreshNanos) && !entry.getValueReference().isLoading()) { V newValue = refresh(key, hash, loader, true); if (newValue != null) { return newValue; } }
3. 然后就可以解释问题为什么只在生产环境出现,而开发环境不出现了,因为是触发了过期时间,我们设置的过期时间是30分钟,所以开发环境很少调试超过30分钟的,每次都是重新运行,所以根本触发不到这个超时的地方。
4. 然后接着调试,发现会走到我们一开始初始化cache的代码那边
/** * 缓存队列变量 */ static LoadingCache<String, Object> cache = CacheBuilder.newBuilder() // 给定时间内没有被读/写访问,则回收。 .refreshAfterWrite(CACHE_OUT_TIME, TimeUnit.MINUTES) // 缓存过期时间和redis缓存时长一样 .expireAfterAccess(CACHE_OUT_TIME, TimeUnit.MINUTES) // 设置缓存个数 .maximumSize(50000). build(new CacheLoader<String, Object>() { @Override public Object load(String key) throws Exception { //找不到就返回null (1) return null; } });
注意上面的代码,(1)的位置,找不到就返回null,在网上找的代码里面这里通常写的是return null或者return doThingsTheHardWay(key)之类的,但是没有详细的doThingsTheHardWay描述,所以我这里写了个null。
所以根本的问题就是这里返回null导致的错误了。
三、解决方案
找到了问题原因,解决方案就相对来说容易的很多了
1. 修改(1)处的代码,将return null修改成return new NullObject()
static LoadingCache<String, Object> cache = CacheBuilder.newBuilder() // 给定时间内没有被读/写访问,则回收。 .refreshAfterWrite(CACHE_OUT_TIME, TimeUnit.MINUTES) // 缓存过期时间和redis缓存时长一样 .expireAfterAccess(CACHE_OUT_TIME, TimeUnit.MINUTES) // 设置缓存个数 .maximumSize(50000). build(new CacheLoader<String, Object>() { @Override public Object load(String key) throws Exception { //尝试将这里改成new NullObject,外面进行判断 return new NullObject(); } });
2. 定义一个空白的类就叫NullObject
/** * ClassName NullObject * Author shenjing * Date 2019/7/10 * Version 1.0 **/ public class NullObject { }
3. 在通用的getCacheByName的方法中进行判断,取到的对象是不是NullObject类型的,如果是,则返回null给外层,进行重新加载。
private static <T> T getCacheByName(String name) { T ret = null; try { if (cache.asMap().containsKey(name)) { ret = (T) cache.get(name); if (ret.getClass().equals(NullObject.class)) { //缓存已过期,返回null return null; } log.debug("缓存读取[{}]成功", name); } } catch (Exception ex) { log.debug("缓存[{}]读取失败:{}", name, ex.getMessage()); } return ret; }
原文链接:https://www.cnblogs.com/JangoJing/p/11162459.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:看年薪50W的架构师如何手写一个SpringMVC框架
下一篇:面试随笔
- 对比Memcached和Redis,谁才是适合你的缓存? 2020-05-22
- 如何用Spring Boot集成Ehcache缓存,教你三招搞定 2020-05-19
- 2020 java Memcached面试题及答案(最全版本持续更新) 2020-05-07
- 2020最新Java工程师面试题-Memcached 面试篇(附答案) 2020-04-23
- 在一个主线程中,要求有大量子线程执行完之后,主线程才执行 2020-04-19
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash