前言
本文我们来分析PrefixMetricReader,PrefixMetricWriter,MultiMetricRepository的实现.类图如下:
PrefixMetricReader
PrefixMetricReader–>对metrics进行分组–> 相同前缀的为1组.代码如下:
public interface PrefixMetricReader {
// 获得Metric的名字是给定的prefix 开头的
Iterable<Metric<?>> findAll(String prefix);
}
BufferMetricReader
BufferMetricReader–> 使用CounterBuffers,GaugeBuffers 来实现MetricReader.
字段,构造器如下:
private static final Predicate<String> ALL = Pattern.compile(".*").asPredicate(); private final CounterBuffers counterBuffers; private final GaugeBuffers gaugeBuffers; public BufferMetricReader(CounterBuffers counterBuffers, GaugeBuffers gaugeBuffers) { this.counterBuffers = counterBuffers; this.gaugeBuffers = gaugeBuffers; }
方法实现如下:
findOne,代码如下:
public Metric<?> findOne(final String name) { // 1. 从CounterBuffers中查找,如果不存在,则在gaugeBuffers中查找 Buffer<?> buffer = this.counterBuffers.find(name); if (buffer == null) { buffer = this.gaugeBuffers.find(name); } // 2.如果找到了,则将其转换为Metric return (buffer == null ? null : asMetric(name, buffer)); }
- 从CounterBuffers中查找,如果不存在,则在gaugeBuffers中查找
如果找到了,则将其转换为Metric.代码如下:
private <T extends Number> Metric<T> asMetric(final String name, Buffer<T> buffer) { return new Metric<T>(name, buffer.getValue(), new Date(buffer.getTimestamp())); }
findAll,findAll(String prefix) 代码如下:
public Iterable<Metric<?>> findAll() { return findAll(BufferMetricReader.ALL); } public Iterable<Metric<?>> findAll(String prefix) { return findAll(Pattern.compile(prefix + ".*").asPredicate()); }
它们的区别就是传入的正则不同而已,最终都会调用如下代码:
private Iterable<Metric<?>> findAll(Predicate<String> predicate) { final List<Metric<?>> metrics = new ArrayList<Metric<?>>(); collectMetrics(this.gaugeBuffers, predicate, metrics); collectMetrics(this.counterBuffers, predicate, metrics); return metrics; }
- 从GaugeBuffers中查找名字会指定前缀的Buffer,最终将其转换为Metric加入到结果集中
- 从CounterBuffers中查找名字会指定前缀的Buffer,最终将其转换为Metric加入到结果集中
collectMetrics,代码如下:
private <T extends Number, B extends Buffer<T>> void collectMetrics( Buffers<B> buffers, Predicate<String> predicate, final List<Metric<?>> metrics) { buffers.forEach(predicate, new BiConsumer<String, B>() { @Override public void accept(String name, B value) { // 如果符合给定的前缀则进行添加 metrics.add(asMetric(name, value)); } }); }
count–>返回CounterBuffers和GaugeBuffers的总数.代码如下:
public long count() { return this.counterBuffers.count() + this.gaugeBuffers.count(); }
自动装配:
声明在FastMetricServicesConfiguration中,代码如下:
@Bean @ExportMetricReader @ConditionalOnMissingBean public BufferMetricReader actuatorMetricReader(CounterBuffers counters, GaugeBuffers gauges) { return new BufferMetricReader(counters, gauges); }
满足以下条件时生效:
- 在JDK1.8及以上的环境中运行
- BeanFactory中不存在BufferMetricReader类型的bean时生效
PrefixMetricWriter
PrefixMetricWriter–> 允许有效的存储以 前缀分组后的metrics.代码如下:
public interface PrefixMetricWriter {
// 保存一些metric的值并且与组名进行管理
void set(String group, Collection<Metric<?>> values);
// 增加给定的测量值(或者减少,如果给定的delta为负数的话).增长的metric 名字为 group.delta.name
void increment(String group, Delta<?> delta);
// 重置指定组所对应的所有metrics的值.实现类可能会选择直接丢弃旧的值
void reset(String group);
}
MultiMetricRepository
MultiMetricRepository–>1个Metric的仓库–>允许有效的存储和检索拥有共同名称前缀的Metric.代码如下:
public interface MultiMetricRepository extends PrefixMetricReader, PrefixMetricWriter {
// 返回该repository中所有的组名
Iterable<String> groups();
// 返回该repository 中的组的数量
long countGroups();
}
InMemoryMultiMetricRepository
InMemoryMultiMetricRepository–> 通过InMemoryMetricRepository 来实现MultiMetricRepository.
字段,构造器如下:
private final InMemoryMetricRepository repository; // 保存着 group 名 private final Collection<String> groups = new HashSet<String>(); public InMemoryMultiMetricRepository() { this(new InMemoryMetricRepository()); } public InMemoryMultiMetricRepository(InMemoryMetricRepository repository) { Assert.notNull(repository, "Repository must not be null"); this.repository = repository; }
方法实现如下:
set,代码如下:
public void set(String group, Collection<Metric<?>> values) { // 1. 对给定的组名加上.(如果没有的话) String prefix = group; if (!prefix.endsWith(".")) { prefix = prefix + "."; } // 2. 遍历values,如果metric的名称不是以指定前缀开头的,则实例化1个Metric,其值为前缀+原metric名称,其它值都是复制的, // 然后加入到repository中 for (Metric<?> metric : values) { if (!metric.getName().startsWith(prefix)) { metric = new Metric<Number>(prefix + metric.getName(), metric.getValue(), metric.getTimestamp()); } this.repository.set(metric); } // 3. 将组名加入到groups 中 this.groups.add(group); }
- 对给定的组名加上.(如果没有的话)
- 遍历values,如果metric的名称不是以指定前缀开头的,则实例化1个Metric,其值为前缀+原metric名称,其它值都是复制的,然后加入到repository中
- 将组名加入到groups 中
increment,代码如下:
public void increment(String group, Delta<?> delta) { // 1. 对给定的组名加上.(如果没有的话) String prefix = group; if (!prefix.endsWith(".")) { prefix = prefix + "."; } // 2. 如果指定的Delta的名字不是以指定的group开头的,则实例化1个Delta,名字为group+原Delta的名字 // ,其余直接复制 if (!delta.getName().startsWith(prefix)) { delta = new Delta<Number>(prefix + delta.getName(), delta.getValue(), delta.getTimestamp()); } // 3. 如果InMemoryMetricRepository中存在指定名字的Delta,则直接增长给定的幅度 // 否则,直接加入到InMemoryMetricRepository中 this.repository.increment(delta); // 4. 将group加入到groups中 this.groups.add(group); }
- 对给定的组名加上.(如果没有的话)
- 如果指定的Delta的名字不是以指定的group开头的,则实例化1个Delta,名字为group+原Delta的名字,其余直接复制
- 如果InMemoryMetricRepository中存在指定名字的Delta,则直接增长给定的幅度.否则,直接加入到InMemoryMetricRepository中
- 将group加入到groups中
groups,如下:
public Iterable<String> groups() { return Collections.unmodifiableCollection(this.groups); }
countGroups,如下:
public long countGroups() { return this.groups.size(); }
reset,代码如下:
public void reset(String group) { // 1. 获取指定前缀的Metric,依次进行删除 for (Metric<?> metric : findAll(group)) { this.repository.reset(metric.getName()); } // 2. 从groups中删除 this.groups.remove(group); }
findAll,代码如下:
public Iterable<Metric<?>> findAll(String metricNamePrefix) { return this.repository.findAllWithPrefix(metricNamePrefix); }
自动装配:
在LegacyMetricRepositoryConfiguration中进行了装配,代码如下:
@Configuration @ConditionalOnJava(value = JavaVersion.EIGHT, range = Range.OLDER_THAN) @ConditionalOnMissingBean(name = "actuatorMetricRepository") static class LegacyMetricRepositoryConfiguration { ... @Bean @ExportMetricReader @ActuatorMetricWriter public InMemoryMultiMetricRepository actuatorMultiMetricRepository( InMemoryMetricRepository actuatorMetricRepository) { return new InMemoryMultiMetricRepository(actuatorMetricRepository); } }
关于这个我们在spring boot 源码解析40-CounterService,GaugeService默认自动装配解析中有介绍,这里就不赘述了
RedisMultiMetricRepository
RedisMultiMetricRepository–>使用redis来实现.Metric的值被存储到zset中,时间戳存储到string 中,其key都是前缀(默认是spring.groups.)+组名.在zset中对应的集合名默认是由keys.+前缀组成的
注意,此类是不会进行自动装配的
字段如下:
private static final String DEFAULT_METRICS_PREFIX = "spring.groups."; // 前缀默认是spring.groups. private final String prefix; // zSet 对应的集合名,默认为keys.spring.groups private final String keys; private final BoundZSetOperations<String, String> zSetOperations; private final RedisOperations<String, String> redisOperations;
构造器如下:
public RedisMultiMetricRepository(RedisConnectionFactory redisConnectionFactory) { this(redisConnectionFactory, DEFAULT_METRICS_PREFIX); } public RedisMultiMetricRepository(RedisConnectionFactory redisConnectionFactory, String prefix) { Assert.notNull(redisConnectionFactory, "RedisConnectionFactory must not be null"); // 1. 实例化RedisOperations this.redisOperations = RedisUtils.stringTemplate(redisConnectionFactory); // 2.如果指定的前置不是.结尾的,则为其加上. 并将其赋值给prefix if (!prefix.endsWith(".")) { prefix = prefix + "."; } this.prefix = prefix; // 3. 在指定的prefix加上keys.前缀生成keys this.keys = "keys." + this.prefix.substring(0, prefix.length() - 1); // 4. 获得以keys为zset集合名称对应的BoundZSetOperations this.zSetOperations = this.redisOperations.boundZSetOps(this.keys); }
实现方法如下:
set,代码如下:
public void set(String group, Collection<Metric<?>> values) { // 1. 生成groupkey-->对group 加上前缀 String groupKey = keyFor(group); // 2.存入groupKey对应的zset中,key-->groupKey,值为-->0.0D trackMembership(groupKey); // 3. 获得groupKey对应的BoundZSetOperations 对象 BoundZSetOperations<String, String> zSetOperations = this.redisOperations .boundZSetOps(groupKey); // 3. 依次遍历Metrics for (Metric<?> metric : values) { // 3.1 将Metric对应的时间戳变为字符串格式 String raw = serialize(metric); // 3.2 将metric的名字加上前缀以生成key String key = keyFor(metric.getName()); // 3.3 添加到groupKey 对应的zset中,key-->metric的名字加上前缀以生成key,value-->测量值 zSetOperations.add(key, metric.getValue().doubleValue()); // 3.4 添加到String结构中,key--> metric的名字加上前缀以生成key,value-->Metric对应的时间戳变为字符串格式 this.redisOperations.opsForValue().set(key, raw); } }
生成groupkey–>对group 加上前缀.代码如下:
private String keyFor(String name) { return this.prefix + name; }
存入groupKey对应的zset中,key–>groupKey,值为–>0.0D.代码如下:
private void trackMembership(String redisKey) { this.zSetOperations.incrementScore(redisKey, 0.0D); }
- 获得groupKey对应的BoundZSetOperations 对象
依次遍历Metrics
将Metric对应的时间戳变为字符串格式.代码如下:
private String serialize(Metric<?> entity) { return String.valueOf(entity.getTimestamp().getTime()); }
- 将metric的名字加上前缀以生成key
- 添加到groupKey 对应的zset中,key–>metric的名字加上前缀以生成key,value–>测量值
- 添加到String结构中,key–> metric的名字加上前缀以生成key,value–>Metric对应的时间戳变为字符串格式
increment,代码如下:
public void increment(String group, Delta<?> delta) { // 1. 生成groupkey-->对group 加上前缀 String groupKey = keyFor(group); // 2.存入groupKey对应的zset中,key-->groupKey,值为-->0.0D trackMembership(groupKey); // 3. 获得groupKey对应的BoundZSetOperations 对象 BoundZSetOperations<String, String> zSetOperations = this.redisOperations .boundZSetOps(groupKey); // 4. 将metric的名字加上前缀以生成key String key = keyFor(delta.getName()); // 5. 向groupKey 对应的zset中,key为metric的名字加上前缀的值进行增加,幅度为Delta的值 double value = zSetOperations.incrementScore(key, delta.getValue().doubleValue()); // 6. 将Metric对应的时间戳变为字符串格式 String raw = serialize( new Metric<Double>(delta.getName(), value, delta.getTimestamp())); // 7. 添加到String结构中,key--> metric的名字加上前缀以生成key,value-->Metric对应的时间戳变为字符串格式 this.redisOperations.opsForValue().set(key, raw); }
reset,代码如下:
public void reset(String group) { // 1. 生成groupkey-->对group 加上前缀 String groupKey = keyFor(group); // 2. 如果redis中有对应的key的话 if (this.redisOperations.hasKey(groupKey)) { // 3. 获得zset中集合名为groupKey的操作对象-->BoundZSetOperations BoundZSetOperations<String, String> zSetOperations = this.redisOperations .boundZSetOps(groupKey); // 4. 获得zset中的所有值,依次从redis中删除 Set<String> keys = zSetOperations.range(0, -1); for (String key : keys) { this.redisOperations.delete(key); } // 5. 从redis中删除groupKey this.redisOperations.delete(groupKey); } // 6. 删除groupKey所对应的zset集合 this.zSetOperations.remove(groupKey); }
groups,代码如下:
public Iterable<String> groups() { // 1. 获得keys.spring.groups 对应的zset中所有的数据 Set<String> range = this.zSetOperations.range(0, -1); Collection<String> result = new ArrayList<String>(); // 2. 依次遍历之,去除前缀后加入到result中 for (String key : range) { result.add(key.substring(this.prefix.length())); } return result; }
findAll,代码如下:
public Iterable<Metric<?>> findAll(String group) { // 1. 对group加入前缀后获得对应的zset的操作对象-->BoundZSetOperations BoundZSetOperations<String, String> zSetOperations = this.redisOperations .boundZSetOps(keyFor(group)); // 2. 获得其zset 存储的所有key,遍历处理之 Set<String> keys = zSetOperations.range(0, -1); Iterator<String> keysIt = keys.iterator(); List<Metric<?>> result = new ArrayList<Metric<?>>(keys.size()); // 3. 获得redis中key为zset 存储的所有key 所对应的值,遍历之 List<String> values = this.redisOperations.opsForValue().multiGet(keys); for (String v : values) { String key = keysIt.next(); // 4. 序列化后加入到result中 result.add(deserialize(group, key, v, zSetOperations.score(key))); } return result; }
countGroups,代码如下:
public long countGroups() { return this.zSetOperations.size(); }