Caffeine CacheManager 如何查看命中率、监控
Caffeine CacheManager 如何查看命中率、监控
官方提供了结合 Prometheus 的方案,如果想简单看下监控,可继续看下文。
直奔主题
@Api(tags = "监控-Caffeine")
@RestController
@RequestMapping("monitor/caffeine")
public class MonitorCaffeineController {
@Resource
private CacheManager caffeine;
@GetMapping("cacheNames")
@ApiOperation("所有缓存name")
public Collection<String> cacheNames() {
return caffeine.getCacheNames();
}
@GetMapping("stats")
@ApiOperation("根据缓存name查询缓存监控信息")
public Map<String, Object> stats(@RequestParam String cacheName) {
CaffeineCache caffeineCache = (CaffeineCache) caffeine.getCache(cacheName);
CacheStats stats = CacheStats.empty();
if (caffeineCache != null) {
stats = caffeineCache.getNativeCache().stats();
}
Map<String, Object> map = new HashMap<>(16);
map.put("请求次数", stats.requestCount());
map.put("命中次数", stats.hitCount());
map.put("未命中次数", stats.missCount());
map.put("加载成功次数", stats.loadSuccessCount());
map.put("加载失败次数", stats.loadFailureCount());
map.put("加载失败占比", stats.loadFailureRate());
map.put("加载总耗时", stats.totalLoadTime());
map.put("回收总次数", stats.evictionCount());
map.put("回收总权重", stats.evictionWeight());
return map;
}
}
下文可忽略,通过 CacheManager 找监控方法的心路
想通过 CacheManager –> CaffeineCacheManager 找到监控的方法,无果,准备重写 CaffeineCacheManager 时意外找到了,源码才是。。。
1、直接使用 Caffeine.newBuilder() 如何监控
public class CaffeineController {
private static final Cache<String, Object> CAFFEINE_CACHE = Caffeine.newBuilder()
.initialCapacity(256)
// 保存缓存数量
.maximumSize(100)
// 从写入开始保存的时间
.expireAfterWrite(10, TimeUnit.SECONDS)
// 删除缓存原因
.removalListener((key, value, cause) -> LOGGER.info(">>> 删除缓存 [{}]({}), reason is [{}]", key, value, cause))
// 开启状态监控
.recordStats()
.build();
@GetMapping("stats")
public String stats() {
CacheStats stats = CAFFEINE_CACHE.stats();
return stats.toString();
}
}
2、通过 CacheManager 使用 Caffeine
@EnableCaching
@Configuration
public class LocalCache {
@Bean("caffeine")
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.initialCapacity(256)
.expireAfterWrite(24, TimeUnit.HOURS)
.maximumSize(100)
// 删除缓存原因
.removalListener((key, value, cause) -> LOGGER.info(">>> 删除缓存 [{}]({}), reason is [{}]", key, value, cause))
// 开启状态监控
.recordStats()
);
return cacheManager;
}
}
3、惯性思维,翻翻 CaffeineCacheManager
不用 CacheManager 时,可以通过 Caffeine.newBuilder().recordStats().build()
查看监控信息,那 CaffeineCacheManager 里边 build 之后的东西返回值放哪了?
public class CaffeineCacheManager implements CacheManager {
private Caffeine<Object, Object> cacheBuilder = Caffeine.newBuilder();
private final Map<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
public void setCaffeine(Caffeine<Object, Object> caffeine) {
Assert.notNull(caffeine, "Caffeine must not be null");
doSetCaffeine(caffeine);
}
// 3. *.caffeine.*.Cache 绑定名字并转化成 CaffeineCache
protected Cache adaptCaffeineCache(String name, com.github.benmanes.caffeine.cache.Cache<Object, Object> cache) {
return new CaffeineCache(name, cache, isAllowNullValues());
}
// 2. 核心步骤
// 这层层线索证明 CacheManager#getCache(String name) 这个方法就是我们要的
// Cache --> CaffeineCache --> *.caffeine.*.Cache --> 拿到 stats() 就好了
protected Cache createCaffeineCache(String name) {
return adaptCaffeineCache(name, createNativeCaffeineCache(name));
}
// 1. 这一步用了 cacheBuilder.build()
// 这个 *.caffeine.*.Cache 就是要找的,也不知道这个 name 是干啥的,打个断点看看
protected com.github.benmanes.caffeine.cache.Cache<Object, Object> createNativeCaffeineCache(String name) {
return (this.cacheLoader != null ? this.cacheBuilder.build(this.cacheLoader) : this.cacheBuilder.build());
}
}
4、重写吧(x_x)
cacheBuilder 是私有的,cacheBuilder.build()之后的东西也拿不到,但是 createNativeCaffeineCache 是 protected 的,可以用继承走一波。
新写个类 CustomCaffeineCacheManager 继承 CaffeineCacheManager,通过重写 createNativeCaffeineCache 拿到创建完的 .caffeine.
.Cache,后边使用它,啥也拿不到(可能是用的方式不对)。
意外收获,打断点后,知道 name 是干啥的了。就是 @Cacheable 的 value 属性对应的值
@Cacheable(value = "short:url", cacheManager = "caffeine", key = "'short_url_' + #key")
5、最后翻了下 CacheManager,原来如此
秒懂 getCacheNames,以及每个缓存 name 都对应了一个 .caffeine..Cache,
也即Caffeine.newBuilder().recordStats().build()
build 后的结果。
public interface CacheManager {
@Nullable
Cache getCache(String name);
Collection<String> getCacheNames();
}
getCache(String name)
根据缓存名字获取缓存对象(看到这句是不是就恍然大悟了)
getCacheNames
查看所有缓存名字
6、结束语
结合 CacheManager#getCache 以及 CaffeineCacheManager 源码写下了开篇的代码。