Spring BootSeniorExperience

Какие production-риски есть у Spring Boot: blocking code, connection pooling, config, auth, observability, deploy или graceful shutdown?

Ключевые production-риски: блокирующий I/O в реактивном стеке, исчерпание пула соединений, утечки конфигурации через Actuator, отсутствие graceful shutdown и неполная observability без трейсинга.

Production-риски Spring Boot: полный разбор

Spring Boot значительно упрощает старт, но в продакшне каждый из его удобных дефолтов может стать источником аварии. Рассмотрим каждую категорию рисков с конкретными примерами и способами их устранения.

1. Blocking code в реактивном стеке

Если вы используете Spring WebFlux (Netty), любой блокирующий вызов в event loop-потоке (JdbcTemplate, синхронный RestTemplate, Thread.sleep) моментально останавливает обработку всех запросов.

// ПЛОХО: блокирующий вызов в реактивной цепочке
@GetMapping("/users/{id}")
public Mono<User> getUser(@PathVariable Long id) {
    // jdbcTemplate.queryForObject — блокирует Netty event loop!
    return Mono.just(jdbcTemplate.queryForObject(
        "SELECT * FROM users WHERE id=?", User.class, id));
}

// ХОРОШО: оборачиваем в boundedElastic-шедулер
@GetMapping("/users/{id}")
public Mono<User> getUser(@PathVariable Long id) {
    return Mono.fromCallable(() ->
        jdbcTemplate.queryForObject("SELECT * FROM users WHERE id=?", User.class, id)
    ).subscribeOn(Schedulers.boundedElastic());
}

В WebFlux используйте R2DBC вместо JDBC, WebClient вместо RestTemplate.

2. Connection pooling

Spring Boot по умолчанию использует HikariCP. Типичная проблема — пул слишком мал для нагрузки или слишком велик для базы данных.

spring:
  datasource:
    hikari:
      maximum-pool-size: 20        # НЕ ставьте 200+ — Postgres не выдержит
      minimum-idle: 5
      connection-timeout: 3000     # 3 сек — не ждать вечно
      idle-timeout: 600000
      max-lifetime: 1800000
      leak-detection-threshold: 5000  # логировать утечки соединений

Формула для Postgres: max_connections = (cores * 2) + effective_spindle_count. При нескольких подах суммарный пул не должен превышать max_connections базы.

3. Утечка конфигурации

Actuator по умолчанию (до 2.x) открывал /env, /configprops, /beans без авторизации. Закройте всё лишнее явно:

management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus
  endpoint:
    env:
      enabled: false
    configprops:
      enabled: false

Никогда не кладите секреты в application.yml в репозитории — используйте Spring Cloud Config, AWS Secrets Manager или Vault.

4. Аутентификация и авторизация

Spring Security по умолчанию генерирует случайный пароль при старте — это нормально для разработки, но не для продакшна. Убедитесь что:

  • Статeless JWT-фильтры не хранят состояние в памяти пода (иначе горизонтальное масштабирование сломает сессии).
  • CSRF защита включена для браузерных клиентов, отключена для API-only эндпоинтов.
  • Методы @PreAuthorize активированы через @EnableMethodSecurity — без этой аннотации они молча игнорируются.
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
            .csrf(AbstractHttpConfigurer::disable)   // REST API
            .sessionManagement(sm -> sm.sessionCreationPolicy(STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/actuator/health", "/actuator/info").permitAll()
                .anyRequest().authenticated())
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
            .build();
    }
}

5. Observability

Spring Boot 3.x поставляется с Micrometer Tracing (OpenTelemetry / Brave). Без трейсинга отладка медленных запросов в распределённой системе невозможна:

management:
  tracing:
    sampling:
      probability: 0.1    # 10% в продакшне
  metrics:
    export:
      prometheus:
        enabled: true

6. Graceful Shutdown

По умолчанию Spring Boot немедленно убивает контекст при SIGTERM. Включите graceful shutdown и задайте таймаут:

server:
  shutdown: graceful
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s

Kubernetes должен отправлять SIGTERM и выждать terminationGracePeriodSeconds (ставьте не меньше 35 секунд) перед SIGKILL. Без этого in-flight транзакции обрываются.

7. Deploy-риски

Fat JAR запускается медленно — типичный старт 8–15 секунд. При rolling update Kubernetes может направить трафик на под, который ещё не готов. Настройте readinessProbe:

readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 20
  periodSeconds: 5
  failureThreshold: 6

Подводные камни

  • spring.jpa.open-in-view=true по умолчанию — держит транзакцию открытой на всё время HTTP-запроса, включая сериализацию JSON. Ведёт к утечкам соединений при медленных клиентах. Отключите явно.
  • Actuator /heapdump и /threaddump открыты — позволяют выгрузить всю память процесса. Никогда не открывайте их в публичную сеть.
  • @Async без настройки ThreadPoolTaskExecutor — по умолчанию Spring создаёт неограниченный пул, который при нагрузке создаёт тысячи потоков и убивает JVM.
  • Circular bean dependencies — Spring Boot 2.6+ запрещает их по умолчанию. Если ваш старый код это обходит через spring.main.allow-circular-references=true, это тикающая бомба при рефакторинге.
  • DevTools в продакшнеspring-boot-devtools в classpath меняет поведение кэширования и перезапускает контекст. Убедитесь, что scope runtime не попадает в fat JAR.
  • JVM heap не ограничен в контейнере — без -XX:MaxRAMPercentage=75.0 JVM видит память хоста и выделяет heap больше лимита контейнера, что приводит к OOMKill.
  • Flyway/Liquibase миграции при rolling update — если две версии приложения работают одновременно и миграция несовместима с предыдущей схемой, старые поды упадут с ошибкой SQL.

What hurts your answer

  • Говорить только о запуске Spring Boot, но не об эксплуатации
  • Не упоминать observability, обновления, безопасность и rollback
  • Описывать риски абстрактно, без способов их снижать

What they're listening for

  • Видит production-риски Spring Boot
  • Говорит про monitoring, rollout, rollback и безопасность
  • Умеет ранжировать риски по вероятности и влиянию

Related topics