您的位置:首页 > 编程语言 > Java开发

spring boot 源码解析44-PrefixMetricReader,PrefixMetricWriter,MultiMetricRepository

2018-01-26 01:14 926 查看

前言

本文我们来分析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());
}


它们的区别就是传入的正则不同而已,最终都会调用如下
4000
代码:

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();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring 源码