refactor(server): 实现多服务器模式启动和关闭逻辑

- 新增 fastServers 列表支持多监听器
- 实现 startMultiServerMode() 并行启动多个服务器
- 添加 shutdownServers() 并行关闭多个 fasthttp.Server
- 重构 StopWithTimeout/GracefulStop 支持多服务器关闭
- startSingleMode 使用 Servers[0] 配置(迁移后)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-04-15 10:33:52 +08:00
parent 6007e96f69
commit 1f170b1951

View File

@ -24,7 +24,9 @@ import (
"crypto/tls"
"fmt"
"net"
"os"
"strings"
"sync"
"sync/atomic"
"time"
@ -71,6 +73,7 @@ type Server struct {
pool *GoroutinePool
config *config.Config
fastServer *fasthttp.Server
fastServers []*fasthttp.Server // 多监听器模式使用
proxies []*proxy.Proxy
listeners []net.Listener
healthCheckers []*proxy.HealthChecker
@ -424,10 +427,19 @@ func (s *Server) Start() error {
return err
}
if s.config.HasServers() {
// 根据模式选择启动方式
mode := s.config.GetMode()
switch mode {
case config.ServerModeSingle:
return s.startSingleMode()
case config.ServerModeVHost:
return s.startVHostMode()
case config.ServerModeMultiServer:
return s.startMultiServerMode()
default:
// 默认使用单服务器模式
return s.startSingleMode()
}
return s.startSingleMode()
}
// startSingleMode 单服务器模式启动。
@ -442,6 +454,9 @@ func (s *Server) Start() error {
// - 静态文件服务作为 fallback 处理非代理路径的请求
// - 使用零拷贝传输优化大文件传输
func (s *Server) startSingleMode() error {
// 使用 Servers[0] 配置(迁移后 Server 字段为空)
serverCfg := &s.config.Servers[0]
router := handler.NewRouter()
// 注册状态监控端点(如果配置)
@ -466,13 +481,13 @@ func (s *Server) startSingleMode() error {
}
// 注册代理路由
s.registerProxyRoutes(router, &s.config.Server)
s.registerProxyRoutes(router, serverCfg)
// 静态文件服务
s.registerStaticHandlers(router, &s.config.Server)
s.registerStaticHandlers(router, serverCfg)
// 构建中间件链
chain, err := s.buildMiddlewareChain(&s.config.Server)
chain, err := s.buildMiddlewareChain(serverCfg)
if err != nil {
return err
}
@ -489,27 +504,27 @@ func (s *Server) startSingleMode() error {
s.fastServer = &fasthttp.Server{
Name: "lolly",
Handler: s.handler,
ReadTimeout: s.config.Server.ReadTimeout,
WriteTimeout: s.config.Server.WriteTimeout,
IdleTimeout: s.config.Server.IdleTimeout,
MaxConnsPerIP: s.config.Server.MaxConnsPerIP,
MaxRequestsPerConn: s.config.Server.MaxRequestsPerConn,
ReadTimeout: serverCfg.ReadTimeout,
WriteTimeout: serverCfg.WriteTimeout,
IdleTimeout: serverCfg.IdleTimeout,
MaxConnsPerIP: serverCfg.MaxConnsPerIP,
MaxRequestsPerConn: serverCfg.MaxRequestsPerConn,
CloseOnShutdown: true,
}
s.running = true
// 创建监听器并保存,用于热升级
ln, err := net.Listen("tcp", s.config.Server.Listen)
ln, err := net.Listen("tcp", serverCfg.Listen)
if err != nil {
return fmt.Errorf("failed to listen: %w", err)
}
s.listeners = []net.Listener{ln}
// 检查是否配置了 SSL/TLS
if s.config.Server.SSL.Cert != "" && s.config.Server.SSL.Key != "" {
if serverCfg.SSL.Cert != "" && serverCfg.SSL.Key != "" {
var err error
s.tlsManager, err = ssl.NewTLSManager(&s.config.Server.SSL)
s.tlsManager, err = ssl.NewTLSManager(&serverCfg.SSL)
if err != nil {
return fmt.Errorf("创建 TLS 管理器失败: %w", err)
}
@ -601,30 +616,33 @@ func (s *Server) startVHostMode() error {
// 包装统计追踪
s.handler = s.trackStats(s.handler)
// 使用 Servers[0] 配置(迁移后 Server 字段为空)
serverCfg := &s.config.Servers[0]
s.fastServer = &fasthttp.Server{
Name: "lolly",
Handler: s.handler,
ReadTimeout: s.config.Server.ReadTimeout,
WriteTimeout: s.config.Server.WriteTimeout,
IdleTimeout: s.config.Server.IdleTimeout,
MaxConnsPerIP: s.config.Server.MaxConnsPerIP,
MaxRequestsPerConn: s.config.Server.MaxRequestsPerConn,
ReadTimeout: serverCfg.ReadTimeout,
WriteTimeout: serverCfg.WriteTimeout,
IdleTimeout: serverCfg.IdleTimeout,
MaxConnsPerIP: serverCfg.MaxConnsPerIP,
MaxRequestsPerConn: serverCfg.MaxRequestsPerConn,
CloseOnShutdown: true,
}
s.running = true
// 创建监听器并保存,用于热升级
ln, err := net.Listen("tcp", s.config.Server.Listen)
ln, err := net.Listen("tcp", serverCfg.Listen)
if err != nil {
return fmt.Errorf("failed to listen: %w", err)
}
s.listeners = []net.Listener{ln}
// 检查是否配置了 SSL/TLS
if s.config.Server.SSL.Cert != "" && s.config.Server.SSL.Key != "" {
if serverCfg.SSL.Cert != "" && serverCfg.SSL.Key != "" {
var err error
s.tlsManager, err = ssl.NewTLSManager(&s.config.Server.SSL)
s.tlsManager, err = ssl.NewTLSManager(&serverCfg.SSL)
if err != nil {
return fmt.Errorf("创建 TLS 管理器失败: %w", err)
}
@ -635,6 +653,145 @@ func (s *Server) startVHostMode() error {
return s.fastServer.Serve(ln)
}
// startMultiServerMode 多服务器模式启动。
//
// 为每个配置的服务器创建独立的 fasthttp.Server 实例,
// 每个实例监听各自的地址并运行在独立的 goroutine 中。
//
// 返回值:
// - error: 启动过程中遇到的第一个错误(或全部成功时返回 nil
//
// 注意事项:
// - 每个服务器有独立的中间件配置
// - 热升级场景下回退到虚拟主机模式
// - 使用 goroutine 并行启动多个服务器
func (s *Server) startMultiServerMode() error {
// 热升级检测multi_server 热升级未实现,回退到 vhost 模式
if os.Getenv("GRACEFUL_UPGRADE") == "1" {
logging.Warn().Msg("热升级模式下 multi_server 模式未实现,回退到虚拟主机模式")
return s.startVHostMode()
}
s.fastServers = make([]*fasthttp.Server, len(s.config.Servers))
s.listeners = make([]net.Listener, len(s.config.Servers))
var wg sync.WaitGroup
errCh := make(chan error, len(s.config.Servers))
// 并行创建监听器和 fasthttp.Server
for i := range s.config.Servers {
wg.Add(1)
go func(idx int) {
defer wg.Done()
serverCfg := &s.config.Servers[idx]
// 创建监听器
ln, err := net.Listen("tcp", serverCfg.Listen)
if err != nil {
errCh <- fmt.Errorf("监听地址 %s 失败: %w", serverCfg.Listen, err)
return
}
s.listeners[idx] = ln
// 创建路由器
router := handler.NewRouter()
s.registerProxyRoutes(router, serverCfg)
// 静态文件服务
s.registerStaticHandlers(router, serverCfg)
// 构建独立的中间件链
chain, err := s.buildMiddlewareChain(serverCfg)
if err != nil {
errCh <- fmt.Errorf("构建中间件链失败 (server[%d]): %w", idx, err)
return
}
// 应用中间件
h := chain.Apply(router.Handler())
if s.pool != nil {
h = s.pool.WrapHandler(h)
}
h = s.trackStats(h)
// 创建 fasthttp.Server
fastSrv := &fasthttp.Server{
Name: "lolly",
Handler: h,
ReadTimeout: serverCfg.ReadTimeout,
WriteTimeout: serverCfg.WriteTimeout,
IdleTimeout: serverCfg.IdleTimeout,
MaxConnsPerIP: serverCfg.MaxConnsPerIP,
MaxRequestsPerConn: serverCfg.MaxRequestsPerConn,
CloseOnShutdown: true,
}
// 检查 SSL 配置
if serverCfg.SSL.Cert != "" && serverCfg.SSL.Key != "" {
tlsManager, err := ssl.NewTLSManager(&serverCfg.SSL)
if err != nil {
errCh <- fmt.Errorf("创建 TLS 管理器失败 (server[%d]): %w", idx, err)
return
}
fastSrv.TLSConfig = tlsManager.GetTLSConfig()
}
s.fastServers[idx] = fastSrv
}(i)
}
// 等待所有 goroutine 完成
wg.Wait()
close(errCh)
// 检查是否有错误
var firstErr error
for err := range errCh {
if firstErr == nil {
firstErr = err
}
}
// 如果有错误,清理已创建的监听器
if firstErr != nil {
for _, ln := range s.listeners {
if ln != nil {
_ = ln.Close()
}
}
return firstErr
}
s.running = true
// 启动所有服务器
for idx, fastSrv := range s.fastServers {
ln := s.listeners[idx]
if fastSrv == nil || ln == nil {
continue
}
wg.Add(1)
go func(f *fasthttp.Server, l net.Listener, i int) {
defer wg.Done()
var serveErr error
if f.TLSConfig != nil {
serveErr = f.ServeTLS(l, "", "")
} else {
serveErr = f.Serve(l)
}
if serveErr != nil {
logging.Error().Err(serveErr).Msgf("服务器 [%d] 监听 %s 时发生错误", i, l.Addr())
}
}(fastSrv, ln, idx)
}
// 等待服务器停止(阻塞)
wg.Wait()
return nil
}
// registerProxyRoutes 注册代理路由。
//
// 根据配置为路由器注册代理路径,创建代理处理器和健康检查器。
@ -692,6 +849,68 @@ func (s *Server) registerProxyRoutes(router *handler.Router, serverCfg *config.S
}
}
// shutdownServers 并行关闭多个 fasthttp.Server 实例。
//
// 使用 goroutine 并行关闭所有服务器,收集所有错误并返回聚合错误。
// 部分服务器关闭失败不会影响其他服务器的关闭。
//
// 参数:
// - ctx: 关闭上下文,用于控制超时和取消
// - servers: 要关闭的 fasthttp.Server 实例列表
//
// 返回值:
// - error: 聚合错误,无错误或全部成功时返回 nil
func shutdownServers(ctx context.Context, servers []*fasthttp.Server) error {
if len(servers) == 0 {
return nil
}
var (
mu sync.Mutex
errs []error
wg sync.WaitGroup
)
for _, srv := range servers {
if srv == nil {
continue
}
wg.Add(1)
go func(s *fasthttp.Server) {
defer wg.Done()
if err := s.Shutdown(); err != nil {
mu.Lock()
errs = append(errs, err)
mu.Unlock()
}
}(srv)
}
// 等待所有关闭完成或上下文取消
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
if len(errs) == 0 {
return nil
}
if len(errs) == 1 {
return errs[0]
}
msgs := make([]string, len(errs))
for i, e := range errs {
msgs[i] = e.Error()
}
return fmt.Errorf("关闭服务器时发生 %d 个错误: %s", len(errs), strings.Join(msgs, "; "))
case <-ctx.Done():
return ctx.Err()
}
}
// StopWithTimeout 快速停止服务器(支持自定义超时)。
//
// 立即停止服务器,不等待正在处理的请求完成。
@ -747,11 +966,16 @@ func (s *Server) StopWithTimeout(timeout time.Duration) error {
logging.Info().Msg("Lua 引擎已关闭")
}
if s.fastServer != nil {
// 使用传入的 timeout
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// 多服务器模式:并行关闭所有 fasthttp.Server
if len(s.fastServers) > 0 {
return shutdownServers(ctx, s.fastServers)
}
// 单服务器模式:关闭单个 fasthttp.Server
if s.fastServer != nil {
done := make(chan struct{})
go func() {
_ = s.fastServer.Shutdown()
@ -762,7 +986,6 @@ func (s *Server) StopWithTimeout(timeout time.Duration) error {
case <-done:
return nil
case <-ctx.Done():
// timeout直接返回
return ctx.Err()
}
}
@ -836,10 +1059,16 @@ func (s *Server) GracefulStop(timeout time.Duration) error {
logging.Info().Msg("Lua 引擎已关闭")
}
if s.fastServer != nil {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// 多服务器模式:并行关闭所有 fasthttp.Server
if len(s.fastServers) > 0 {
return shutdownServers(ctx, s.fastServers)
}
// 单服务器模式:关闭单个 fasthttp.Server
if s.fastServer != nil {
done := make(chan struct{})
go func() {
_ = s.fastServer.Shutdown()