fix(server,app,config): address code review findings
- Fix FD leak in DupListener: close *os.File after net.FileListener - Add cleanup of partially-duped listeners on DupListener failure - Make reload timeout configurable via shutdown.reload_timeout - Handle filepath.Abs errors in processIncludes instead of ignoring - Use net.ParseIP in isAnyAddr for robust IPv6 support
This commit is contained in:
parent
9b8ce2a08a
commit
728a9f454b
@ -187,6 +187,9 @@ func (a *App) reloadConfig() {
|
||||
for i, ln := range listeners {
|
||||
duped[i], err = server.DupListener(ln)
|
||||
if err != nil {
|
||||
for j := 0; j < i; j++ {
|
||||
_ = duped[j].Close()
|
||||
}
|
||||
a.logger.Error().Err(err).Msg("Failed to dup listener for reload")
|
||||
return
|
||||
}
|
||||
@ -205,6 +208,11 @@ func (a *App) reloadConfig() {
|
||||
}
|
||||
}()
|
||||
|
||||
reloadTimeout := a.cfg.Shutdown.ReloadTimeout
|
||||
if reloadTimeout <= 0 {
|
||||
reloadTimeout = 5 * time.Second
|
||||
}
|
||||
|
||||
select {
|
||||
case err := <-startErr:
|
||||
a.logger.Error().Err(err).Msg("Failed to start new server with reloaded config")
|
||||
@ -212,7 +220,7 @@ func (a *App) reloadConfig() {
|
||||
_ = ln.Close()
|
||||
}
|
||||
return
|
||||
case <-time.After(5 * time.Second):
|
||||
case <-time.After(reloadTimeout):
|
||||
}
|
||||
|
||||
oldSrv := a.srv
|
||||
|
||||
@ -140,7 +140,10 @@ func Load(path string) (*Config, error) {
|
||||
}
|
||||
|
||||
if len(cfg.Include) > 0 {
|
||||
absPath, _ := filepath.Abs(path)
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取配置文件绝对路径失败: %w", err)
|
||||
}
|
||||
visited := map[string]bool{absPath: true}
|
||||
if err := processIncludes(&cfg, filepath.Dir(path), 0, visited); err != nil {
|
||||
return nil, fmt.Errorf("处理配置引入失败: %w", err)
|
||||
@ -176,7 +179,10 @@ func processIncludes(cfg *Config, baseDir string, depth int, visited map[string]
|
||||
}
|
||||
|
||||
for _, match := range matches {
|
||||
absMatch, _ := filepath.Abs(match)
|
||||
absMatch, err := filepath.Abs(match)
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取引入文件绝对路径失败 %q: %w", match, err)
|
||||
}
|
||||
if visited[absMatch] {
|
||||
return fmt.Errorf("检测到循环引入: %s", absMatch)
|
||||
}
|
||||
|
||||
@ -222,6 +222,7 @@ func DefaultConfig() *Config {
|
||||
Shutdown: ShutdownConfig{
|
||||
GracefulTimeout: 30 * time.Second,
|
||||
FastTimeout: 5 * time.Second,
|
||||
ReloadTimeout: 5 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -612,6 +613,7 @@ func GenerateConfigYAML(cfg *Config) ([]byte, error) {
|
||||
buf.WriteString("shutdown:\n")
|
||||
fmt.Fprintf(&buf, " graceful_timeout: %ds # 优雅停止超时(SIGQUIT),等待活跃请求完成(0=使用默认30s)\n", int(cfg.Shutdown.GracefulTimeout.Seconds()))
|
||||
fmt.Fprintf(&buf, " fast_timeout: %ds # 快速停止超时(SIGINT/SIGTERM,0=使用默认5s)\n", int(cfg.Shutdown.FastTimeout.Seconds()))
|
||||
fmt.Fprintf(&buf, " reload_timeout: %ds # 热重载启动等待超时(SIGHUP,0=使用默认5s)\n", int(cfg.Shutdown.ReloadTimeout.Seconds()))
|
||||
buf.WriteString("\n")
|
||||
|
||||
// stream 配置
|
||||
|
||||
@ -181,6 +181,11 @@ type ShutdownConfig struct {
|
||||
// 接收到 SIGINT 或 SIGTERM 信号后,等待服务器关闭的最大时间
|
||||
// 默认: 5s(当值为 0 时使用默认值)
|
||||
FastTimeout time.Duration `yaml:"fast_timeout"`
|
||||
|
||||
// ReloadTimeout 热重载启动等待超时(SIGHUP)
|
||||
// 等待新服务器启动完成的最大时间,超时后视为启动成功
|
||||
// 默认: 5s(当值为 0 时使用默认值)
|
||||
ReloadTimeout time.Duration `yaml:"reload_timeout"`
|
||||
}
|
||||
|
||||
// ResolverConfig DNS 解析器配置。
|
||||
|
||||
@ -409,7 +409,11 @@ func (s *Server) tcpAddrMatch(inherited, target string) bool {
|
||||
}
|
||||
|
||||
func isAnyAddr(host string) bool {
|
||||
return host == "" || host == "0.0.0.0" || host == "::" || host == "[::]"
|
||||
if host == "" {
|
||||
return true
|
||||
}
|
||||
ip := net.ParseIP(host)
|
||||
return ip != nil && ip.IsUnspecified()
|
||||
}
|
||||
|
||||
// DupListener 复制 listener 的文件描述符,返回独立的 listener。
|
||||
@ -422,12 +426,14 @@ func DupListener(ln net.Listener) (net.Listener, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dup tcp listener: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
return net.FileListener(file)
|
||||
case *net.UnixListener:
|
||||
file, err := l.File()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dup unix listener: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
return net.FileListener(file)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported listener type: %T", ln)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user