前言
讲过前面2篇文章的讲解,我们知道了spring boot 是如何集成spring cache的,那么我们接着来看一下org.springframework.boot.actuate.cache 中有关cache的实现.这部分的类图如下:
我们本文关于CacheStatisticsProvider的实现只讲解EhCacheStatisticsProvider,ConcurrentMapCacheStatisticsProvider,其它的实现也是差不多的,这里就不解析了
本文的最后,我们来看一下CachePublicMetrics的实现(这才是我们的目的)
解析
CacheStatistics
CacheStatistics–>对于给定cache的统计快照,CacheStatistics 的实例有非常短的生命周期,它只代表在特定时间内的缓存统计.该接口声明了如下方法:
// 通过指定的前缀生成相关的Metric
Collection<Metric<?>> toMetrics(String prefix);
// 返回缓存的大小
Long getSize();
// 返回缓存命中率,其值为0至1之间,0 意味着命中率为0% , 1-->缓存命中率100%,返回null--> 当前的CacheStatistics 没有提供必要的信息
Double getHitRatio();
// 缓存丢失率,返回值在0至1之间,0-->缺失率0%, 1--> 缺失率100%,null--> 当前的CacheStatistics 没有提供必要的信息
Double getMissRatio();
DefaultCacheStatistics
DefaultCacheStatistics–> CacheStatistics的唯一实现
字段如下:
// 缓存大小 private Long size; // 缓存命中率 private Double hitRatio; // 缓存缺失率 private Double missRatio;
方法实现如下:
toMetrics,代码如下:
public Collection<Metric<?>> toMetrics(String prefix) { Collection<Metric<?>> result = new ArrayList<Metric<?>>(); addMetric(result, prefix + "size", getSize()); addMetric(result, prefix + "hit.ratio", getHitRatio()); addMetric(result, prefix + "miss.ratio", getMissRatio()); return result; }
向结果集中添加缓存大小,缓存命中率,缓存缺失率的Metric.
addMetric,代码如下:
private <T extends Number> void addMetric(Collection<Metric<?>> metrics, String name, T value) { if (value != null) { metrics.add(new Metric<T>(name, value)); } }
getSize,getHitRatio,getMissRatio 方法只需返回该类所持有的字段即可.代码如下:
public Long getSize() { return this.size; } public Double getHitRatio() { return this.hitRatio; } public Double getMissRatio() { return this.missRatio; }
此外还声明1个setGetCacheCounts(设置缓存命中率,缓存缺失率的方法)的方法,代码如下:
public void setGetCacheCounts(long hitCount, long missCount) { long total = hitCount + missCount; if (total > 0) { double hitRatio = hitCount / (double) total; setHitRatio(hitRatio); setMissRatio(1 - hitRatio); } }
- 计算缓存的请求数(total)=hitCount+missCount
- 如果total大于0,则计算缓存命中率(hitCount/total),缓存缺失率(1-hitRatio)并赋值
CacheStatisticsProvider
CacheStatisticsProvider–>根据cache提供对应的CacheStatistics的接口,该接口是1个泛型接口,其泛型参数如下:
CacheStatisticsProvider<C extends Cache>
限定只能是Cache的子类.该类只声明了1个方法,如下:
// 根据给定的Cache 返回 CacheStatistics 的快照,返回null --> 如果给定的cache不能进行处理的话
CacheStatistics getCacheStatistics(CacheManager cacheManager, C cache);
ConcurrentMapCacheStatisticsProvider
ConcurrentMapCacheStatisticsProvider–>提供ConcurrentMapCache对应的CacheStatistics的实现.
该类的声明如下:
public class ConcurrentMapCacheStatisticsProvider implements CacheStatisticsProvider<ConcurrentMapCache>
泛型参数为ConcurrentMapCache
方法实现如下:
public CacheStatistics getCacheStatistics(CacheManager cacheManager, ConcurrentMapCache cache) { DefaultCacheStatistics statistics = new DefaultCacheStatistics(); statistics.setSize((long) cache.getNativeCache().size()); return statistics; }
方法逻辑很简单,直接实例化DefaultCacheStatistics,然后将ConcurrentMapCache中的缓存用的map的size赋值给DefaultCacheStatistics即可. 没有提供缓存命中率,缓存缺失率的统计
自动装配:
声明在ConcurrentMapCacheStatisticsConfiguration中,代码如下:
@Configuration @ConditionalOnClass(ConcurrentMapCache.class) static class ConcurrentMapCacheStatisticsConfiguration { @Bean public ConcurrentMapCacheStatisticsProvider concurrentMapCacheStatisticsProvider() { return new ConcurrentMapCacheStatisticsProvider(); } }
由于ConcurrentMapCacheStatisticsConfiguration是CacheStatisticsAutoConfiguration的静态内部类.而在CacheStatisticsAutoConfiguration中声明了如下注解:
@Configuration @AutoConfigureAfter(CacheAutoConfiguration.class) @ConditionalOnBean(CacheManager.class)
因此,当BeanFactory中存在CacheManager类型的bean时ConcurrentMapCacheStatisticsConfiguration才会生效.
@ConditionalOnClass(ConcurrentMapCache.class) –> 当ConcurrentMapCache.class在当前类路径下时生效.
EhCacheStatisticsProvider
EhCacheStatisticsProvider–> 提供EhCacheCache对应的CacheStatistics的实现
getCacheStatistics 实现如下:
public CacheStatistics getCacheStatistics(CacheManager cacheManager, EhCacheCache cache) { DefaultCacheStatistics statistics = new DefaultCacheStatistics(); // 1. 获得StatisticsGateway StatisticsGateway ehCacheStatistics = cache.getNativeCache().getStatistics(); // 2. 设置size statistics.setSize(ehCacheStatistics.getSize()); // 3. 计算缓存命中率,如果其值没有溢出的话,则设置缓存命中率和缓存缺失率 double hitRatio = cacheHitRatio(ehCacheStatistics); if (!Double.isNaN(hitRatio)) { // ratio is calculated 'racily' and can drift marginally above unity, // so we cap it here double sanitizedHitRatio = (hitRatio > 1 ? 1 : hitRatio); statistics.setHitRatio(sanitizedHitRatio); statistics.setMissRatio(1 - sanitizedHitRatio); } return statistics; }
- 获得StatisticsGateway
- 设置size
计算缓存命中率,如果其值没有溢出的话,则设置缓存命中率和缓存缺失率.代码如下:
private double cacheHitRatio(StatisticsGateway stats) { long hitCount = stats.cacheHitCount(); long missCount = stats.cacheMissCount(); return ((double) hitCount) / (hitCount + missCount); }
自动装配:
同样,也是声明在CacheStatisticsAutoConfiguration的静态内部类中–>EhCacheCacheStatisticsProviderConfiguration.代码如下:
@Configuration @ConditionalOnClass({ EhCacheCache.class, Ehcache.class, StatisticsGateway.class }) static class EhCacheCacheStatisticsProviderConfiguration { @Bean public EhCacheStatisticsProvider ehCacheCacheStatisticsProvider() { return new EhCacheStatisticsProvider(); } }
因此,当满足如下条件时生效:
- BeanFactory中存在CacheManager类型的bean
- 在当前的类路径下存在 EhCacheCache.class, Ehcache.class, StatisticsGateway.class
CachePublicMetrics
CachePublicMetrics–>提供缓存统计的PublicMetrics实现,实现了PublicMetrics接口.
字段,构造器如下:
// key--> cacheManager bean 对应的id,value --> CacheManager @Autowired private Map<String, CacheManager> cacheManagers; @Autowired // beanFactory中所有类型为CacheStatisticsProvider的bean private Collection<CacheStatisticsProvider<?>> statisticsProviders; // 1.5.4 版本废弃 @Deprecated public CachePublicMetrics() { } public CachePublicMetrics(Map<String, CacheManager> cacheManagers, Collection<CacheStatisticsProvider<?>> statisticsProviders) { this.cacheManagers = cacheManagers; this.statisticsProviders = statisticsProviders; }
metrics,实现如下:
public Collection<Metric<?>> metrics() { Collection<Metric<?>> metrics = new HashSet<Metric<?>>(); for (Map.Entry<String, List<CacheManagerBean>> entry : getCacheManagerBeans() .entrySet()) { addMetrics(metrics, entry.getKey(), entry.getValue()); } return metrics; }
遍历cacheManagers,获得key–> 缓存集名称,value–>CacheManagerBean(CacheManager对应的id,CacheManager 的封装).代码如下:
private MultiValueMap<String, CacheManagerBean> getCacheManagerBeans() { // key--> 缓存集名称,value--> CacheManager对应的id,CacheManager 的封装 MultiValueMap<String, CacheManagerBean> cacheManagerNamesByCacheName = new LinkedMultiValueMap<String, CacheManagerBean>(); // 1. 遍历cacheManagers依次进行处理 for (Map.Entry<String, CacheManager> entry : this.cacheManagers.entrySet()) { // 2. 遍历CacheManager的CacheNames 依次进行处理 for (String cacheName : entry.getValue().getCacheNames()) { cacheManagerNamesByCacheName.add(cacheName, new CacheManagerBean(entry.getKey(), entry.getValue())); } } return cacheManagerNamesByCacheName; }
- 遍历cacheManagers依次进行处理
- 遍历CacheManager的CacheNames依次将其添加到结果集中
其中,CacheManagerBean 就是对CacheManager的封装.代码如下:
private static class CacheManagerBean { private final String beanName; private final CacheManager cacheManager; CacheManagerBean(String beanName, CacheManager cacheManager) { this.beanName = beanName; this.cacheManager = cacheManager; } public String getBeanName() { return this.beanName; } public CacheManager getCacheManager() { return this.cacheManager; } }
遍历第1步的结果,依次调用addMetrics方法进行添加,代码如下:
private void addMetrics(Collection<Metric<?>> metrics, String cacheName, List<CacheManagerBean> cacheManagerBeans) { for (CacheManagerBean cacheManagerBean : cacheManagerBeans) { // 1. 获得对应的CacheManager CacheManager cacheManager = cacheManagerBean.getCacheManager(); // 2. 获得cacheName所对应的Cache Cache cache = unwrapIfNecessary(cacheManager.getCache(cacheName)); // 3. 根据cache和cacheManager 获得 对应的CacheStatistics CacheStatistics statistics = getCacheStatistics(cache, cacheManager); if (statistics != null) { // 4. 如果statistics不等于null,则生成前缀后加入到metrics中 String prefix = cacheName; if (cacheManagerBeans.size() > 1) { prefix = cacheManagerBean.getBeanName() + "_" + prefix; } prefix = "cache." + prefix + (prefix.endsWith(".") ? "" : "."); metrics.addAll(statistics.toMetrics(prefix)); } } }
- 获得对应的CacheManager
获得cacheName所对应的Cache,其中调用了unwrapIfNecessary,代码如下:
private Cache unwrapIfNecessary(Cache cache) { if (ClassUtils.isPresent( "org.springframework.cache.transaction.TransactionAwareCacheDecorator", getClass().getClassLoader())) { return TransactionAwareCacheDecoratorHandler.unwrapIfNecessary(cache); } return cache; }
- 如果在当前类路径下存在org.springframework.cache.transaction.TransactionAwareCacheDecorator,则通过TransactionAwareCacheDecoratorHandler来获得cache. 由于在项目中加入了spring-boot-starter-cache,因此间接的引入了spring-context-support,TransactionAwareCacheDecorator就在此包中.因此,这里是会执行的
- 否则,直接返回.
TransactionAwareCacheDecoratorHandler#unwrapIfNecessary 实现如下:
private static Cache unwrapIfNecessary(Cache cache) { try { if (cache instanceof TransactionAwareCacheDecorator) { return ((TransactionAwareCacheDecorator) cache).getTargetCache(); } } catch (NoClassDefFoundError ex) { // Ignore } return cache; }
如果是TransactionAwareCacheDecorator的实例,则直接获得其持有的cache,否则,直接返回
根据cache和cacheManager 获得 对应的CacheStatistics.代码如下:
private CacheStatistics getCacheStatistics(Cache cache, CacheManager cacheManager) { if (this.statisticsProviders != null) { // 1. 如果statisticsProviders 不等于null,则依次遍历之 for (CacheStatisticsProvider provider : this.statisticsProviders) { // 2. 获得CacheStatisticsProvider的泛型参数,如果其泛型参数类型和指定的Cache类似的话,则调用CacheStatisticsProvider#getCacheStatistics // 获得对应的CacheStatistics Class<?> cacheType = ResolvableType .forClass(CacheStatisticsProvider.class, provider.getClass()) .resolveGeneric(); if (cacheType.isInstance(cache)) { CacheStatistics statistics = provider.getCacheStatistics(cacheManager, cache); if (statistics != null) { return statistics; } } } } return null; }
- 如果statisticsProviders 不等于null,则依次遍历之
- 获得CacheStatisticsProvider的泛型参数,如果其泛型参数类型和指定的Cache类似的话,则调用CacheStatisticsProvider#getCacheStatistics获得对应的CacheStatistics
如果statistics不等于null
- 如果cacheManagerBeans有多个,则prefix= CacheManager bean 的id+”_”+cacheName
- 在prefix前加上cache.的前缀
生成Metrics后加入到结果集中
自动装配:
声明在CacheStatisticsConfiguration中,代码如下:
@Configuration @ConditionalOnClass(CacheManager.class) @ConditionalOnBean(CacheManager.class) static class CacheStatisticsConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnBean(CacheStatisticsProvider.class) public CachePublicMetrics cachePublicMetrics( Map<String, CacheManager> cacheManagers, Collection<CacheStatisticsProvider<?>> statisticsProviders) { return new CachePublicMetrics(cacheManagers, statisticsProviders); } }
当满足以下条件时生效:
- @ConditionalOnClass(CacheManager.class)–>在当前类路径下存在CacheManager.class时生效
- @ConditionalOnBean(CacheManager.class)–> 在BeanFactory中存在CacheManager类型的bean时生效
- @ConditionalOnMissingBean –> BeanFactory中不存在CachePublicMetrics类型的bean时生效
- @ConditionalOnBean(CacheStatisticsProvider.class)–> 在BeanFactory中存在CacheStatisticsProvider类型的bean时生效