前言
从本文开始,我们开始分析org.springframework.boot.actuate.metrics包中的实现.这里面的代码比较多,因此我们采用一步一步的方式来进行分析.本文先分析CounterService相关的类(DropwizardMetricServices这个实现我们后续分析) 类图如下:
ps:关于CounterService的自动装配,我们在解析完GaugeService之后再介绍.
解析
CounterService
CounterService–> 1个可以增加,减少,重置的命名的计数器服务.其声明的方法如下:
// 计数器加1
void increment(String metricName);
// 计数器减1
void decrement(String metricName);
// 重置给定的技术器
void reset(String metricName);
MetricWriter
MetricWriter继承自GaugeWriter,CounterWriter接口.没有声明其他的方法,只是1个合并接口.
GaugeWriter
GaugeWriter–>测量值的写出接口.声明了如下方法:
/**
* Set the value of a metric.
* @param value the value
*/
void set(Metric<?> value);
Delta
Delta–> 1个可增长的测量值所对应的值对象(通常作为计数器).代码如下:
public class Delta<T extends Number> extends Metric<T> {
public Delta(String name, T value, Date timestamp) {
super(name, value, timestamp);
}
public Delta(String name, T value) {
super(name, value);
}
}
CounterWriter
CounterWriter–>计数器的简单 writer 接口.声明了如下方法:
// 增加当前metric的值(或者减少,如果Delta 是负数的话). Delta 中指定的name指定了要增加的metric的名字
void increment(Delta<?> delta);
// 重置,通常会置为0.该操作是可选的(一些实现可能无法实现该契约--> 什么也没有做)
void reset(String metricName);
SimpleInMemoryRepository
SimpleInMemoryRepository–> 在内存中存储数据的工具类.该类还是一个泛型类,其泛型参数T为存储类型
字段如下:
// key-->metricName,value--> T private ConcurrentNavigableMap<String, T> values = new ConcurrentSkipListMap<String, T>(); // key--> metricName,value-->Object(用于加锁) private final ConcurrentMap<String, Object> locks = new ConcurrentHashMap<String, Object>();
该类还声明1个接口–> Callback,作用是用来修改值的回调接口,其泛型参数T为值的类型.如下:
public interface Callback<T> { // 修改给定的值 T modify(T current); }
声明了如下方法:
getLock–>用于获得给定名称所对应的锁.代码如下:
private Object getLock(String name) { Object lock = this.locks.get(name); if (lock == null) { Object newLock = new Object(); lock = this.locks.putIfAbsent(name, newLock); if (lock == null) { lock = newLock; } } return lock; }
从缓存中获取,如果获取到则直接返回,如果获取不到则实例化1个Object放入缓存中,然后返回
update–> 对指定的name所对应的值,调用传入的Callback进行修改,然后放入缓存中.代码如下:
public T update(String name, Callback<T> callback) { Object lock = getLock(name); synchronized (lock) { T current = this.values.get(name); T value = callback.modify(current); this.values.put(name, value); return value; } }
findAllWithPrefix–> 通过指定的前缀进行查找.代码如下:
public Iterable<T> findAllWithPrefix(String prefix) { if (prefix.endsWith(".*")) { prefix = prefix.substring(0, prefix.length() - 1); } if (!prefix.endsWith(".")) { prefix = prefix + "."; } return new ArrayList<T>( // 不包括头,含为-->(xx] this.values.subMap(prefix, false, prefix + "~", true).values()); }
- 如果prefix是.* 结尾的,则进行截取.如prefix为aa.*,则会截取为aa.
- 如果prefix不是.结尾的,则为其加上.
- 获得value中key在prefix到prefix~范围中的值,左开右闭
还有其他的方法,比较简单,这里就不在赘述了
InMemoryMetricRepository
1个在内存中存储metrics的MetricRepository的实现
字段如下:
private final SimpleInMemoryRepository<Metric<?>> metrics = new SimpleInMemoryRepository<Metric<?>>();
其接口的方法实现如下:
increment,代码如下:
public void increment(Delta<?> delta) { final String metricName = delta.getName(); final int amount = delta.getValue().intValue(); final Date timestamp = delta.getTimestamp(); this.metrics.update(metricName, new Callback<Metric<?>>() { @Override public Metric<?> modify(Metric<?> current) { if (current != null) { return new Metric<Long>(metricName, current.increment(amount).getValue(), timestamp); } return new Metric<Long>(metricName, (long) amount, timestamp); } }); }
- 从metrics中获得Delta所对应的Metric
- 如果Metric存在的话,则直接实例化1个Metric,名字,时间戳不变,值则在原先的基础上加上传入的Delta的值
- 如果不存在,则直接根据传入Delta的名称,时间戳,值实例化1个Metric,加入到metrics的values中
set 实现如下:
public void set(Metric<?> value) { this.metrics.set(value.getName(), value); }
调用SimpleInMemoryRepository的 set方法直接保存至SimpleInMemoryRepository持有的values中.代码如下:
public void set(String name, T value) { this.values.put(name, value); }
count–>返回SimpleInMemoryRepository中持有的values的大小.实现如下:
public long count() { return this.metrics.count(); }
调用:
public long count() { return this.values.size(); }
reset–> 从SimpleInMemoryRepository中的values删除.代码如下:
public void reset(String metricName) { this.metrics.remove(metricName); }
调用:
public void remove(String name) { this.values.remove(name); }
findOne–>从SimpleInMemoryRepository中的values中查找,代码如下:
public Metric<?> findOne(String metricName) { return this.metrics.findOne(metricName); }
调用:
public T findOne(String name) { return this.values.get(name); }
findAll –> 获得SimpleInMemoryRepository中values的所有值.代码如下:
public Iterable<Metric<?>> findAll() { return this.metrics.findAll(); }
findAllWithPrefix–> 从SimpleInMemoryRepository的values中查找前缀为prefix的Metric.代码如下:
public Iterable<Metric<?>> findAllWithPrefix(String prefix) { return this.metrics.findAllWithPrefix(prefix); }
自动装配:
在LegacyMetricRepositoryConfiguration中进行了装配.代码如下:
@Configuration @ConditionalOnJava(value = JavaVersion.EIGHT, range = Range.OLDER_THAN) @ConditionalOnMissingBean(name = "actuatorMetricRepository") static class LegacyMetricRepositoryConfiguration { @Bean @ExportMetricReader @ActuatorMetricWriter public InMemoryMetricRepository actuatorMetricRepository() { return new InMemoryMetricRepository(); } ..... }
当满足以下条件时生效:
- @ConditionalOnJava(value = JavaVersion.EIGHT, range = Range.OLDER_THAN) –> 在jdk1.8之前的运行环境下运行
- @ConditionalOnMissingBean(name = “actuatorMetricRepository”)–> beanFactory中不存在id为actuatorMetricRepository的bean
其中: @ExportMetricReader 如下:
@Qualifier @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface ExportMetricReader { }
@ActuatorMetricWriter如下:
@Qualifier @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface ActuatorMetricWriter { }
DefaultCounterService
DefaultCounterService –> 在jdk1.8之前默认装配的CounterService的实现
字段,构造器如下:
// 此时注入的是InMemoryMetricRepository private final MetricWriter writer; // key-->metricName,value-->加入前缀后的metricName private final ConcurrentHashMap<String, String> names = new ConcurrentHashMap<String, String>(); public DefaultCounterService(MetricWriter writer) { this.writer = writer; }
方法实现如下:
increment,代码如下:
public void increment(String metricName) { this.writer.increment(new Delta<Long>(wrap(metricName), 1L)); }
尝试为metricName加上前缀.代码如下:
private String wrap(String metricName) { String cached = this.names.get(metricName); if (cached != null) { return cached; } if (metricName.startsWith("counter.") || metricName.startsWith("meter.")) { return metricName; } String name = "counter." + metricName; this.names.put(metricName, name); return name; }
- 如果缓存中有的话,则直接返回对应的值
- 如果传入的metricName是counter.或者meter.开头的则直接返回
- 为metricName 加上counter.的前缀,放入到names缓存中,然后进行返回
- 实例化Delta
- 加入到InMemoryMetricRepository中
decrement,代码如下:
public void decrement(String metricName) { this.writer.increment(new Delta<Long>(wrap(metricName), -1L)); }
reset,代码如下:
public void reset(String metricName) { this.writer.reset(wrap(metricName)); }
Buffer
Buffer,抽象泛型类–>可变的buffer(含有时间戳和所对应的值)的基类.泛型参数为T extends Number–>其持有值的类型.代码如下:
abstract class Buffer<T extends Number> {
private volatile long timestamp;
Buffer(long timestamp) {
this.timestamp = timestamp;
}
public long getTimestamp() {
return this.timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
/**
* Returns the buffer value.
* @return the value of the buffer
*/
public abstract T getValue();
}
CounterBuffer
CounterBuffer–> 继承自Buffer,泛型参数为Long.其类上声明了@UsesJava8注解,表明该类是使用java8特有的api进行实现的,不意味着其严格要求java 8. 代码如下:
@UsesJava8// 表明该类是使用java8特有的api进行实现的,不意味着其严格要求java 8
public class CounterBuffer extends Buffer<Long> {
private final LongAdder adder;
public CounterBuffer(long timestamp) {
super(timestamp);
this.adder = new LongAdder();
}
public void add(long delta) {
this.adder.add(delta);
}
public void reset() {
this.adder.reset();
}
@Override
public Long getValue() {
return this.adder.sum();
}
}
关于LongAdder,可以参考如下链接:
Java 8 LongAdders:管理并发计数器的正确方式
Buffers
Buffers–> 管理1个Buffer对象的映射的抽象泛型基类.泛型参数为B extends Buffer
CounterBuffers
CounterBuffers–> 继承自Buffers,泛型参数为CounterBuffer.
createBuffer–> 直接实例化了CounterBuffer.代码如下:
protected CounterBuffer createBuffer() { return new CounterBuffer(0); }
此外,还声明了2个方法.如下:
increment–>对给定名字的CounterBuffer增长给定的幅度. 代码如下:
public void increment(final String name, final long delta) { doWith(name, new Consumer<CounterBuffer>() { @Override public void accept(CounterBuffer buffer) { buffer.setTimestamp(System.currentTimeMillis()); buffer.add(delta); } }); }
- 从父类中的buffers获得给定名字所对应的CounterBuffer,如果不存在,则创建1个
- 将CounterBuffer中的时间戳设为当前时间,并增加指定的步幅.
reset–> 对给定名字的CounterBuffer进行重置.代码如下:
public void reset(final String name) { doWith(name, new Consumer<CounterBuffer>() { @Override public void accept(CounterBuffer buffer) { buffer.setTimestamp(System.currentTimeMillis()); buffer.reset(); } }); }
- 从父类中的buffers获得给定名字所对应的CounterBuffer,如果不存在,则创建1个
- 将CounterBuffer中的时间戳设为当前时间,将其值设置为0
自动装配:
在FastMetricServicesConfiguration中进行了配置.代码如下:
@Configuration @ConditionalOnJava(JavaVersion.EIGHT) @ConditionalOnMissingBean(GaugeService.class) static class FastMetricServicesConfiguration { @Bean @ConditionalOnMissingBean public CounterBuffers counterBuffers() { return new CounterBuffers(); } }
当满足如下条件时该配置生效:
- @ConditionalOnJava(JavaVersion.EIGHT)–> 在jdk1.8的环境中运行
- @ConditionalOnMissingBean(GaugeService.class)–>BeanFactory中不存在GaugeService类型的bean时生效
- @ConditionalOnMissingBean–>BeanFactory中不存在CounterBuffers类型的bean时生效
BufferCounterService
BufferCounterService–>实现了CounterService接口.
字段,构造器如下:
// key --> 原始的名字,value --> buffer中的名字 private final ConcurrentHashMap<String, String> names = new ConcurrentHashMap<String, String>(); private final CounterBuffers buffers; public BufferCounterService(CounterBuffers buffers) { this.buffers = buffers; }
方法实现如下:
increment,代码如下:
public void increment(String metricName) { this.buffers.increment(wrap(metricName), 1L); }
尝试为metricName加上前缀.代码如下:
private String wrap(String metricName) { String cached = this.names.get(metricName); if (cached != null) { return cached; } if (metricName.startsWith("counter.") || metricName.startsWith("meter.")) { return metricName; } String name = "counter." + metricName; this.names.put(metricName, name); return name; }
- 如果缓存中有的话,则直接返回对应的值
- 如果传入的metricName是counter.或者meter.开头的则直接返回
- 为metricName 加上counter.的前缀,放入到names缓存中,然后进行返回
- 加入到CounterBuffers中
decrement,代码如下:
public void decrement(String metricName) { this.buffers.increment(wrap(metricName), -1L); }
reset,代码如下:
public void reset(String metricName) { this.buffers.reset(wrap(metricName)); }
CounterService使用案例
我们可以使用CounterService来完成对接口请求次数的统计.
新建CounterController,代码如下:
package com.example.demo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.metrics.CounterService; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class CounterController { @Autowired private CounterService counterService; @RequestMapping("/test-counter") public String testCounter() { counterService.increment("test-counter.count"); return "操作成功"; } }
我们访问如下链接后 http://127.0.0.1:8080/test-counter 后,访问 http://127.0.0.1:8080/metrics,即可发现counter.test-counter.count对应的统计次数为1.如下:
counter.test-counter.count: 1