← Назад к вопросам
Что делать если завис сервис
2.0 Middle🔥 161 комментариев
#JVM и управление памятью#REST API и микросервисы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Что делать если завис сервис - диагностика и восстановление
Это сценарий, с которым столкнется практически каждый Java разработчик в production. Важно иметь четкий алгоритм действий для быстрой диагностики и восстановления сервиса.
Первые действия (первые 30 секунд)
1. Подтвердить факт зависания
Сервис может казаться зависшим, но на самом деле просто медленным.
# Проверить доступность сервиса
curl -v http://localhost:8080/health
# Если ответа нет - сервис действительно зависший
# Если ответ медленный - проблема производительности (другой случай)
2. Посмотреть логи
# Последние логи
tail -100 /var/log/app/application.log
# В контейнере
docker logs -f myapp | tail -100
# В kubernetes
kubectl logs -f deployment/myapp --tail=100
Ищи:
- Stack traces (исключения)
- Deadlock messages
- Out of memory errors
- Network timeouts
3. Проверить ресурсы сервера
# CPU и память
top
# Более детально для Java процесса
jps -l # Найти Java процесс
# Смотри:
# - CPU usage > 100% (использует все ядра)
# - Memory usage близко к лимиту
# - Процесс зависает (STAT = D или T)
Второй этап: Сбор диагностической информации (2-5 минут)
1. Thread dump
Получить снимок всех потоков - критически важно для понимания причины зависания.
# Найти PID Java процесса
PID=$(pgrep -f myapp.jar)
# Получить thread dump
jstack $PID > thread_dump.txt
# Или с jcmd
jcmd $PID Thread.print > thread_dump.txt
Анализ thread dump:
# Ищи паттерны:
1. Deadlock признаки:
"Thread-1" prio=10 tid=0x00007f...
java.lang.Thread.State: BLOCKED (on object monitor)
at java.util.HashMap.get(HashMap.java:...)
- waiting to lock <...>
at App.method(...)
2. Infinite loop:
at myapp.HotSpotMethod.doSomething(MyClass.java:123)
at myapp.HotSpotMethod.doSomething(MyClass.java:120)
at myapp.HotSpotMethod.doSomething(MyClass.java:120) # Повтор!
3. Ожидание ввода-вывода:
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(...)
4. Все потоки заняты:
Все потоки в RUNNABLE состоянии - может быть infinite loop
2. Heap dump
Если заподозреваешь memory leak или OOM.
PID=$(pgrep -f myapp.jar)
# Создать heap dump
jcmd $PID GC.heap_dump filename=heap_dump.hprof
# Анализировать позже в Eclipse MAT
3. Проверить соединения
# Сколько соединений открыто
lsof -p $PID | grep TCP
# Сколько потоков создано
ps -p $PID -L | wc -l
# Ожидающие соединения
netstat -an | grep ESTABLISHED | wc -l
Третий этап: Определение причины
Причина 1: Deadlock
Топический признак в thread dump:
Found one Java-level deadlock:
==============================
"Thread-1":
waiting to lock monitor 0x00007f... (a java.lang.Object)
at MyClass.methodA(MyClass.java:50)
- locked <0x00007f...> (a java.lang.Object)
at MyClass.methodB(MyClass.java:30)
"Thread-2":
waiting to lock monitor 0x00007f... (a java.lang.Object)
at MyClass.methodB(MyClass.java:30)
- locked <0x00007f...> (a java.lang.Object)
at MyClass.methodA(MyClass.java:50)
Решение: Перезагрузка (единственный выход)
kill -9 $PID
servicectl restart myapp
# или
docker restart myapp
Причина 2: Memory Leak / OOM
Признаки:
- Memory usage постоянно растет
- java.lang.OutOfMemoryError в логах
- GC overhead limit exceeded
# Проверить текущую память
jcmd $PID VM.memory_usage
# Вывод:
# Java Heap (committed): 2G
# Java Heap (used): 1.9G # Близко к лимиту
Быстрое решение:
# Увеличить heap размер
export JVM_OPTS="-Xmx4g -Xms4g"
kill -9 $PID
# Перезагрузить с новыми параметрами
Долгосрочное решение:
- Найти memory leak в коде (часто незавершенные connection pools, listener'ы)
- Использовать профайлер (YourKit, JProfiler)
Причина 3: Бесконечный цикл / Высокое CPU
Признаки: CPU > 90%, сервис отвечает медленно
# Найти самый "горячий" поток
jstack $PID | grep -A 20 "tid" | head -50
# Или с async-profiler
/opt/async-profiler/asprof -d 10 -f flamegraph.html $PID
Решение:
- Оптимизировать алгоритм (обычно N² вместо N log N)
- Добавить кэширование
- Параллелизм
Причина 4: Network Timeout / Блокирующие I/O
Признаки в thread dump:
java.lang.Thread.State: WAITING (on object monitor)
at java.net.SocketInputStream.read0(Native Method)
at com.example.MyDatabaseClient.query(MyDatabaseClient.java:100)
Решение:
- Добавить timeout на external API calls
- Использовать non-blocking I/O (async, CompletableFuture)
- Circuit breaker pattern
@Service
public class DatabaseClient {
// Плохо - может зависнуть
public Data queryDatabase() {
return database.query(); // Бесконечное ожидание
}
// Хорошо - с timeout
public Data queryDatabaseWithTimeout() {
return database.query();
}
}
Причина 5: Resource Exhaustion
# Слишком много потоков
ps -p $PID -L | wc -l # > 10000 threads
# Слишком много соединений
lsof -p $PID | grep TCP | wc -l # > 10000
Решение:
- Ограничить thread pool размер
- Ограничить connection pool
- Закрыть незакрытые соединения
Пошаговый алгоритм восстановления
Вариант 1: Graceful restart
#!/bin/bash
PID=$(pgrep -f myapp.jar)
# 1. Остановить прием новых запросов
# Используй load balancer или proxy
# Например, с nginx
ngx_signal reload # Перезагружает конфиг,停止 отправку на этот сервер
# 2. Дать время для завершения текущих операций
sleep 30
# 3. Закрыть сервис
kill -TERM $PID # Graceful shutdown
sleep 10
# 4. Если не завершилось - force kill
kill -9 $PID
# 5. Перезагрузить
servicectl restart myapp
# 6. Проверить здоровье
sleep 5
curl http://localhost:8080/health
# 7. Вернуть в load balancer
# nginx auto-detect или manual
Вариант 2: Kubernetes
# 1. Удалить pod (он пересоздастся)
kubectl delete pod myapp-xyz-123
# 2. Или перезагрузить deployment
kubectl rollout restart deployment/myapp
# 3. Следить за progress
kubectl rollout status deployment/myapp
Вариант 3: Docker
# 1. Graceful stop
docker stop myapp
# 2. Restart
docker start myapp
# 3. Или просто перезагрузить
docker restart myapp
Профилактика (предотвращение зависаний)
1. Health checks
@RestController
@RequestMapping("/health")
public class HealthController {
@GetMapping
public ResponseEntity<Health> health() {
// Проверить критические компоненты
if (isDatabaseConnected() &&
isThreadPoolHealthy() &&
isMemoryOk()) {
return ResponseEntity.ok(new Health("UP"));
}
return ResponseEntity.status(503).body(new Health("DOWN"));
}
private boolean isThreadPoolHealthy() {
// Проверить thread pool
ThreadPoolExecutor executor = (ThreadPoolExecutor) threadPool;
int activeCount = executor.getActiveCount();
int queueSize = executor.getQueue().size();
// Если очередь растет - это проблема
return queueSize < 1000;
}
}
2. Monitoring и Alerting
# Prometheus metrics
# thread_count > 500 -> Alert
# memory_usage > 80% -> Alert
# response_time > 5s -> Alert
# Datadog / New Relic / AppDynamics setup
3. Resource Limits
# Docker / Kubernetes
apiVersion: v1
kind: Pod
metadata:
name: myapp
spec:
containers:
- name: myapp
image: myapp:latest
resources:
limits:
memory: "2Gi"
cpu: "1000m"
requests:
memory: "1Gi"
cpu: "500m"
env:
- name: JVM_OPTS
value: "-Xmx1500m -Xms1500m"
4. Timeout везде
// Spring REST Client
@Bean
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory factory =
new HttpComponentsClientHttpRequestFactory();
factory.setConnectTimeout(5000); // 5 секунд
factory.setReadTimeout(10000); // 10 секунд
return new RestTemplate(factory);
}
// Database
@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setConnectionTimeout(10000);
config.setIdleTimeout(600000);
config.setMaxLifetime(1800000);
return new HikariDataSource(config);
}
5. Circuit Breaker
@Service
public class ExternalApiService {
@CircuitBreaker(name = "externalApi", fallbackMethod = "fallback")
public String callExternalApi() {
return externalApi.getData();
}
public String fallback(Exception e) {
log.error("Circuit breaker triggered", e);
return "Fallback data"; // Возврат кэшированных данных
}
}
Чеклист при зависании
- Подтвердить что сервис действительно зависший
- Собрать thread dump
- Собрать heap dump (если подозреваешь memory leak)
- Проверить логи на ошибки
- Проверить ресурсы (CPU, memory)
- Определить причину (deadlock, OOM, loop, timeout)
- Применить краткосрочное решение (restart)
- Назначить анализ (code review, performance test)
- Внедрить долгосрочное решение
Быстрые действия спасают production, но анализ и профилактика спасают репутацию!