gRPC的正常关闭
首先
这篇文章是Go 3 Advent Calendar 2020的第21天。
使用Prometheus来获取gRPC的指标时,下面是一个Graceful Shutdown的示例。
我们想要实现的目标是,在出现错误或特定信号时,在任何一个服务器上安全地停止所有服务器。
请注意
我们的目标是在使用Go构建的gRPC服务器中获取指标。
概括起来。
这次我们将使用run来创建Graceful Shutdown。
还有一个很方便的包叫做errgroup。
大致的背景
在尝试获取gRPC的指标时,我认为可以按照go-grpc-prometheus的方式来创建两个服务器,一个用于gRPC的HTTP2服务器,另一个用于Prometheus的HTTP服务器。以上摘录自此处。
// メトリクス用のサーバを別のgoroutineで起動
go func() {
if err := httpServer.ListenAndServe(); err != nil {
log.Fatal("Unable to start a http server.")
}
}()
// gRPC用のサーバを起動
log.Fatal(grpcServer.Serve(lis))
只是在上述的例子中,有一个类似的备注:“由于没有实施Graceful Shutdown,请在生产环境中不要使用。”
虽然有许多关于在单个服务器上实现Graceful Shutdown的示例可以参考,但是我对如何在两个服务器上实现Graceful Shutdown的方法不太了解,为了不忘记在公司内听到的信息,我打算写一篇文章。
優雅關機的回顧
如果收到特定的信号,服务器将调用以下方法正常关闭:
– 对于net/http:
Server.Shutdown
– 对于grpc:
Server.GracefulStop
使用run来实现优雅的关机。
与errgroup类似,但通过Group.Add()将执行函数和中断函数添加到Group中,通过Group.Run()由协程执行执行函数,
当执行函数完成时调用所有中断函数。
我个人认为,由于可以单独管理执行和中断,这非常方便。
请详阅README以了解更多信息。
在本例中,我认为会有以下结果。
var g run.Group
{
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGTERM, os.Interrupt)
cancel := make(chan struct{})
g.Add(
func() error {
select {
case <-c:
log.Println("Received signal, exiting gracefully...")
case <-cancel:
}
return nil
},
func(err error) {
close(cancel)
},
)
}
{
g.Add(
func() error {
if err := httpServer.ListenAndServe(); err != http.ErrServerClosed {
log.Printf("Failed to serve http server: %v", err)
return err
}
return nil
},
func(err error) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := httpServer.Shutdown(ctx); err != nil {
log.Printf("Failed to shutdown http server: %v", err)
}
},
)
}
{
g.Add(
func() error {
listener, err := net.Listen("tcp", ":9093")
if err != nil {
log.Printf("Failed to listen grpc server: %v", err)
return err
}
if err := server.Serve(listener); err != nil {
log.Printf("Failed to serve grpc server: %v", err)
return err
}
return nil
},
func(err error) {
server.GracefulStop()
},
)
}
if err := g.Run(); err != nil {
log.Fatalf("error: %v", err)
}
以下是三个模块:接收 Signal、启动 HTTP 服务器和启动 gRPC 服务器。可以参考 Prometheus 中的 run 函数的使用方法。
使用errgroup实现优雅关闭
暂定待商议…
总结
希望通过使用errgroup和run来轻松实现多个服务器的优雅关闭。