一、问题显现

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;
}

  

guava cache大量的WARN日志的问题分析的更多相关文章

  1. [Java 缓存] Java Cache之 Guava Cache的简单应用.

    前言 今天第一次使用MarkDown的形式发博客. 准备记录一下自己对Guava Cache的认识及项目中的实际使用经验. 一: 什么是Guava Guava工程包含了若干被Google的 Java项 ...

  2. 使用Guava cache构建本地缓存

    前言 最近在一个项目中需要用到本地缓存,在网上调研后,发现谷歌的Guva提供的cache模块非常的不错.简单易上手的api:灵活强大的功能,再加上谷歌这块金字招牌,让我毫不犹豫的选择了它.仅以此博客记 ...

  3. Spring Boot 揭秘与实战(二) 数据缓存篇 - Guava Cache

    文章目录 1. Guava Cache 集成 2. 个性化配置 3. 源代码 本文,讲解 Spring Boot 如何集成 Guava Cache,实现缓存. 在阅读「Spring Boot 揭秘与实 ...

  4. Guava cache功能简介(转)

    原文链接:http://ifeve.com/google-guava-cachesexplained/ 范例 LoadingCache<Key, Graph> graphs = Cache ...

  5. Guava Cache 使用笔记

    https://www.cnblogs.com/parryyang/p/5777019.html https://www.cnblogs.com/shoren/p/guava_cache.html J ...

  6. Guava Cache相关

    官方:http://ifeve.com/google-guava-cachesexplained/ 理解:https://segmentfault.com/a/1190000007300118 项目中 ...

  7. Guava Cache本地缓存

    Guava介绍 Guava是一种基于开源的Java库,其中包含谷歌正在由他们很多项目使用的很多核心库. 这个库是为了方便编码,并减少编码错误. 这个库提供用于集合,缓存,支持原语,并发性,常见注解,字 ...

  8. guava cache学习

    Guava Cache与ConcurrentMap很相似,但也不完全一样.最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除.相对地,Guava Cache为了限制内存占 ...

  9. Spring cache简单使用guava cache

    Spring cache简单使用 前言 spring有一套和各种缓存的集成方式.类似于sl4j,你可以选择log框架实现,也一样可以实现缓存实现,比如ehcache,guava cache. [TOC ...

随机推荐

  1. Base64实现测试,不要太相信apache-common的性能

    针对三种Base64实现: * 自已实现的 * JDK8的java.util.Base64 * apache-common的org.apache.commons.codec.binary.Base64 ...

  2. SendMessage函数与MSDN系统预定义消息

    SendMessage function https://msdn.microsoft.com/en-us/library/windows/desktop/ms644950%28v=vs.85%29. ...

  3. XF 开关控件

    <?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http:/ ...

  4. 生成view的描述字段列表

    ); declare @field_list nvarchar(max); set @table = N'vwMaterial'; set @field_list = N''; SELECT u.na ...

  5. 微信小程序把玩(二十四)toast组件

    原文:微信小程序把玩(二十四)toast组件 toast消息提示框,可用在提示一些信息,比如清楚缓存给用户一个友好的提示!或操作一些请求不想让用户有什么操作,toast也可以做到因为toast显示时其 ...

  6. 零元学Expression Blend 4 - Chapter 33 简单轻松的学会如何使用Visual States(下)

    原文:零元学Expression Blend 4 - Chapter 33 简单轻松的学会如何使用Visual States(下) 上篇提到了Visual State Manager中文翻译为视觉状态 ...

  7. Android零基础入门第10节:开发IDE大升级,终于迎来了Android Studio

    原文:Android零基础入门第10节:开发IDE大升级,终于迎来了Android Studio 通过前面几期的学习,我们知道了Android的前世今生,也了解了Android的系统架构和应用组件,也 ...

  8. asp.net处理请求

    当用户通过客户端浏览器向Web服务器发出请求时,Web服务器检查所请求页的扩展名, 如果是aspx,就会启动ASP.NET引擎处理该请求.ASP.NET引擎首先会检查输出缓冲中, 是否有此页面或此页面 ...

  9. .NET解析xml字符串,通过反射给实体类对象赋值,获取实体类数据列表

    /// <summary> /// 解析xml字符串 转换为实体类列表数据 /// </summary> /// <param name="xmlStr&quo ...

  10. 利用消息机制实现VC与Delphi之间的通讯(发送自定义消息)

    摘要: 本文介绍了使用Windows消息机制实现由不同语言编制的程序之间的相互通讯.联系,并以当前较为流行的两种语言Microsoft Visual C++ 6.0和Borland delphi 5. ...