Какие знаешь способы сбора JVM метрик?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Способы сбора JVM метрик
Сбор метрик JVM (Java Virtual Machine) критически важен для мониторинга production приложений. Метрики показывают состояние памяти, потоков, сборки мусора и производительности. Я знаком со всеми основными подходами.
1. JMX (Java Management Extensions)
JMX — это встроенный механизм JVM для мониторинга и управления. Это самый базовый способ.
// JMX доступен по умолчанию
import java.lang.management.*;
public class JMXMetricsExample {
public static void main(String[] args) {
// Получить информацию о памяти
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapMemory = memoryMXBean.getHeapMemoryUsage();
System.out.println("Heap Used: " + heapMemory.getUsed() / 1024 / 1024 + " MB");
System.out.println("Heap Max: " + heapMemory.getMax() / 1024 / 1024 + " MB");
// Информация о потоках
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
System.out.println("Thread Count: " + threadMXBean.getThreadCount());
System.out.println("Peak Thread Count: " + threadMXBean.getPeakThreadCount());
// Информация о GC
List<GarbageCollectorMXBean> garbageCollectors =
ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gc : garbageCollectors) {
System.out.println("GC: " + gc.getName());
System.out.println("Collection Count: " + gc.getCollectionCount());
System.out.println("Collection Time: " + gc.getCollectionTime() + "ms");
}
}
}
Запуск с JMX:
java -Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9010 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-jar application.jar
Подключение через JConsole:
jconsole localhost:9010
Преимущества:
- Встроено в JVM
- Нет зависимостей
Недостатки:
- Сложно обрабатывать данные
- Требует ручного подключения
- Не scalable для больших систем
2. Micrometer (современный стандарт)
Micrometer — это фасад для сбора метрик, поддерживает различные backend'ы (Prometheus, DataDog, etc).
<!-- pom.xml -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>1.11.0</version>
</dependency>
<!-- Для Prometheus -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.11.0</version>
</dependency>
// Использование с Spring Boot (автоматическое)
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
// Пользовательские метрики
@Component
public class CustomMetrics {
private final MeterRegistry meterRegistry;
public CustomMetrics(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@PostConstruct
public void init() {
// Counter (счетчик)
Counter.builder("orders.created")
.description("Total orders created")
.register(meterRegistry);
// Gauge (значение)
Gauge.builder("active.users", () -> getUserCount())
.description("Number of active users")
.register(meterRegistry);
// Timer (время выполнения)
Timer.builder("request.processing.time")
.description("Request processing time")
.register(meterRegistry);
}
public void recordOrderCreation() {
meterRegistry.counter("orders.created").increment();
}
public long getUserCount() {
return getUserRepository().count();
}
}
// Использование в service
@Service
public class OrderService {
private final MeterRegistry meterRegistry;
private final Timer requestTimer;
public OrderService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.requestTimer = Timer.builder("order.service.time").register(meterRegistry);
}
public Order createOrder(OrderRequest request) {
return requestTimer.recordCallable(() -> {
Order order = new Order(request);
meterRegistry.counter("orders.created").increment();
return order;
});
}
// Или использовать Sample
public void processOrder(Order order) {
Timer.Sample sample = Timer.start(meterRegistry);
try {
// обработка заказа
} finally {
sample.stop(Timer.builder("order.process").register(meterRegistry));
}
}
}
// application.properties
management.endpoints.web.exposure.include=prometheus
management.metrics.export.prometheus.enabled=true
# Доступна на http://localhost:8080/actuator/prometheus
management:
endpoints:
web:
exposure:
include: prometheus,health,metrics
metrics:
export:
prometheus:
enabled: true
Метрики автоматически собираемые Spring Boot:
- JVM memory
- CPU usage
- Thread count
- GC metrics
- HTTP requests
- Database connections
- Kafka producer/consumer metrics
Преимущества:
- Лучше стандартов
- Поддержка многих backend'ов
- Spring Boot интеграция
- Типизированная API
3. Spring Boot Actuator
Встроенный инструмент для мониторинга:
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
# application.yml
management:
endpoints:
web:
exposure:
include: health,metrics,info,prometheus
endpoint:
health:
show-details: always # Показывать подробности здоровья
metrics:
enable:
jvm: true
process: true
system: true
# Доступные endpoints
GET /actuator # Все endpoints
GET /actuator/health # Состояние приложения
GET /actuator/metrics # Список доступных метрик
GET /actuator/metrics/{name} # Значение конкретной метрики
GET /actuator/prometheus # Prometheus формат
GET /actuator/info # Информация о приложении
Пример ответа:
GET /actuator/health
{
"status": "UP",
"components": {
"db": {"status": "UP"},
"diskSpace": {"status": "UP"},
"livenessState": {"status": "UP"},
"readinessState": {"status": "UP"}
}
}
GET /actuator/metrics
[
"jvm.memory.used",
"jvm.memory.max",
"jvm.threads.live",
"process.cpu.usage",
"http.server.requests"
]
4. Prometheus + Grafana
С помощью Micrometer данные можно отправить в Prometheus и визуализировать в Grafana:
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'java-app'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['localhost:8080']
# Запустить Prometheus
docker run -p 9090:9090 -v $(pwd)/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus
# Запустить Grafana
docker run -p 3000:3000 grafana/grafana
5. JFR (Java Flight Recorder)
Мощный профилировочный инструмент, встроенный в JVM:
# Записать JFR с помощью jcmd
jcmd <pid> JFR.start duration=60s filename=/tmp/recording.jfr
# Посмотреть результаты
jfr dump --xml /tmp/recording.jfr > /tmp/recording.xml
// Программное использование JFR (Java 14+)
import jdk.jfr.Event;
import jdk.jfr.Recording;
public class CustomEvent extends Event {
public String message;
public long value;
}
public class JFRExample {
public static void main(String[] args) throws Exception {
Recording recording = new Recording();
recording.start();
CustomEvent event = new CustomEvent();
event.message = "Sample event";
event.value = 42;
event.commit();
recording.stop();
recording.dumpToFile(Paths.get("/tmp/recording.jfr"));
}
}
Запуск с JFR:
java -XX:+UnlockCommercialFeatures \
-XX:+FlightRecorder \
-XX:StartFlightRecording=delay=0,duration=60s,filename=/tmp/recording.jfr \
-jar application.jar
Преимущества:
- Low overhead
- Очень подробные данные
- Встроено в JVM
6. OpenTelemetry
Современный стандарт для трейсинга и метрик:
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-prometheus</artifactId>
</dependency>
// Использование OpenTelemetry
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.metrics.Meter;
public class OpenTelemetryExample {
private static final Meter meter = GlobalOpenTelemetry.getMeterProvider()
.get("com.example");
public static void main(String[] args) {
var counter = meter.counterBuilder("requests.total")
.build();
counter.add(1);
}
}
7. Custom Monitoring Service
Если нужно более специфичное решение:
@Service
public class MetricsCollector {
private final MeterRegistry meterRegistry;
public MetricsCollector(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@Scheduled(fixedDelay = 5000)
public void collectMetrics() {
// Heap Memory
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
long heapUsed = memoryBean.getHeapMemoryUsage().getUsed();
meterRegistry.gauge("jvm.memory.heap.used.bytes", heapUsed);
// CPU Usage
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
double cpuUsage = osBean.getProcessCpuLoad() * 100;
meterRegistry.gauge("process.cpu.usage.percent", cpuUsage);
// Thread Count
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
int threadCount = threadBean.getThreadCount();
meterRegistry.gauge("jvm.threads.count", threadCount);
}
}
Сравнение методов
| Метод | Сложность | Overhead | Автоматизация | Для Production |
|---|---|---|---|---|
| JMX | Средняя | Низкий | Нет | Да |
| Micrometer | Средняя | Низкий | Да (Spring) | Да |
| Actuator | Низкая | Низкий | Да | Да |
| Prometheus | Средняя | Низкий | Да | Да |
| JFR | Высокая | Очень низкий | Нет | Да (profiling) |
| OpenTelemetry | Высокая | Низкий | Да | Да (будущее) |
Лучшие практики
- Используй Micrometer + Prometheus — стандартное решение
- Spring Boot Actuator для базового мониторинга
- JFR для глубокого анализа проблем
- OpenTelemetry для новых проектов
- Мониторь важные метрики:
- Heap memory usage
- GC time и frequency
- Thread count
- CPU usage
- Request latency
- Error rate
- Database connections
Заключение
Для большинства приложений оптимально использовать Spring Boot Actuator + Micrometer + Prometheus + Grafana. Это современный стек, который обеспечивает низкий overhead и хорошую наблюдаемость. Для глубокого профилирования используй JFR. OpenTelemetry становится стандартом, особенно в распределенных системах.