bounty将在5天内到期。回答此问题可获得+100声望奖励。Cmag希望吸引更多的注意力这个问题:寻找一个工作的解决方案乡亲!希望同时启动工作例程和服务器例程。
谢谢你
我正在尝试创建一个独立启动的工人和HTTP服务器,并侦听终止和完成后优雅地退出。
由于某种原因,工作进程启动,但HTTP服务器不启动,直到发送SIGTERM事件。只有在sigterm事件被发送之后,http服务器才会启动。下面的问题在哪里?
输出
https://gosamples.dev is the best
https://gosamples.dev is the best
https://gosamples.dev is the best
^C2023/05/27 15:07:52 Listening on HTTP server port:
Process finished with the exit code 0
代码
package main
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"sync"
"syscall"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
signals := make(chan os.Signal, 1)
signal.Notify(signals, os.Interrupt, syscall.SIGTERM)
<-signals
cancel()
}()
var wg sync.WaitGroup
wg.Add(1)
go func() {
if err := myWorker(ctx); err != nil {
cancel()
}
wg.Done()
}()
wg.Add(1)
go func() {
if err := startServer(ctx); err != nil {
cancel()
}
wg.Done()
}()
wg.Wait()
}
func myWorker(ctx context.Context) error {
shouldStop := false
go func() {
<-ctx.Done()
shouldStop = true
}()
for !shouldStop {
fmt.Println("https://gosamples.dev is the best")
time.Sleep(1 * time.Second)
}
return nil
}
func startServer(ctx context.Context) error {
var srv http.Server
go func() {
<-ctx.Done() // Wait for the context to be done
// Shutdown the server
if err := srv.Shutdown(context.Background()); err != nil {
// Error from closing listeners, or context timeout:
log.Printf("HTTP server Shutdown: %v", err)
}
}()
if err := srv.ListenAndServe(); err != http.ErrServerClosed {
// Error starting or closing listener:
return fmt.Errorf("HTTP server ListenAndServe: %w", err)
}
log.Printf("Listening on HTTP server port: %s", srv.Addr)
http.HandleFunc("/readiness", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
})
http.HandleFunc("/liveness", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
})
return nil
}
1条答案
按热度按时间whitzsjs1#
如果我没有看错代码,您可以在定义路由处理程序之前启动服务器。这意味着当服务器启动时,它不知道您的
/readiness
和/liveness
端点,因为您尚未添加它们。结果,服务器启动,但它不做任何事情,因为它没有要处理的路由。然后,您就不用在
http.Server
示例中定义Addr
字段。ListenAndServe()
使用在其被调用的http.Server
示例的Addr
字段中定义的地址。如果它为空,则默认为":http"
,但这在代码中没有显式说明,这可能会导致混淆。我把
srv.ListenAndServe
移到了startServer的最后。我错过了什么?问题不在于
srv.ListenAndServe
在函数中的位置,而在于如何配置http.Server
以及何时设置HTTP处理程序。在原始代码中,您将在服务器启动后设置HTTP处理程序。在启动服务器之前需要设置处理程序,因为一旦服务器运行,它将不会拾取任何稍后定义的新处理程序。
log语句
log.Printf("Listening on HTTP server port: %s", srv.Addr)
在srv.ListenAndServe()
之后,这是一个阻塞调用。这意味着log语句将只在服务器停止后运行,这就是为什么您只在发送SIGTERM信号后才能看到它。尝试像这样重新组织
startServer
函数:在
startServer
函数的这个修改版本中,服务器现在知道/readiness
和/liveness
端点,因为它们是在服务器启动之前定义的。HTTP处理程序在服务器启动之前设置,日志语句在服务器启动之前打印。这应该可以解决您的问题,并允许服务器按预期启动和处理请求。此外,现在服务器知道在哪里监听,因为
Addr
是显式定义的。