1 Обзор
Основные сведения о метриках см. в предыдущих статьях:Метрики — сервисные метрики.
В этой статье кратко рассказывается, как интегрировать мониторинг метрик в наш проект.
В данном документе используется Metrics-Core версии 3.1.0.
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.1.0</version>
</dependency>
2. Сцена
Наши основные потребности в мониторинге следующие:
- Метрики машины
Базовая информация, такая как память, потоки, жесткий диск и служба GC, являются основными показателями, которые нас интересуют. мы можем рассмотретьGaugeИндикаторный элемент собирает эти машинные индикаторы унифицированным образом.
- Частота и время запроса сервисного интерфейса
Потребление частоты и времени запроса являются основными показателями нашего сервисного интерфейса. Мы можем рассмотреть возможность использованияTimerиндикаторы для сбора соответствующей информации.
- Основные данные внутри сервиса
В некоторых сценариях мы сообщаем мгновенные показатели, собранные внутренней службой, такие как количество запросов, которые в настоящее время обрабатываются в веб-фильтре. Мы также можем использоватьGaugeметрики для сбора.
3. Схема
Для приведенных выше сценариев, хотя мы можем создать и зарегистрировать соответствующие сервисные индикаторы, написав код, это не очень удобно в использовании. Как удобнее и гибче интегрировать статистику показателей Metrics в наш проект?
3.1. MetricSet автоматически регистрируется для сбора метрик машины
- (1) MetricSet предопределен;
Коллекцию метрик MetricSet можно найти в библиотеке metrics-jvm.MemoryUsageGaugeSetЧтобы определить, MemoryUsageGaugeSet определяет основные показатели использования памяти, как показано ниже.
/**
* A set of gauges for JVM memory usage, including stats on heap vs. non-heap memory, plus
* GC-specific memory pools.
*/
public class MemoryUsageGaugeSet implements MetricSet {
private static final Pattern WHITESPACE = Pattern.compile("[\\s]+");
private final MemoryMXBean mxBean;
private final List<MemoryPoolMXBean> memoryPools;
public MemoryUsageGaugeSet() {
this(ManagementFactory.getMemoryMXBean(),
ManagementFactory.getMemoryPoolMXBeans());
}
public MemoryUsageGaugeSet(MemoryMXBean mxBean,
Collection<MemoryPoolMXBean> memoryPools) {
this.mxBean = mxBean;
this.memoryPools = new ArrayList<MemoryPoolMXBean>(memoryPools);
}
@Override
public Map<String, Metric> getMetrics() {
final Map<String, Metric> gauges = new HashMap<String, Metric>();
gauges.put("total.init", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getHeapMemoryUsage().getInit() +
mxBean.getNonHeapMemoryUsage().getInit();
}
});
gauges.put("total.used", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getHeapMemoryUsage().getUsed() +
mxBean.getNonHeapMemoryUsage().getUsed();
}
});
gauges.put("total.max", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getHeapMemoryUsage().getMax() +
mxBean.getNonHeapMemoryUsage().getMax();
}
});
gauges.put("total.committed", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getHeapMemoryUsage().getCommitted() +
mxBean.getNonHeapMemoryUsage().getCommitted();
}
});
gauges.put("heap.init", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getHeapMemoryUsage().getInit();
}
});
gauges.put("heap.used", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getHeapMemoryUsage().getUsed();
}
});
gauges.put("heap.max", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getHeapMemoryUsage().getMax();
}
});
gauges.put("heap.committed", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getHeapMemoryUsage().getCommitted();
}
});
gauges.put("heap.usage", new RatioGauge() {
@Override
protected Ratio getRatio() {
final MemoryUsage usage = mxBean.getHeapMemoryUsage();
return Ratio.of(usage.getUsed(), usage.getMax());
}
});
gauges.put("non-heap.init", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getNonHeapMemoryUsage().getInit();
}
});
gauges.put("non-heap.used", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getNonHeapMemoryUsage().getUsed();
}
});
gauges.put("non-heap.max", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getNonHeapMemoryUsage().getMax();
}
});
gauges.put("non-heap.committed", new Gauge<Long>() {
@Override
public Long getValue() {
return mxBean.getNonHeapMemoryUsage().getCommitted();
}
});
gauges.put("non-heap.usage", new RatioGauge() {
@Override
protected Ratio getRatio() {
final MemoryUsage usage = mxBean.getNonHeapMemoryUsage();
return Ratio.of(usage.getUsed(), usage.getMax());
}
});
for (final MemoryPoolMXBean pool : memoryPools) {
gauges.put(name("pools",
WHITESPACE.matcher(pool.getName()).replaceAll("-"),
"usage"),
new RatioGauge() {
@Override
protected Ratio getRatio() {
final long max = pool.getUsage().getMax() == -1 ?
pool.getUsage().getCommitted() :
pool.getUsage().getMax();
return Ratio.of(pool.getUsage().getUsed(), max);
}
});
}
return Collections.unmodifiableMap(gauges);
}
}
- (2) пройтиBeanPostProcessorПроцессор автоматически регистрирует объект Bean MetricSet;
public class UserDefinedMetricBeanPostProcessor implements BeanPostProcessor {
private final Logger LOG = LoggerFactory.getLogger(getClass());
private final MetricRegistry metrics = MetricBeans.getRegistry();
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MetricSet) {
MetricSet metricSet = (MetricSet) bean;
if (!canRegister(beanName)) {
return bean;
}
String metricName;
if (isJvmCollector(beanName)) {
metricName = Config.getProjectPrefix() + "." + beanName;
} else {
//根据规则生成Metric的名字
metricName = Util.forMetricBean(bean.getClass(), beanName);
}
try {
metrics.register(metricName, metricSet);
LOG.debug("Registered metric named {} in registry. class: {}.", metricName, metricSet);
} catch (IllegalArgumentException ex) {
LOG.warn("Error injecting metric for field. bean named {}.", metricName, ex);
}
}
return bean;
}
private boolean isJvmCollector(String beanName) {
return beanName.indexOf("jvm") != -1;
}
private boolean canRegister(String beanName) {
return !isJvmCollector(beanName) || Config.canJvmCollectorStart();
}
}
- (3) Определить объекты bean-компонентов в файлах spring xml или с помощью аннотаций spring;
<!--定义Jvm监控对象-->
<bean id="jvm.memory" class="com.codahale.metrics.jvm.MemoryUsageGaugeSet"/>
<!--自动添加用户定义的监控对象Metric-->
<bean class="com.test.metrics.collector.UserDefinedMetricBeanPostProcessor"/>
Коллекцию MetricSet можно настроить по мере необходимости, чтобы реализовать автоматическую регистрацию и отчетность по метрикам службы.
3.2 в сочетании с аннотациями для автоматической регистрации переменных-членов.
Мы можем комбинировать аннотации для автоматической регистрации переменных-членов. Аннотации переменных-членов могут быть получены в BeanPostProcessor.Если используются наши целевые аннотации, информация о переменных может быть получена путем отражения для автоматической регистрации.
Ниже в качестве примера используется аннотация Gauged.Аннотация Gauged позволяет автоматически регистрировать переменные-члены и сообщать о них.
- (1) определение калибровочной аннотации;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
public @interface Gauged {
String name() default "";
}
- (2) использоватьBeanPostProcessorРазбирать аннотации Gauge и регистрировать;
Основной код выглядит следующим образом:
protected void withField(final Object bean, String beanName, Class<?> targetClass, final Field field) {
ReflectionUtils.makeAccessible(field);
final Gauged annotation = field.getAnnotation(Gauged.class);
final String metricName = Util.forGauge(targetClass, field, annotation);
metrics.register(metricName, new Gauge<Object>() {
@Override
public Object getValue() {
return ReflectionUtils.getField(field, bean);
}
});
LOG.debug("Created gauge {} for field {}.{}", metricName, targetClass.getCanonicalName(), field.getName());
}
- (3) Определите соответствие в файле spring.xmlBeanPostProcessorготовые к использованию.
Основное использование заключается в следующем:
@Component
public class GaugeUsage {
@Gauged(name = "gaugeField")
private int gaugedField = 999;
}
3.3. В сочетании с аннотациями для получения статистики перехвата аспектов метода
Отнимающую много времени статистику вызовов интерфейса можно реализовать на основе Spring AOP.
Ниже в качестве примера используется аннотация Timed, которая может подсчитывать время, затрачиваемое методами интерфейса.
- (1) сроки определенные аннотации;
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface Timed {
String name() default "";
}
- (2) Определение аспекта временной аннотации;
@Component
@Aspect
public class MetricAspect {
@Around("@annotation(timed)")
public Object processTimerAnnotation(ProceedingJoinPoint joinPoint, Timed timed) throws Throwable {
Class clazz = joinPoint.getTarget().getClass();
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
String metricName = Util.forTimedMethod(clazz, method, timed);
Timer timer = MetricBeans.timer(metricName);
final Timer.Context context = timer.time();
try {
return joinPoint.proceed();
} finally {
context.stop();
}
}
}
- (3) Определение MetricAspect в файле spring.xml может реализовать частоту запросов и статистику затрат времени интерфейса с аннотацией Timed.
Основной пример выглядит следующим образом:
@Component
public class TimedUsage {
//@Timed注解会让监控组件创建Timer对象,统计该方法的执行次数和执行时间等指标
@Timed(name = "simple-timed-method")
public void timedMethod() {
for (int i = 0; i < 1000; i++) {
}
}
}
4. Резюме
В настоящее время мы в основном проходимBenPostProcessorиSpring AOPПерехватка экземпляров классов, тем самым реализующая автоматическую регистрацию и коллекцию индикаторов обслуживания.