我有一个 Spring 组件,它使用具有以下配置的文件
ehcache.xml
声明一些缓存:
<?xml version="1.0" encoding="UTF-8"?>
<eh:config
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:eh="http://www.ehcache.org/v3"
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.3.xsd">
<eh:cache alias="Cache1">
<eh:expiry>
<eh:ttl unit="minutes">1</eh:ttl>
</eh:expiry>
<eh:resources>
<eh:heap>10000</eh:heap>
</eh:resources>
</eh:cache>
<!-- [...] -->
</eh:config>
然后使用类似于以下内容:
import org.springframework.cache.annotation.Cacheable;
public class ThingDoer {
@Cacheable("Cache1")
public Integer doSomethingCached(int value) {
return value + 3;
}
// ...
}
这样,我可以看到 Prometheus 中出现不同的缓存指标:
bash> 2>&1 curl -v --silent 'http://localhost:9000/actuator/prometheus' | grep cache_gets | sort
cache_gets_total{cache="Cache1",cache_manager="cacheManager",hostname="localhost",name="Cache1",result="hit",} 0.0
cache_gets_total{cache="Cache1",cache_manager="cacheManager",hostname="localhost",name="Cache1",result="miss",} 0.0
cache_gets_total{cache="Cache2",cache_manager="cacheManager",hostname="localhost",name="Cache2",result="hit",} 0.0
cache_gets_total{cache="Cache2",cache_manager="cacheManager",hostname="localhost",name="Cache2",result="miss",} 0.0
cache_gets_total{cache="Cache3",cache_manager="cacheManager",hostname="localhost",name="Cache3",result="hit",} 0.0
cache_gets_total{cache="Cache3",cache_manager="cacheManager",hostname="localhost",name="Cache3",result="miss",} 0.0
[...]
我在文件中定义的所有缓存
ehcache.xml
都会自动公开它们的指标。
现在,我创建了一个自制的缓存,它在查询时执行复杂的操作,并为此使用了
com.google.common.cache.LoadingCache
接口,因为它的目的是替换此类缓存的另一个实例,并对其余代码进行尽可能少的更改。因此,我创建了一个自定义缓存,具有以下签名:
import com.google.common.cache.AbstractLoadingCache;
public class CustomCache extends AbstractLoadingCache<String, Object> {
public CustomCache(Function<String, Object> valueLoader) {
// ...
}
// ...
}
它正确声明了一个
stats()
方法,该方法返回包含缓存统计信息的 com.google.common.cache.CacheStats
。其构造函数的参数 valueLoader
用于在缓存中缺少新值时加载新值。
现在,这是如何使用此缓存的草图:
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
public class CustomThingDoer {
private final CustomCache customCache;
public CustomThingDoer() {
this.customCache = new CustomCache(this::loadNewObject);
}
public Optional<Object> doSomethingCached(String value) {
try {
return Optional.of(customCache.get(value));
} catch (ExecutionException e) {
System.out.println("Something wrong happened");
return Optional.empty();
}
}
private Object loadNewObject(String value) {
// Do something non-static
}
}
由于缓存旨在直接调用其方法的地方使用(即没有
@Cacheable
注释来使用此缓存,而是使用对 get
的调用),并且由于加载缺失值的方法取决于在定义它的类的实例上(即,所讨论的方法是提取数据库,其代码取决于CustomThingDoer
实例中定义的内容),我希望将其手动注册到MeterRegistry
中。
为此,我将
MeterRegistry
的 bean 传递给构造函数中的 CustomThingDoer
,并执行以下操作:
GuavaCacheMetrics.monitor(meterRegistry, this.customCache, "MyCustomCache");
但是现在这已经完成了,这就是我在 Prometheus 中获得的缓存指标:
bash> 2>&1 curl -v --silent 'http://localhost:9000/actuator/prometheus' | grep cache_gets | sort
cache_gets_total{cache="MyCustomCache",hostname="localhost",result="hit",} 0.0
cache_gets_total{cache="MyCustomCache",hostname="localhost",result="miss",} 0.0
现在,仅显示我的自定义缓存的指标。此外,字段
cache_manager
不存在。
我的怀疑是,由于我手动注册缓存,一些谓词被 automagic 破坏了,并且它永远不会将自动生成的 EhCache 缓存添加到
MeterRegistry
。
我想要同时拥有自定义缓存的指标以及隐式创建的 EhCache 缓存的指标。如果可能的话,我希望不必更改所使用的接口(即`com.google.common.cache.LoadingCache),以便对代码(并且老实说,对单元测试)进行最少的更改。有人知道问题可能是什么,或者我可能有什么解决方案吗?
这似乎与 Micrometer 中的一个长期错误有关:https://github.com/micrometer-metrics/micrometer/issues/877。
它在几年前就被修复了,但它仍然有副作用,其中每个指标名称必须具有相同的标签键集,否则有些不被考虑在内。 https://github.com/micrometer-metrics/micrometer/issues/877#issuecomment-944894069