В этой статье объясняется фоновая разработка Spring Web.
В предыдущей статье объяснялось, как использоватьjvisualvm
Мониторинг Java-программ.jvisualvm
Хотя он довольно мощный, он все же не отвечает нашим потребностям в практических приложениях. Теперь мы хотим отслеживать всеController
Количество обращений, частота и время отклика предоставленного интерфейса.Service
Время выполнения метода слоя, время выполнения, частота и т. д. Чтобы подготовиться к оптимизации производительности системы позже. В настоящее времяjvisualvm
Он больше не может удовлетворить наши потребности.
1 Мониторинг Java-программ на уровне методов
Это мое решение для Java-программы мониторинга на уровне методов:
- Платные, такие как YourKit, JProfile и т.д. Я попробовал YourKit, он действительно мощный, но производительность не является нашим текущим узким местом, мы стараемся использовать бесплатные.
- Метрика - Весна. Metrics-Spring требует аннотаций для каждого метода. Мы используем микросервисную архитектуру с более чем 20 сервисами, и ожидается, что каждый проект будет иметь в среднем около 100 методов для мониторинга. Я думаю, что это нормально, если он используется в первую очередь.
- Метрики + Spring АОП. Как видно из Metrics-Spring, статистическая информация Metrics в основном соответствует нашим потребностям. Наши требования к проекту - статистика
Controller
слой иService
слойный метод. Затем мы можем завершить наши потребности через аспекты в Spring.
Планы и анализ, которые я исследовал, в основном таковы: если у других есть планы получше, они могут обсудить их вместе.
Ниже приводится объяснение + часть кода, и в этом объяснении есть статьи по оптимизации.
2. Функции метрик
Было много статей об использовании метрик, и я порекомендую те из них, которые я считаю хорошими, всем здесь, а затем я представлю методы использования.
- Введение в метрики. В этой статье очень подробно представлены основные функции метрик.
- Официальная документация Metrics-Spring. В этой статье рассказывается об интеграции Metrics и Spring, но документация кажется неполной.
Я не буду много рассказывать о других статьях, я чувствую то же самое. Не большая разница.
3 Загрузите классы, связанные с метриками, в контейнер Spring.
Чтобы использовать Метрику, вам сначала нужноMetricRegistry
.
Нам нужно предоставить отчеты Http, поэтому нам нужноMetricsServlet
Зарегистрируйтесь в Spring, чтобы результаты мониторинга можно было получать через Http-интерфейс. В следующем коде мы определяем интерфейс мониторинга как:/monitor/metrics
.
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.servlets.MetricsServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MonitorConfig {
@Bean
public MetricRegistry metricRegistry() {
return new MetricRegistry();
}
@Bean
public ServletRegistrationBean servletRegistrationBean(MetricRegistry metricRegistry) {
return new ServletRegistrationBean(new MetricsServlet(metricRegistry), "/monitor/metrics");
}
}
4 предоставляет контролируемые отчеты терминала
Кроме того, чтобы облегчить отладку, я надеюсь, что способ поддерживать отчет о выводе терминала. Но можно настроить на включение и выключение, поэтому я использую другой класс конфигурации,ConditionalOnProperty
Аннотация для загрузки конфигурации в соответствии со свойствами конфигурации:
import com.codahale.metrics.ConsoleReporter;
import com.codahale.metrics.MetricRegistry;
import lombok.extern.java.Log;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import java.util.concurrent.TimeUnit;
@Configuration
@Log
@ConditionalOnProperty(prefix = "monitor.report", name = "console", havingValue = "true")
@Import(MonitorConfig.class)
public class MonitorReportConfig {
@Bean
public ConsoleReporter consoleReporter(MetricRegistry metrics) {
ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(10, TimeUnit.SECONDS);
return reporter;
}
}
Это можно сделать в проектеapplication.properties
В файле откройте отчет терминала через следующую конфигурацию и выводите его каждые 10 секунд:
monitor.report.console = true
5 Подготовьте таймер для контролируемого метода
Информации, которую можно посчитать в Метрике, очень много, и Timer уже собрал нужную нам информацию.
Почему я должен сначала подготовить таймер для отслеживаемого метода, а не создавать его при выполнении метода? Есть две причины.
- Нам важно не только то, что метод вызывается, но и то, что он никогда не вызывается, если он создается при выполнении метода, то мы не знаем, не отслеживается ли метод или метод не вызывается.
- Позже мы планируем напрямую фасетировать аннотации @RestController, @Controller и @Service. Эта сила аспекта на уровне класса будет включать в себя методы, которые нам не нужны, такие как toString и другие методы, поэтому подготовьте методы, которые вам нужны, и когда вы их вызовете, вы обнаружите, что вам не нужны методы, и пусть они идут.
Мы используемMethodMonitorCenter
класс для сбора методов, которые мы хотим отслеживать. путем реализацииApplicationContextAware
Интерфейс после загрузки контейнера Spring будет возвращенsetApplicationContext
метод, мы проходимgetBeansWithAnnotation
Метод находит класс, содержащий указанную аннотацию. Затем отфильтруйте его и получите методы, которые мы хотим отслеживать. В конце мы проходимmetricRegistry.timer(method.toString());
метод подготавливает таймер для интересующего нас метода.
@Component
@Getter
@Log
public class MethodMonitorCenter implements ApplicationContextAware {
public static final String PACKAGE_NAME = "com.sinafenqi"; // 这里换成自己的包名
@Autowired
private MetricRegistry metricRegistry;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Object> monitorBeans = new HashMap<>();
monitorBeans.putAll(applicationContext.getBeansWithAnnotation(Controller.class));
monitorBeans.putAll(applicationContext.getBeansWithAnnotation(Service.class));
monitorBeans.putAll(applicationContext.getBeansWithAnnotation(RestController.class));
log.info("monitor begin scan methods");
monitorBeans.values().stream()
.map(obj -> obj.getClass().getName())
.map(this::trimString)
.map(clzName -> {
try {
return Class.forName(clzName);
} catch (Exception e) {
return null;
}
})
.filter(Objects::nonNull)
.filter(aClass -> aClass.getName().startsWith(PACKAGE_NAME))
.forEach(this::getClzMethods);
}
private void getClzMethods(Class<?> clz) {
Stream.of(clz.getDeclaredMethods())
.filter(method -> method.getName().indexOf('$') < 0)
.forEach(method -> {
log.info("add method timer, method name :" + method.toGenericString());
metricRegistry.timer(method.toString());
});
}
private String trimString(String clzName) {
if (clzName.indexOf('$') < 0) return clzName;
return clzName.substring(0, clzName.indexOf('$'));
}
}
6 Методы мониторинга по аспектам
Затем мы можем отслеживать интересующие нас методы в аспекте. Используйте фасеты с закруглением здесьRestController
,Controller
,иService
Три аннотации делают аспект. Это позволяет вам добавить некоторый код мониторинга до и после метода. При входе в функцию вокруг мы сначала идем в MetricRegistry, чтобы узнать, есть ли соответствующий таймер.Если нет описания, которое не является интересующим нас методом, то мы можем выполнить его напрямую.Если оно есть, то я буду мониторить Это. Подробности смотрите в коде:
@Component
@Aspect
@Log
public class MetricsMonitorAOP {
@Autowired
private MetricRegistry metricRegistry;
@Pointcut("@within(org.springframework.stereotype.Controller)" +
"||@within(org.springframework.stereotype.Service)" +
"||@within(org.springframework.web.bind.annotation.RestController)")
public void monitor() {
}
@Around("monitor()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
String target = joinPoint.getSignature().toLongString();
Object[] args = joinPoint.getArgs();
if (!metricRegistry.getNames().contains(target)) {
return joinPoint.proceed(args);
}
Timer timer = metricRegistry.timer(target);
Timer.Context context = timer.time();
try {
return joinPoint.proceed(args);
} finally {
context.stop();
}
}
}
7 эффектов
посетить после/monitor/metrics
интерфейс, вы можете получить результаты мониторинга в формате данных Json. Не забудьте поставитьMethodMonitorCenter
в классеPACKAGE_NAME
Замените константу на свою.
Теперь мы в основном реализовали методы, которые мы определили для мониторинга всех уровней контроллеров и сервисов, но в коде еще много места для оптимизации. Я нашел эти коды в репозитории Git и больше не пробовал.Если у вас есть какие-либо вопросы, пожалуйста, оставьте сообщение. Пожалуйста, поймите. В настоящее время я оптимизировал код во многих местах, содержание оптимизации будет объяснено в следующей статье, а исходный код будет прикреплен.
Наконец, приглашаю обратить внимание на мой личный публичный номер. Задавайте вопросы, общайтесь, что угодно.