하나의 프로세스에서 여러 서비스를 우아하게 관리

14155 단어


배경


API gatewayRPC service를 같은 프로세스에 넣을 수 있는지, 어떻게 하면 되는지 자주 질문을 받았습니다. 외부 서비스와 메시지 큐를 같은 프로세스에 넣는 개발자도 있습니다. 이러한 문제를 보다 우아하게 해결하는 방법을 살펴보겠습니다.

문제



두 가지 모의 서비스를 예로 들어 보겠습니다. 하나의 프로세스에서 두 개의 서로 다른 포트에서 시작해야 하는 두 개의 서비스가 있습니다. 코드는 다음과 같습니다.

package main

import (
  "fmt"
  "net/http"
)

func morning(w http.ResponseWriter, req *http.Request) {
  fmt.Fprintln(w, "morning!")
}

func evening(w http.ResponseWriter, req *http.Request) {
  fmt.Fprintln(w, "evening!")
}

type Morning struct{}

func (m Morning) Start() {
  http.HandleFunc("/morning", morning)
  http.ListenAndServe("localhost:8080", nil)
}

func (m Morning) Stop() {
  fmt.Println("Stop morning service...")
}

type Evening struct{}

func (e Evening) Start() {
  http.HandleFunc("/evening", evening)
  http.ListenAndServe("localhost:8081", nil)
}

func (e Evening) Stop() {
  fmt.Println("Stop evening service...")
}

func main() {
  // todo: start both services here
}


코드는 morning 서비스에 대한 요청이 있고 서비스가 morning!를 반환하고 evening 서비스에 대한 요청이 있고 서비스가 evening를 반환할 만큼 간단합니다. 구현해보시죠~

첫 시도


main에서 두 서비스를 모두 시작할 수 있습니까? 해보자.

func main() {
  var morning Morning
  morning.Start()
  defer morning.Stop()

  var evening Evening
  Start()
  Stop()
}


시작 후 curl로 확인합시다.

$ curl -i http://localhost:8080/morning
HTTP/1.1 200 OK
Date: Mon, 18 Apr 2022 02:10:34 GMT
Content-Length: 9
Content-Type: text/plain; charset=utf-8

morning!
$ curl -i http://localhost:8081/evening
curl: (7) Failed to connect to localhost port 8081 after 4 ms: Connection refused

morning만 성공하고 evening는 요청할 수 없는 이유는 무엇입니까?
main에 인쇄 문을 추가하고 시도해 봅시다.

func main() {
  fmt.Println("Start morning service...")
  var morning Morning
  morning.Start()
  defer morning.Stop()

  fmt.Println("Start evening service...")
  var evening Evening
  Start()
  Stop() defer evening.
}


다시 실행하고 확인하십시오.

$ go run main.go
Start morning service...

Start morning service... 만 인쇄하므로 evening 서비스가 전혀 시작되지 않습니다. 그 이유는 morning.Start()가 현재의 goroutine를 차단하여 후속 코드가 실행되지 않기 때문입니다.

두 번째 시도



이것은 WaitGroup가 유용한 곳입니다. 이름에서 알 수 있듯이 WaitGroup는 작업 그룹을 wait 수행하는 데 사용되며 대기 중인 고루틴이 계속될 수 있도록 작업이 완료될 때까지 기다립니다. 시도해 봅시다.

func main() {
  var wg sync.WaitGroup
  wg.Add(2)

  go func() {
    defer wg.Done()
    fmt.Println("Start morning service...")
    var morning Morning
    defer morning.Stop()
    morning.Start()
  Start() }()

  go func() {
    defer wg.Done()
    fmt.Println("Start evening service...")
    var evening Evening
    defer evening.Stop()
    evening.Start()
  Start() }()

  wg.Wait()
}


시도 해봐.

$ go run main.go
Start evening service...
Start morning service...


좋아요, 두 서비스가 모두 작동 중이므로 curl 로 확인하겠습니다.

$ curl -i http://localhost:8080/morning
HTTP/1.1 200 OK
Date: Mon, 18 Apr 2022 02:28:33 GMT
Content-Length: 9
Content-Type: text/plain; charset=utf-8

morning!
$ curl -i http://localhost:8081/evening
HTTP/1.1 200 OK
Date: Mon, 18 Apr 2022 02:28:36 GMT
Content-Length: 9
Content-Type: text/plain; charset=utf-8

evening!


작동하며 WaitGroup를 사용한 프로세스가
  • 필요한 여러 서비스가 있음을 기억하십시오wait.
  • 서비스를 하나씩 추가합니다
  • .
  • 모든 서비스가 완료될 때까지 대기
  • go-zero 어떻게 처리하는지 보자~

    세 번째 시도


    go-zero에서는 여러 서비스의 시작과 중지를 쉽게 관리할 수 있도록 ServiceGroup를 제공합니다. 시나리오에서 사용하여 어떻게 수행되는지 봅시다.

    import "github.com/zeromicro/go-zero/core/service"
    
    // more code here
    
    func main() {
      group := service.NewServiceGroup()
      defer group.Stop()
      group.Add(Morning{})
      group.Add(Evening{})
      group.Start()
    }
    


    보시다시피 코드는 훨씬 더 읽기 쉽고 WaitGroup 에 추가할 수를 실수로 잘못 계산할 수 없습니다. 그리고 ServiceGroupStart가 나중에 Stop 먼저 defer와 동일한 서비스를 확인하고 리소스를 더 쉽게 정리할 수 있도록 합니다.
    ServiceGroup는 각 서비스의 Start/Stop를 관리할 뿐만 아니라 graceful shutdown 신호를 수신하면 각 서비스의 Stop 메소드를 능동적으로 호출하는 SIGTERM를 제공하고 HTTP 서비스에 대해서는 server.Shutdown 서비스의 경우 HTTP로, server.GracefulStop() 서비스의 경우 gRPC로 정상적으로 종료할 수 있습니다.

    요약


    ServiceGroup의 구현은 82줄의 코드로 충분히 간단합니다.

    $ cloc core/service/servicegroup.go
    ------------------------------------------------------------------
    Language files blank comment code
    ------------------------------------------------------------------
    Go           1    22      14   82
    ------------------------------------------------------------------
    


    코드는 짧고 간결하며 모든 서비스(Restful, RPC, MQ)는 기본적으로 ServiceGroupgo-zero에 의해 관리되므로 매우 편리하고 읽을 가치가 있습니다.

    프로젝트 주소



    https://github.com/zeromicro/go-zero
    go-zero를 사용하고 별표를 표시하여 지원해 주셔서 감사합니다!

    좋은 웹페이지 즐겨찾기