Go에서 HTTP서버에 그레이스풀 셧다운(Graceful Shutdown) 구현

Divan·2024년 1월 16일
post-thumbnail

그레이스풀 셧다운은 데이터 손실을 방지하고, 사용자 경험을 유지하기 위해 서버와 애플리케이션을 안전하게 종료하는 과정입니다. 이 과정은 시스템 자원을 올바르게 해제하고, 갑작스러운 종료로 인한 시스템 오류를 최소화합니다. 또한, 로그 및 감사 추적을 통해 시스템의 성능 분석과 문제 해결에 도움을 줍니다.


1. 그레이스풀 셧다운(Graceful Shutdown)을 하지 않을 경우

서버나 애플리케이션에서 여러 가지 문제가 발생할 수 있습니다. 주요 문제점은 다음과 같습니다

  1. 데이터 손실 또는 데이터 무결성 문제: 서버가 진행 중인 요청을 마무리하지 못한 채 갑작스럽게 종료되면, 데이터가 제대로 저장되지 않거나 손상될 수 있습니다. 예를 들어, 데이터베이스에 정보를 쓰는 중에 서버가 종료되면, 해당 데이터가 불완전하게 저장될 위험이 있습니다.
  2. 클라이언트 오류: 클라이언트 측에서 서버로 요청을 보낸 후 응답을 기다리는 중에 서버가 갑자기 종료되면, 클라이언트는 예상치 못한 오류나 예외 처리를 해야 할 수 있습니다. 이는 사용자 경험을 저하시키는 원인이 됩니다.
  3. 자원 유출(Resource Leak): 서버가 그레이스풀 셧다운을 하지 않고 갑자기 종료되면, 열려 있는 파일 핸들, 네트워크 연결, 메모리 할당 등이 제대로 해제되지 않을 수 있습니다. 이는 시스템의 안정성과 성능에 영향을 미칠 수 있습니다.
  4. 서비스 중단: 갑작스러운 서버 종료는 서비스의 가용성에 영향을 줄 수 있습니다. 특히 고가용성이 중요한 서비스에서는 이러한 문제가 치명적일 수 있습니다.
  5. 재시작 지연: 그레이스풀 셧다운 없이 서버가 종료되면, 서버의 재시작 과정이 지연될 수 있습니다. 시스템이 비정상적으로 종료된 상태를 감지하고 복구하는 데 추가 시간이 소요될 수 있기 때문입니다.

2. 예제

기본적인 http 서버

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "안녕하세요, Go 서버에 오신 것을 환영합니다!")
    })

    fmt.Println("서버가 8080 포트에서 시작됩니다.")
    http.ListenAndServe(":8080", nil)
}

이 코드는 8080 포트에서 실행되는 간단한 HTTP 서버를 만듭니다. 클라이언트가 서버에 연결할 때마다, 서버는 "안녕하세요, Go 서버에 오신 것을 환영합니다!"라는 메시지를 응답합니다.

Graceful shoudown을 사용한 서버

그레이스풀 셧다운은 서버를 안전하게 종료하는 방법으로, 진행 중인 모든 요청을 마무리한 후 서버를 종료합니다. 이를 통해 갑작스러운 서버 종료로 인한 데이터 손실이나 클라이언트 오류를 방지할 수 있습니다.

그레이스풀 셧다운 구현 방법
1. 컨텍스트와 취소 함수 사용: context 패키지를 사용하여 셧다운 시그널을 관리합니다.
2. HTTP 서버 커스터마이징: http.Server 구조체를 사용하여 서버를 구성하고, 셧다운 시그널에 대응할 수 있게 합니다.
3. OS 시그널 감지: OS로부터 셧다운 요청을 감지합니다 (예: CTRL+C).


package main

import (
    "context"
    "fmt"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    srv := &http.Server{Addr: ":8080"}

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "안녕하세요, Go 서버에 오신 것을 환영합니다!")
    })

    go func() {
        if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            fmt.Printf("HTTP 서버 에러: %s\n", err)
        }
    }()

    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    fmt.Println("서버가 종료되고 있습니다...")

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    if err := srv.Shutdown(ctx); err != nil {
        fmt.Printf("서버 셧다운 에러: %s\n", err)
    }
    fmt.Println("서버가 안전하게 종료되었습니다.")
    
    // 서버가 완전히 종료될 때까지 대기
	<-ctx.Done()
	fmt.Println("서버 종료 완료")

이 코드는 Go 언어를 사용하여 HTTP 서버를 구축하고, OS 시그널을 감지하여 서버를 안전하게 종료하는 그레이스풀 셧다운을 구현합니다. 서버는 기본적으로 8080 포트에서 시작되며, CTRL+C 같은 중단 시그널을 받으면, 정해진 시간 내에 모든 처리 중인 요청을 마무리하고 서버를 종료합니다.


3. 결론

그레이스풀 셧다운을 통해 서버를 안전하게 종료하는것은 실제 웹 서비스나 API 서버 개발에 있어 매우 중요한 부분입니다. 그리고 어렵지도 않기에 기본적으로 사용하는것을 적극 권장합니다.

profile
하루 25분의 투자

0개의 댓글