Как вы восстанавливали упавший или деградировавший сервис на практике?

«Как вы восстанавливали упавший или деградировавший сервис на практике?» — вопрос из категории DevOps, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Восстановление включает диагностику, устранение причины и возврат в рабочее состояние.

Типичный сценарий и действия:

  1. Диагностика:

    • Логи и метрики: Анализ ERROR/WARN логов и графиков (CPU, память, latency) в Prometheus/Grafana.
    • Дампы памяти: При OutOfMemoryError снимается heap dump и анализируется в Eclipse MAT или VisualVM для поиска "утечек" (например, HashMap, растущий без ограничений).
  2. Пример восстановления после утечки памяти:

    • Проблема: Сервис падает раз в несколько дней из-за OOM.
    • Анализ: В MAT обнаруживается, что статический кэш ConcurrentHashMap никогда не очищается.
    • Решение: Замена на кэш с TTL (Time-To-Live), например, используя Caffeine:
      Cache<Key, Value> cache = Caffeine.newBuilder()
      .expireAfterWrite(10, TimeUnit.MINUTES)
      .maximumSize(10_000)
      .build();
  3. Механизмы resilience для автоматического восстановления:

    • Health Checks: Эндпоинт /actuator/health для мониторинга и автоматического перезапуска оркестратором (Kubernetes).
    • Circuit Breaker (Resilience4j): Автоматически изолирует сбойный внешний вызов и позволяет ему восстановиться.
      CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("externalService");
      Supplier<String> decoratedSupplier = CircuitBreaker
      .decorateSupplier(circuitBreaker, () -> callExternalService());
    • Graceful Shutdown & Startup: Настройка server.shutdown=graceful в Spring Boot для корректного завершения текущих запросов.