TIL: Go에서 다른 서버를 수신하는 방법(select 사용)

9704 단어 todayilearnedgoselect
오늘 저는 기존 코드베이스를 리팩토링하여 Go에서 다른 서버를 수신하는 방법을 배웠습니다. 이 리팩토링의 목표는 "인위적인"문을 만드는 대신 select 문을 올바르게 사용하는 것이었습니다.

이것이 시작점입니다. 리팩토링 시작 시 존재했던 "인위적"선택을 가장 잘 강조하기 위해 대부분의 코드를 생략하고 있습니다.

func Start(/* options here*/) error {
    errChan := make(chan error)
    go func() {
        c := make(chan os.Signal, 1)
        signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
        errChan <- fmt.Errorf("%s", <-c)
    }()
    // [...]
    go startHTTPSrv(errChan)
    // [...]
    go startGRPCSrv(errChan)
    // [...]
    return <-errChan
}

func startHTTPSrv(errChan chan<-error) { // the param list is simplified to focus only on the err chan
    errChan <- http.ListenAndServe(/* http.ListenAndServe params */)
}

func startGRPCSrv(errChan chan<-error) { // the param list is simplified to focus only on the err chan
    errChan <- gRPCServer.Serve(/* gRPCServer.Serve params */)
}


동일한 errChan가 모든 곳에서 사용되기 때문에 HTTP 서버, GRPC 또는 SIGINT/SIGTERM 리스너 등 오류를 반환하는 모든 고루틴은 Start func를 종료합니다.

이것은 실제로 select 문의 동작입니다. 이것이 내가 그것을 변형하기 시작한 이유이며 이것이 지금의 모습입니다.

func Start(o *Opts) error {
    select {
    case err := <-startHTTPSrv(newSrvData(o.HTTPAddr)):
        return err
    case err := <-startGRPCSrv(newSrvData(o.GRPCAddr)):
        return err
    case err := <-handleSigTerm():
        return err
    }
}

func startHTTPSrv(/* params */)  <-chan error{ // the param list is simplified to focus only on the err chan
    // [...]
    errChan := make(chan error)
    go func() {
        errChan <- http.ListenAndServe(/* http.ListenAndServe params */)
    }()
    return errChan
}

func startGRPCSrv((/* params */) <-chan error { // the param list is simplified to focus only on the err chan
    // [...]
    errChan := make(chan error)
    go func() {
        // [...]
        errChan <- gRPCServer.Serve(/* gRPCServer.Serve params */)
    }()
    return errChan
}

func handleSigTerm() <-chan error {
    // [...]
    errChan := make(chan error)
    go func() {
        c := make(chan os.Signal, 1)
        signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
        errChan <- fmt.Errorf("%s", <-c)
    }()
    return errChan
}


이 시점에서 select 문을 사용하면 서로 다른 고루틴이 실행되고 Start 에서 반환하기 위해 첫 번째가 종료될 때까지 대기하는 명확한 구분을 보여줄 수 있습니다.
Start func의 변경 외에도 다른 함수도 select 문 내에서 편안하게 사용할 수 있도록 일반 오류 대신 오류 수신자chan를 반환하도록 변경되었습니다.

이 리팩터링에는 두 가지 긍정적인 측면이 있습니다.
  • 다른 프로세스/서버를 수신하기 위해 select 문을 사용하는 방법을 잘 보여줍니다.
  • "인공적인"select 문에서 관용적인 문으로 전환하는 방법을 보여줍니다.

  • 도움이 되었기를 바랍니다. 시간 내어 읽어주셔서 감사합니다!

    추신. Here be dragons! 일명 이 TIL에 영감을 준 완전한 변경으로 수행한 커밋에 대한 github 참조입니다.

    좋은 웹페이지 즐겨찾기