← Назад к вопросам

Что произойдет при нарушении работы сервиса?

2.0 Middle🔥 161 комментариев
#REST API и микросервисы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сценарии отказа сервиса и стратегии восстановления

Это вопрос о том, как система ведёт себя при сбое и какие механизмы используются для восстановления работоспособности. Это критично для проектирования надёжных и отказоустойчивых систем.

Типичные сценарии отказа сервиса

1. Полный крах приложения (OutOfMemoryError, StackOverflowError)

// Утечка памяти
public class MemoryLeakService {
    private static List<byte[]> cache = new ArrayList<>();
    
    public void cacheData(byte[] data) {
        cache.add(data);  // Память никогда не освобождается!
    }
    // Результат: OutOfMemoryError -> приложение падает
}

Что произойдёт:

  • JVM выбросит OutOfMemoryError
  • Сервис перестанет отвечать на запросы
  • Приложение может затвердеть (hang) или полностью упасть

Решение: Мониторинг памяти, heap dumps, правильное управление ресурсами

2. Потеря соединения с БД

@RestController
public class UserController {
    @Autowired
    private UserRepository userRepository;
    
    @GetMapping("/users/{id}")
    public ResponseEntity<User> getUser(@PathVariable Long id) {
        try {
            User user = userRepository.findById(id)
                .orElseThrow(() -> new UserNotFoundException());
            return ResponseEntity.ok(user);
        } catch (DataAccessException e) {
            // БД недоступна!
            return ResponseEntity.status(503)
                .body(new ErrorResponse("Database unavailable"));
        }
    }
}

Что произойдёт:

  • Все запросы к БД начнут падать
  • Connection pool исчерпается
  • Сервис станет неработоспособным

3. Медленный ответ (Slow Query, Network Latency)

@Service
public class ReportService {
    @Autowired
    private ReportRepository reportRepository;
    
    // Без таймаута
    public Report generateReport(String filter) {
        // Если запрос зависает на 30 минут...
        return reportRepository.findByFilter(filter);
    }
}

Что произойдёт:

  • Потоки будут заблокированы в ожидании ответа
  • Thread pool будет исчерпан
  • Новые запросы не смогут обработаться
  • Сервис станет неответственным

4. Cascade Failure (каскадный отказ в микросервисной архитектуре)

@RestController
public class OrderService {
    @Autowired
    private RestTemplate restTemplate;
    
    @PostMapping("/orders")
    public Order createOrder(OrderRequest request) {
        // Если PaymentService упал...
        PaymentResponse response = restTemplate.postForObject(
            "http://payment-service/pay",
            request,
            PaymentResponse.class
        );  // Ждём ответа БЕСКОНЕЧНО
        
        return new Order(request, response);
    }
}

Что произойдёт:

  • OrderService зависает в ожидании PaymentService
  • Потоки исчерпываются
  • OrderService тоже падает
  • Это распространяется на другие сервисы

Стратегии восстановления

1. Timeout (таймауты)

@Configuration
public class RestTemplateConfig {
    
    @Bean
    public RestTemplate restTemplate() {
        HttpComponentsClientHttpRequestFactory factory = 
            new HttpComponentsClientHttpRequestFactory();
        
        // Таймауты защищают от зависаний
        factory.setConnectTimeout(Duration.ofSeconds(5));
        factory.setReadTimeout(Duration.ofSeconds(10));
        
        return new RestTemplate(factory);
    }
}

@Service
public class PaymentServiceClient {
    @Autowired
    private RestTemplate restTemplate;
    
    public PaymentResponse pay(PaymentRequest request) {
        try {
            return restTemplate.postForObject(
                "http://payment-service/pay",
                request,
                PaymentResponse.class
            );
        } catch (ResourceAccessException e) {
            // Таймаут или ошибка соединения
            throw new PaymentTimeoutException(e);
        }
    }
}

2. Circuit Breaker (прерыватель цепи)

// Используя Resilience4j
@Service
public class PaymentService {
    private final CircuitBreaker circuitBreaker;
    private final RestTemplate restTemplate;
    
    public PaymentService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
        this.circuitBreaker = CircuitBreaker.of(\"payment\",
            CircuitBreakerConfig.custom()
                .failureRateThreshold(50.0f)
                .waitDurationInOpenState(Duration.ofSeconds(30))
                .build()
        );
    }
    
    public PaymentResponse pay(PaymentRequest request) {
        return circuitBreaker.executeSupplier(() -> 
            restTemplate.postForObject(
                "http://payment-service/pay",
                request,
                PaymentResponse.class
            )
        );
    }
}

// States: CLOSED (нормально) -> OPEN (много ошибок) -> HALF_OPEN (проверка)

3. Retry (повторные попытки)

@Service
public class ReliableService {
    
    @Retryable(
        maxAttempts = 3,
        backoff = @Backoff(delay = 1000, multiplier = 2.0)
    )
    public String callExternalService(String data) {
        return externalClient.process(data);
    }
    
    @Recover
    public String recover(Exception e, String data) {
        // Fallback если все попытки исчерпаны
        return \"Default value for: \" + data;
    }
}

4. Fallback (запасной вариант)

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    @CircuitBreaker(
        name = \"userService\",
        fallbackMethod = \"fallbackGetUser\"
    )
    public User getUser(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException());
    }
    
    // Fallback метод
    public User fallbackGetUser(Long id, Exception e) {
        // Вернуть кэшированные данные или пустой объект
        return new User(id, \"Unknown\", null);
    }
}

5. Graceful Degradation (мягкое снижение качества)

@Service
public class SearchService {
    @Autowired
    private ElasticsearchClient elasticsearchClient;
    
    public List<Product> search(String query) {
        try {
            // Полнотекстовый поиск в Elasticsearch
            return elasticsearchClient.search(query);
        } catch (Exception e) {
            logger.warn(\"Elasticsearch down, using fallback\", e);
            
            // Fallback: простой поиск в БД
            return databaseSearch(query);
        }
    }
}

6. Bulkhead (изоляция потоков)

@Configuration
public class ThreadPoolConfig {
    
    @Bean(\"paymentExecutor\")
    public Executor paymentExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(50);
        executor.setThreadNamePrefix(\"payment-\");
        executor.initialize();
        return executor;
    }
    
    @Bean(\"reportExecutor\")
    public Executor reportExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(3);
        executor.setQueueCapacity(20);
        executor.setThreadNamePrefix(\"report-\");
        executor.initialize();
        return executor;
    }
}

// Использование: изолируем тяжёлые операции
@Service
public class MixedService {
    
    @Async(\"paymentExecutor\")
    public void processPayment(Payment payment) {
        // Не влияет на reportExecutor если заблокируется
    }
    
    @Async(\"reportExecutor\")
    public void generateReport(String filter) {
        // Не влияет на paymentExecutor если заблокируется
    }
}

7. Health Check и Self-Healing

@Component
public class HealthIndicator extends AbstractHealthIndicator {
    
    @Autowired
    private DataSource dataSource;
    
    @Override
    protected void doHealthCheck(Health.Builder builder) {
        try (Connection conn = dataSource.getConnection()) {
            conn.prepareStatement(\"SELECT 1\").execute();
            builder.up().withDetail(\"database\", \"reachable\");
        } catch (SQLException e) {
            builder.down().withDetail(\"database\", \"unreachable\");
        }
    }
}

// Kubernetes автоматически перезапустит Pod если liveness probe упадёт
@GetMapping(\"/actuator/health\")
public ResponseEntity<Map> health() {
    // Используется Kubernetes для мониторинга
    return ResponseEntity.ok(healthIndicator.health());
}

8. Логирование и мониторинг отказов

@Component
public class ErrorHandlingAspect {
    
    @Autowired
    private ErrorMetrics errorMetrics;
    
    @Around(\"@annotation(com.example.Monitored)\")
    public Object monitorErrors(ProceedingJoinPoint joinPoint) 
            throws Throwable {
        try {
            return joinPoint.proceed();
        } catch (Exception e) {
            errorMetrics.recordError(joinPoint.getSignature().getName(), e);
            logger.error(\"Error in {}\", joinPoint.getSignature(), e);
            throw e;
        }
    }
}

Типичный ответ на собеседовании

При нарушении работы сервиса происходит следующее:

  1. Если краша приложения: OutOfMemoryError, StackOverflowError -> JVM падает
  2. Если потеря связи с БД: все запросы начинают падать
  3. Если медленные запросы: потоки блокируются, thread pool исчерпывается
  4. Каскадный отказ: один упавший сервис тянет вниз остальные

Для защиты используются:

  • Timeout - не зависать бесконечно
  • Circuit Breaker - отключить отказавший сервис
  • Retry - повторить при временном сбое
  • Fallback - использовать запасной вариант
  • Bulkhead - изолировать потоки
  • Health Check - мониторить состояние
  • Graceful Degradation - снизить качество вместо полного отказа

Вывод: Правильное проектирование с использованием паттернов отказоустойчивости критично для надёжных систем.

Что произойдет при нарушении работы сервиса? | PrepBro