Auto-configuration enables the instrumentation of all available Caches on startup with metrics prefixed with cache. Cache instrumentation is standardized for a basic set of metrics. Additional, cache-specific metrics are also available.
The following cache libraries are supported:
Caffeine
EhCache 2
Hazelcast
Any compliant JCache (JSR-107) implementation
Redis
Metrics are tagged by the name of the cache and by the name of the CacheManager that is derived from the bean name.
Note
Only caches that are configured on startup are bound to the registry. For caches not defined in the cache’s configuration, e.g. caches created on-the-fly or programmatically after the startup phase, an explicit registration is required. A CacheMetricsRegistrar bean is made available to make that process easier.
// CacheMeterBinder.class
public final void bindTo(MeterRegistry registry) {
// 显而易见,这里是绑定各种指标
if (this.size() != null) {
Gauge.builder("cache.size", this.cache.get(), (c) -> {
Long size = this.size();
return size == null ? 0.0D : (double)size;
}).tags(this.tags).description("The number of entries in this cache. This may be an approximation, depending on the type of cache.").register(registry);
}
if (this.missCount() != null) {
FunctionCounter.builder("cache.gets", this.cache.get(), (c) -> {
Long misses = this.missCount();
return misses == null ? 0.0D : (double)misses;
}).tags(this.tags).tag("result", "miss").description("the number of times cache lookup methods have returned an uncached (newly loaded) value, or null").register(registry);
}
FunctionCounter.builder("cache.gets", this.cache.get(), (c) -> {
return (double)this.hitCount();
}).tags(this.tags).tag("result", "hit").description("The number of times cache lookup methods have returned a cached value.").register(registry);
FunctionCounter.builder("cache.puts", this.cache.get(), (c) -> {
return (double)this.putCount();
}).tags(this.tags).description("The number of entries added to the cache").register(registry);
if (this.evictionCount() != null) {
FunctionCounter.builder("cache.evictions", this.cache.get(), (c) -> {
Long evictions = this.evictionCount();
return evictions == null ? 0.0D : (double)evictions;
}).tags(this.tags).description("cache evictions").register(registry);
}
this.bindImplementationSpecificMetrics(registry);
}
// CaffeineCacheMetrics.class
protected void bindImplementationSpecificMetrics(MeterRegistry registry) {
// 这里是一些Caffeine提供的额外指标
FunctionCounter.builder("cache.eviction.weight", this.cache, (c) -> {
return (double)c.stats().evictionWeight();
}).tags(this.getTagsWithCacheName()).description("The sum of weights of evicted entries. This total does not include manual invalidations.").register(registry);
if (this.cache instanceof LoadingCache) {
TimeGauge.builder("cache.load.duration", this.cache, TimeUnit.NANOSECONDS, (c) -> {
return (double)c.stats().totalLoadTime();
}).tags(this.getTagsWithCacheName()).description("The time the cache has spent loading new values").register(registry);
FunctionCounter.builder("cache.load", this.cache, (c) -> {
return (double)c.stats().loadSuccessCount();
}).tags(this.getTagsWithCacheName()).tags(new String[]{"result", "success"}).description("The number of times cache lookup methods have successfully loaded a new value").register(registry);
FunctionCounter.builder("cache.load", this.cache, (c) -> {
return (double)c.stats().loadFailureCount();
}).tags(this.getTagsWithCacheName()).tags(new String[]{"result", "failure"}).description("The number of times {@link Cache} lookup methods failed to load a new value, either because no value was found or an exception was thrown while loading").register(registry);
}
}
SLRU cache is divided into two segments, a probationary segment and a protected segment. Lines in each segment are ordered from the most to the least recently accessed. Data from misses is added to the cache at the most recently accessed end of the probationary segment. Hits are removed from wherever they currently reside and added to the most recently accessed end of the protected segment. Lines in the protected segment have thus been accessed at least twice. The protected segment is finite, so migration of a line from the probationary segment to the protected segment may force the migration of the LRU line in the protected segment to the most recently used (MRU) end of the probationary segment, giving this line another chance to be accessed before being replaced. The size limit on the protected segment is an SLRU parameter that varies according to the I/O workload patterns. Whenever data must be discarded from the cache, lines are obtained from the LRU end of the probationary segment