home
버퍼있는 채널에 관하여
예시 코드
그냥 요청이 들어오면 이메일을 보내는 척 하는 예시 코드입니다. 프레임워크 안쓰고 net/http go 내장 라이브러리를 사용했어요
package main
import (
"fmt"
"net/http"
"time"
"encoding/json"
)
type Job struct {
Email string
Body string
}
type EmailRequest struct {
Email string `json:"email"`
Body string `json:"body"`
}
var jobQueue = make(chan Job, 100)
func startWorker() {
for job := range jobQueue {
time.Sleep(2 * time.Second)
fmt.Printf("Sending email to %s with body %s\n", job.Email, job.Body)
}
}
func handleEmailRequest(w http.ResponseWriter, r *http.Request) {
var emailRequest EmailRequest
err := json.NewDecoder(r.Body).Decode(&emailRequest)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
newJob := Job{Email: emailRequest.Email, Body: emailRequest.Body}
// select를 사용하여 채널이 가득 찬 경우를 처리
select {
case jobQueue <- newJob:
// 채널에 성공적으로 추가됨
fmt.Println("Job added to queue")
w.WriteHeader(http.StatusOK)
w.Write([]byte("이메일 전송 요청이 접수되었습니다. (바로 응답)"))
default:
// 채널이 가득 참
http.Error(w, "서버가 너무 바쁩니다. 잠시 후 다시 시도해주세요.", http.StatusServiceUnavailable)
}
}
func main() {
go startWorker()
http.HandleFunc("/send-email", handleEmailRequest)
fmt.Println("엔드포인트: POST /send-email")
err := http.ListenAndServe(":30000", nil)
if err != nil {
fmt.Printf("서버 시작 실패: %v\n", err)
}
}
go의 버퍼있는 채널을 사용하게되면 정말 간편하게 작업 큐를 만들 수 있는데요
우체통에 비교하는 경우가 많은 것 같습니다.
var jobQueue = make(chan Job, 100)
를 사용해서 최대 100개의 Job을 넣을 수 있게 하고, go startWorker()가 돌면서 요청이 들어오면 지속적으로 jobQueue를 뽑아서 job을 실행합니다.
여기서 중요한 것이 저 select라는 놈입니다.
select case default
select문의 default 케이스는 채널이 준비될 때까지 기다리지 않고, 즉시 실행하는 탈출구와 비슷합니다. 그래서 case문이 실패하면 default 문으로 빠지는데, default가 없으면 채널의 버퍼가 빌 때까지 무한 대기합니다. 따라서, 논블록을 위해서라면 default가 필요합니다.
실행

이렇게 비동기로 버퍼에서 job을 잘 빼서 이메일을 보내는 척 합니다.

만약 버퍼가 꽉 찼다면 다음과 같이 응답을 빠르게 줍니다. 로드쉐딩을 해서 부하를 줄여줄 수 있겠네요