feat: drain readiness before graceful shutdown

This commit is contained in:
2026-09-20 19:41:28 +08:00
parent 656a81251e
commit 00d4156b25
5 changed files with 66 additions and 9 deletions

View File

@@ -4,7 +4,9 @@ package httpserver
import (
"context"
"encoding/json"
"errors"
"net/http"
"sync/atomic"
"time"
"git.yuntuo.hk/k2safe/orbit/internal/buildinfo"
@@ -14,6 +16,37 @@ import (
type Checker func(context.Context) error
// Readiness 将数据库/结构检查与进程排空状态合并为一个就绪门控。
// 收到终止信号后先调用 BeginDrain,再停止接收新请求;存量请求仍由
// http.Server.Shutdown 在限定时间内完成。这样编排平台有机会先看到 503。
type Readiness struct {
checker Checker
draining atomic.Bool
}
// NewReadiness 创建可在优雅下线期间切换为未就绪的门控。
func NewReadiness(checker Checker) *Readiness {
return &Readiness{checker: checker}
}
// Check 执行就绪检查;排空开始后始终返回内部错误,由 HTTP 层统一隐藏细节。
func (r *Readiness) Check(ctx context.Context) error {
if r == nil || r.draining.Load() {
return errors.New("server is draining")
}
if r.checker == nil {
return errors.New("readiness checker is unavailable")
}
return r.checker(ctx)
}
// BeginDrain 先于 http.Server.Shutdown 调用,阻止排空窗口内的新流量。
func (r *Readiness) BeginDrain() {
if r != nil {
r.draining.Store(true)
}
}
func New(addr string, ready Checker, tasks ...*service.Service) *http.Server {
mux := http.NewServeMux()
write := func(w http.ResponseWriter, status int, body map[string]string) {