진 서버 종료 시도 중...(고루틴 및 채널 팁)
16536 단어 goginchannelsgoroutines
여러분과 나누고 싶은 소중한 것들을 배웠습니다.
그래서, 무엇이 문제입니까?
gin.Run()
또는 http.ListenAndServe()
를 사용하여 서버를 실행하면 메인 프로세스에 있는 경우 고루틴을 차단하므로 메인 고루틴을 차단합니다.func main() {
ginApp := gin.Default()
ginApp.Run(":80") // This code is blocking
// next lines will never run because of blocking code.
FuncForStoppingGinServer() // This function can't be used.
}
다른 고루틴 사용
우리는 단순히 다른 고루틴을 차단하고 메인 고루틴이 자유롭게 유지되도록 도울 수 있습니다.
func main() {
ginApp := gin.Default()
go ginApp.Run(":80") // This code is not blocking anymore.
FuncForStoppingGinServer() // This function will run now.
}
그런데, 안에서는 어떻게 해야 할까요
FuncForStoppingGinServer()
??아무것도 아님!
대답은 gin 자체에는 서버를 종료하는 기능이 없다는 것입니다!
그러나 그것에 들어가기 전에 다른 잘못된 방법을 시도해 봅시다!!
그냥 죽여!
고루틴과 채널을 사용하여 진 서버를 중지하는 것에 대해 생각하고 있습니까? (지금은 활성 연결 처리를 잊어 버리십시오)
var ginApp *gin.Engine
func main() {
// Make an engine
ginApp = gin.Default()
// Make a stop channel
stopChan := make(chan struct{})
go func() {
ginApp.Run(":80") // blocking process
fmt.Println(">> 1 <<")
for range stopChan {
return
}
}()
fmt.Println(">> 2 <<")
// terminate gin server after 3 seconds
time.Sleep(time.Second * 3)
stopChan <- struct{}{}
fmt.Println(">> 3 <<")
}
이것은 좋은 접근 방식으로 들리지만 여기서 무엇이 잘못되었다고 생각하십니까?
어떤 숫자가 인쇄됩니까? (1, 2, 3)?
앞에서 언급했듯이
ginApp.Run(":80")
는 차단 프로세스이며 고 루틴을 차단하므로 채널 stopChan
로 보냅니다.하지만,
우리는
stopChan
를 사용하여 for range loop
에서 읽을 수 없을 것입니다. 이는 차단 프로세스 후에 나왔고 ">> 1 <<"가 인쇄되지 않기 때문입니다.그리고 우리 채널이 버퍼링되지 않았기 때문에 메인 고루틴인 발신자도 ">> 3 <<"를 인쇄하기 바로 전에 누군가가 그것을 읽을 때까지 차단될 것이며 역시 인쇄되지 않을 것입니다.
">> 2 <<"는 어쨌든 인쇄됩니다 😄
추가 재미있는 사실: 채널을 버퍼링하면(이 경우에는 크기 1이면 충분함) 메인 고루틴이 여기로 전송하고 진행한 다음 메인 고루틴 작업이 완료될 때마다 작업을 완료하고 닫힙니다. 전체 프로그램이 존재합니다. ! 다른 goroutine은 그것과 함께 죽을 것입니다. 이렇게 변경하고 결과를 확인하십시오.
stopChan := make(chan struct{}, 1)
또한
chan struct{}
는 채널 유형이고 빈 구조체struct{}{}
는 메모리를 전혀 소비하지 않으며 신호 전용 채널로 알려져 있습니다.나는 마침내 이런 식으로 이 문제를 해결할 방법이 없다는 것을 깨달았습니다.
Is there a way to stop a long blocking function?
이 질문에서 고루틴 중지에 대해 자세히 알아볼 수도 있습니다. How to stop a goroutine
그리고 나중에 더 많은 예를 다루겠습니다. (저를 팔로우하시면 주목받을 수 있습니다)
다시 문제로!
따라서 고루틴을 사용해도 문제가 해결되지 않았습니다.
http.Server.Shutdown()
방법을 시도해보는 것은 어떨까요?var ginApp *gin.Engine
func main() {
// Make an engine
ginApp = gin.Default()
// Make a http server
httpServer := &http.Server{
Addr: ":80",
Handler: ginApp,
}
// Launch http server in a separate goroutine
go httpServer.ListenAndServe()
// Stop the server
time.Sleep(time.Second * 5)
fmt.Println("We're going to stop gin server")
httpServer.Shutdown(context.Background())
}
이 질문을 읽는 것이 도움이 될 수 있습니다: Graceful stop of gin server
작동하지만 여전히 잘못된 점은 무엇입니까?
이제 프로그램 내에서 서버를 종료하고 있지만 어떻게 다시 시작할 수 있습니까?
ListenAndServe()
를 다시 실행하면 될까요?내가 이 질문에서 물은 것처럼: How to stop and start gin server multiple times in one run
이 세미 코드와 같은 것을 구현하려고 합니다.
srv := NewServer()
srv.Start()
srv.Stop()
srv.Start()
srv.Stop()
srv.Start()
약간의 리팩토링으로 해봅시다.
type Server struct {
httpServer *http.Server
}
func main() {
srv := NewHTTPServer()
srv.Start()
time.Sleep(time.Second * 2)
srv.Stop()
time.Sleep(time.Second * 2)
srv.Start()
select {} // Simulate other processes
}
func NewHTTPServer() *Server {
return &Server{
httpServer: &http.Server{
Addr: ":80",
Handler: gin.Default(),
},
}
}
func (s *Server) Start() {
go func() {
if err := s.httpServer.ListenAndServe(); err != nil {
fmt.Println(">>>", err)
}
}()
}
func (s *Server) Stop() {
s.httpServer.Shutdown(context.Background())
}
이 코드를 실행하고 무슨 일이 일어나는지 보십시오!
산출:
...Gin logs...
>>> http: Server closed
>>> http: Server closed
뭐? 우리는 srv.Stop()을 한 번만 호출했지만 서버가 두 번 닫혔습니다!
왜요?
.Shutdown() 함수 문서에 따르면:
Once Shutdown has been called on a server, it may not be reused; future calls to methods such as Serve will return ErrServerClosed.
따라서 공식적으로 작동하지 않습니다!
그러나 여전히 한 가지 작은 변화가 있습니다. 서버를 시작할 때마다 서버 구조를 처음부터 다시 만들 수 있습니다.
마지막 코드에 서브 기능을 추가하고 메인 기능을 변경하면 다른 코드는 동일합니다.
func main() {
srv := serve()
time.Sleep(time.Second * 2)
srv.Stop()
time.Sleep(time.Second * 2)
srv = serve()
srv.Start()
select {} // Simulate other processes
}
func serve() *Server {
srv := NewHTTPServer()
// Register handlers or other stuff
srv.Start()
return srv
}
이것은 우리가 원하는 것이지만 코드를 더 깔끔하게 만들 수 있습니다.
기타 솔루션
내 경우에는 Fiber를 대신 사용할 수 있었고 내 프로젝트에 훨씬 더 잘 맞지만 원하는 솔루션이 아닐 수도 있습니다.
또한 이 페이지를 볼 수 있습니다: Graceful restart or stop
최종 견적
이 기사는 나 자신에 대해 다른 느낌을 가졌습니다. 몇 가지 팁과 작동하지 않는 접근 방식이 있는 위아래 경로였습니다. 여러분도 같은 느낌이기를 바라며 여러분의 생각을 듣게 되어 기쁩니다.
Reference
이 문제에 관하여(진 서버 종료 시도 중...(고루틴 및 채널 팁)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/arshamalh/trying-to-shutdown-a-gin-server-goroutines-and-channels-tips-gf3텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)