С DevOps метрики начинают быть среди нефункциональных требований, которые любое приложение должно принести в область. Прежде чем идти дальше, я хотел бы сделать несколько замечаний:
- Метрики не только о нефункциональных вещах. Многие показатели представляют очень важный KPI для бизнеса. Например, для интернет-магазина бизнес должен знать, сколько клиентов покидают процесс оформления заказа и на каком экране. Правда, есть несколько решений для достижения этой цели, хотя все они основаны на веб-технологиях (Google Analytics приходит на ум) и метрики могут также потребоваться для разных архитектур. И наличие всех метрик в одном бэкэнде означает, что их можно легко соотнести.
- Метрики, как и любая другая NFR ( например, регистрация и обработка исключений), должны разрабатываться и управляться заранее, а не выдвигаться в качестве запоздалой мысли. Откуда я это знаю? Ну, один из моих последних проектов был сосредоточен только на функциональных требованиях, и только в конце концов руководство проекта осознало важность NFR. Поверьте мне, когда я говорю, что это было ужасно — и это стоило намного дороже, чем если бы оно было разработано на ранних этапах проекта.
- Метрики имеют накладные расходы. Однако без метрик невозможно повысить производительность. Просто прими это и живи с этим.
Входные данные следующие: приложение основано на Spring MVC, а показатели должны быть агрегированы в графите . Мы начнем с использования отличного проекта Metrics : он не только выполняет свою работу, его документация очень высокого качества и доступна под дружественной лицензией OpenSource Apache v2.0.
Тем не менее, давайте представим «стандартную» базовую архитектуру для управления этими компонентами.
Во-первых, хотя Metrics предлагает конечную точку Graphite, это требует настройки в каждой среде, что усложняет работу, особенно на рабочих станциях разработчиков. Для этого мы отправим метрики в JMX и представим jmxtrans в качестве промежуточного компонента между JMX и графитом. Поскольку каждая JVM предоставляет службы JMX, это не требует настройки, когда в этом нет необходимости, и не влияет на производительность.
Во-вторых, как разработчики, нам обычно нравится разрабатывать все с нуля, чтобы показать, насколько мы хороши — или иногда потому, что они не просматривали документацию. Моя точка зрения, как инженера-программиста, заключается в том, что я бы не стал изобретать велосипед и сосредоточиться на задаче в конце. Фактически Spring Boot уже интегрируется с Metrics через компонент Actuator. Однако он предоставляет только GaugeService — для отправки уникальных значений, а CounterService — для увеличения / уменьшения значений. Это может быть достаточно для FR, но не для NFR, поэтому мы могли бы немного подправить.
Поток будет выглядеть так: Код> Spring Boot> Метрики> JMX> Графит
Отправной точкой является создание аспекта, поскольку показатель производительности является сквозной задачей:
@Aspect
public class MetricAspect {
private final MetricSender metricSender;
@Autowired
public MetricAspect(MetricSender metricSender) {
this.metricSender = metricSender;
}
@Around("execution(* ch.frankel.blog.metrics.ping..*(..)) ||execution(* ch.frankel.blog.metrics.dice..*(..))")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
StopWatch stopWatch = metricSender.getStartedStopWatch();
try {
return pjp.proceed();
} finally {
Class<?> clazz = pjp.getTarget().getClass();
String methodName = pjp.getSignature().getName();
metricSender.stopAndSend(stopWatch, clazz, methodName);
}
}
}
Единственное, что выходит за рамки обычного — это использование автопроводки, так как аспекты, кажется, не могут быть целью явной разводки (пока?). Также обратите внимание, что сам аспект не взаимодействует с Metrics API, он делегирует только выделенному компоненту:
public class MetricSender {
private final MetricRegistry registry;
public MetricSender(MetricRegistry registry) {
this.registry = registry;
}
private Histogram getOrAdd(String metricsName) {
Map<String, Histogram> registeredHistograms = registry.getHistograms();
Histogram registeredHistogram = registeredHistograms.get(metricsName);
if (registeredHistogram == null) {
Reservoir reservoir = new ExponentiallyDecayingReservoir();
registeredHistogram = new Histogram(reservoir);
registry.register(metricsName, registeredHistogram);
}
return registeredHistogram;
}
public StopWatch getStartedStopWatch() {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
return stopWatch;
}
private String computeMetricName(Class<?> clazz, String methodName) {
return clazz.getName() + '.' + methodName;
}
public void stopAndSend(StopWatch stopWatch, Class<?> clazz, String methodName) {
stopWatch.stop();
String metricName = computeMetricName(clazz, methodName);
getOrAdd(metricName).update(stopWatch.getTotalTimeMillis());
}
}
Отправитель делает несколько интересных вещей (но без состояния):
- Возвращает новый
StopWatchдля аспекта, чтобы передать обратно после выполнения метода - Он вычисляет имя метрики в зависимости от класса и метода
- Он останавливает
StopWatchи отправляет времяMetricRegistry - Обратите внимание , что также лениво создает и регистрирует новый
HistogramсExponentiallyDecayingReservoirэкземпляром. Поведение по умолчанию — предоставитьUniformReservoir, который хранит данные вечно и не подходит для наших нужд.
Последний шаг — сообщить Metrics API о необходимости отправки данных в JMX. Это можно сделать в одном из классов конфигурации, предпочтительно в классе, предназначенном для метрик, с использованием @PostConstruct аннотации для желаемого метода.
@Configuration
public class MetricsConfiguration {
@Autowired
private MetricRegistry metricRegistry;
@Bean
public MetricSender metricSender() {
return new MetricSender(metricRegistry);
}
@PostConstruct
public void connectRegistryToJmx() {
JmxReporter reporter = JmxReporter.forRegistry(metricRegistry).build();
reporter.start();
}
}
JConsole должна выглядеть следующим образом. Обледенение, все метрики Spring Boot по умолчанию также доступны:
Источники для этой статьи доступны в «формате» Maven.
Чтобы идти дальше:
- Общая документация Code Hale’s Metrics
- Особенности гистограммы и резервуара
- Spring Boot общая документация
- Интеграция Spring Boot и Metrics
