티스토리 뷰

728x90

1. 들어가며

회사에서 비용 절감 차원에서 가끔 테스트 쪽 서버 몇가지를 시간을 정해서 운영한 적이 있었다

19:00 ~ 09:00 까지는 테스트 서버 몇개는는 운영하지 않았고, 서버가 다운됨에 따라,

다운된 서버에 의존하는 스프링 어플리케이션은 java.net.SocketException 을 30초 주기로 계속 발생했다

org.springframework.data.redis.RedisConnectionFailureException:Unable to connect to Redis; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to localhost:6379
Caused by: java.net.ConnectException: Connection refused

여기서 한 가지 궁금증이 생겼다. 스프링 어플리케이션은 이미 처음 로딩 시 데이터베이스 커넥션을 진행한 후에 메모리에 올라가 있는데 어떻게 Redis/MySQL 이 끊긴걸 알아챘을까??

위 내용이 궁금해서 찾아 본 내용을 정리하였다

 

2. 본론

결론부터 말하자면, Spring 은 요청이 있기 전까지는 Redis/MySQL 이 끊긴지 알 수 없다

이유는 아래 글을 읽다보면 나올 것이다

맨처음에 생각해본 방법은 스프링 어플리케이션이 외부 써드파티(MySQL, Redis) 등 Bean 을 생성할 때 default 옵션중에
interval 로 헬스체크에 대한 설정 중에 polling 을 주기적으로 주고받는게 있을걸가? 라는 생각을 하였다

그럼 몇초 주기로 polling 을 하는거지? timeout 몇초를 보고 죽었다고 판단하는거지? 라는 고민을 찾기 위해 DataSource 인터페이스를 열심히 찾아보았다

하지만 명확한 해결책을 보지못했고 검색해본 결과 답은 다른 것이였다

위 polling 방법은 상위 수준에서 요청 <-> 응답 구조를 가지고 있지만

생각해보니 더 저수준인 TCP 로 세션을 맺으면 한번 연결해두고 둘중 하나가 끊지 않는 이상 계속 연결되는 것 이였다

 

2-1) TCP 소켓 연결감지 메커니즘 원리

// Spring이 Redis/MySQL과 연결하는 방식
┌─────────────────┐    TCP Socket     ┌─────────────────┐
│ Spring App      │ ←───────────────→ │ Redis/MySQL     │
│ (Client)        │      (양방향통신)    │ (Server)        │
└─────────────────┘                   └─────────────────┘

// 연결 상태
1. ESTABLISHED: 정상 연결
2. FIN_WAIT: 종료 대기
3. CLOSE: 연결 종료

Spring 이 Client 라고 생각을 하고 Redis/MySQL 은 서버의 입장이다

그리고 Spring 은 TCP 소켓 상태 변화를 통해 연결 끊김을 감지한다

그리고 내가 한가지 간과 하고 있었던건 건 내가 어플리케이션 헬스체크 대상에 MySQL, Redis 을 30초 주기로 체킹하게 설정을 해뒀기에 감지가 되었던 것이였다

기본적으로 스프링은 어떠한 요청이 들어오기 전까지 죽었는지 살았는지 알 수가 없다

┌─────────────────┐    TCP Socket     ┌─────────────────┐
│ Spring App      │ ←───────────────→ │ Redis/MySQL     │
│ (Client)        │                   │ (Server)        │
└─────────────────┘                   └─────────────────┘

// 연결 감지 시점
1. 서버 다운 → TCP RST/FIN 패킷 전송 → 소켓 상태 변경
2. Health Check 클라이언트가 실제 요청 전송 시도
3. SocketException 발생 → Spring에서 감지

 

그럼 Spring Actuator 구축이 안되어있는 상황이라면 어떻게 감지를 해야할까?

  1. spring 설정을 변경 (yml, @Bean 등록) 을 통한 능동적 감지 

필자는 yml 방식을 선호하여 yml 을 통한 설정 방법은 아래와 같다

spring:
  datasource:
    hikari:
      maximum-pool-size: 20          # connection pool
      minimum-idle: 5
      connection-timeout: 3000      # 30초
      idle-timeout: 60000           # 10분
      max-lifetime: 180000          # 30분
      validation-timeout: 3000       # 5초
      connection-test-query: "SELECT 1"
      test-while-idle: true
      test-on-borrow: true

위 처럼 간단한 방법 말고도 모니터링 로직을 따로 추가한다거나, 인프라 적으로 모니터링을 건다거나 다양한 방법이 있다

 

실제 운영상황에 서버가 다운되는건 최악의 문제이다

그러므로 항시 모니터링 하는 것은 정말 중요하다

 

3. 결론

  • DB 연결 상태 감지 -> 소켓 기반 (Polling 아님)
  • Redis/MySQL 세션 종료 시 소켓 끊김 → 요청이 오는 시점에 감지

추가적으로 짚고 넘어가야 하는 점은 모니터링에 대한 중요성 이다

  • 능동적 감지의 중요성
    • 단순 연결이 끝이 아닌, 주기적인 상태 확인은 필수다

그러므로 spring actuator 를 적극적으로 활용했으면 하는 바램이 있다

 

조금 더 detail 하게 헬스체크 및 모니터링, 메트릭 관리가 필요하다면

- springboot admin server

- springboot admin client

 

위 라이브러리를 찾아봐서 프로젝트에 적용해보는 것을 추천한다!

 

아니면 대표적으로 사용하는 Grafana + Prometheus 조합을 사용하는 것도 방법이다.

 


자체적으로 모니터링에 대한 모듈을구축하여 사용을 하여도 좋지만
이미 잘 추상화되어 구현된 라이브러리가 존재하므로 적극 활용을 추천한다!

 

spring actuator 에 대한 참조할만한 글들을 아래 정리해 두었다

 

 

728x90
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크