golang Web 服务器正常关闭

问题描述 投票:0回答:1

我正在尝试找到一种最佳方法来在 Kubernetes 上运行的 golang 服务器关闭期间处理正在进行的 PostgreSQL 事务。

当这些事务正在服务由已关闭的服务器发起的请求时,等待事务完成是否有意义?即使事务在正常关闭超时时间内完成 - 服务器是否能够发送响应?

即使无法在关闭期间响应正在进行的请求,我也更愿意取消所有正在运行的事务的上下文,以便它们在服务器终止后不会继续在数据库上运行,从而增加不必要的负载。但每当我等待事务完成时,似乎都存在一个权衡:等待正在进行的事务完成的时间越长,容器与无响应的服务器一起存在的时间就越长,该服务器在每个传入请求上都会出错。

这里有一些示例代码演示了这一点:

import (
    "github.com/jackc/pgx/v5/pgxpool"
    "os/signal"
    "context"
    "net/http"
    "syscall"
    "time"
)

func main() {
    ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT)
    defer cancel()

    // db is used by the API handler functions
    db, err := pgxpool.NewWithConfig(ctx, <some_config>)
    if err != nil {
        logger.Error("server failed to Shutdown", err)
    }


    server := http.Server{<some_values>}
    serverErr := make(chan error)
    go func() {
        serverErr <- server.ListenAndServe()
    }()

    select {
    case <-ctx.Done():
        if err := Shutdown(closeCtx, time.Second*10, server, db); err != nil {
            logger.Error("server failed to Shutdown", err)
        }

    case err := <-serverErr:
        logger.Error("server failed to ListenAndServe", err)
    }
}

func Shutdown(ctx context.Context, timeout time.Duration, server *http.Server, db *pgxpool.Pool) error {

    closeCtx, cancel := context.WithTimeout(ctx, timeout)
    defer cancel()

    // first, shutdown the server to stop accepting new requests
    if err := server.Shutdown(closeCtx); err != nil {
        return err
    }

    // allow running transactions to finish, but if they don't finish within
    // ten seconds, cancel the context of all running transactions so that they
    // are forced to finish (albeit, with error)
    transactionsComplete := waitForTransacitons(time.Second*10, db)
    if !transactionsComplete {
        cancelContextOfEveryTransaction()
    }
    
    // since this call blocks until all transactions finished we must call it
    // only once we are sure that there are no more running transactions.
    db.Close(ctx)

    return nil
}

最佳的优雅终止序列是:

  • 关闭服务器。
  • 立即取消所有正在进行的请求的上下文(一旦数据库驱动程序尝试对其执行任何操作,就终止事务)。
  • 关闭连接池。
  • 退出。

[编辑]:替代终止序列(更优雅):

  • 接收到终止信号。
  • Pod 处于“终止”状态并已从负载均衡器中删除。
  • 关闭服务器并设置超时N。
  • 关闭连接池 - 短暂超时。 推理:由于
    server.Shutdown
    返回,不会返回任何回复。等待正在进行的事务的唯一原因是后台工作人员完成其工作,例如将日志写入数据库。
  • 如果仍有打开的事务阻止连接池关闭 - 终止这些事务并尝试再次关闭连接池。
  • 退出。
go kubernetes application-server pgx
1个回答
0
投票

为什么要重新发明轮子而不使用一些现有的库,这对你来说很神奇?

在我们的生产服务中,我们经常使用这个优雅的关闭库并且从未遇到过问题。它会等待,直到所有 HTTP 请求都得到服务(在给定的超时时间内),然后关闭。

使用方法再简单不过了。安装后

go mod download github.com/TV4/graceful

(最终:

go get -u github.com/TV4/graceful

您只需导入它:

import (
    // ...

    "github.com/TV4/graceful"
)

然后您可以在实例化

server
(包括您的关闭函数)后用以下一行代码替换所有代码:

server := ...
graceful.LogListenAndServe(server, logger)
© www.soinside.com 2019 - 2024. All rights reserved.