refactor(app): 提取通用函数到 import.go,注释改为英文
- 提取 Run、generateConfig、printVersion 到 import.go - 删除冗长的中文注释,改为简洁英文 - 同步 Windows 版本的结构变化 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
4f24dd7fd7
commit
b444f5b1d7
@ -1,22 +1,5 @@
|
|||||||
//go:build !windows
|
//go:build !windows
|
||||||
|
|
||||||
// Package app 提供应用程序的启动和运行逻辑。
|
|
||||||
//
|
|
||||||
// 该文件包含应用程序相关的核心逻辑,包括:
|
|
||||||
// - 应用程序生命周期管理
|
|
||||||
// - 信号处理(优雅停止、重载配置、热升级)
|
|
||||||
// - 配置加载和版本信息
|
|
||||||
//
|
|
||||||
// 主要用途:
|
|
||||||
//
|
|
||||||
// 用于启动和管理服务器进程,处理系统信号和运行时操作。
|
|
||||||
//
|
|
||||||
// 注意事项:
|
|
||||||
// - 支持热升级(USR2 信号)
|
|
||||||
// - 支持配置重载(HUP 信号)
|
|
||||||
// - 支持日志重新打开(USR1 信号)
|
|
||||||
//
|
|
||||||
// 作者:xfy
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -35,13 +18,9 @@ import (
|
|||||||
"rua.plus/lolly/internal/server"
|
"rua.plus/lolly/internal/server"
|
||||||
"rua.plus/lolly/internal/stream"
|
"rua.plus/lolly/internal/stream"
|
||||||
"rua.plus/lolly/internal/variable"
|
"rua.plus/lolly/internal/variable"
|
||||||
"rua.plus/lolly/internal/version"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// App 应用程序结构。
|
// App manages the server lifecycle, including HTTP, HTTP/3, Stream servers and graceful upgrades.
|
||||||
//
|
|
||||||
// 管理服务器的完整生命周期,包括 HTTP 服务器、HTTP/3 服务器、Stream 服务器
|
|
||||||
// 和热升级管理器。
|
|
||||||
type App struct {
|
type App struct {
|
||||||
resv resolver.Resolver
|
resv resolver.Resolver
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
@ -57,90 +36,22 @@ type App struct {
|
|||||||
listeners []net.Listener
|
listeners []net.Listener
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewApp 创建应用程序实例。
|
|
||||||
//
|
|
||||||
// 该函数初始化 App 结构体,保存配置文件路径供后续加载使用。
|
|
||||||
//
|
|
||||||
// 参数:
|
|
||||||
// - cfgPath: 配置文件路径(YAML 格式),用于加载服务器配置
|
|
||||||
//
|
|
||||||
// 返回值:
|
|
||||||
// - *App: 初始化的应用程序实例,包含配置路径等信息
|
|
||||||
func NewApp(cfgPath string) *App {
|
func NewApp(cfgPath string) *App {
|
||||||
return &App{
|
return &App{
|
||||||
cfgPath: cfgPath,
|
cfgPath: cfgPath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPidFile 设置 PID 文件路径。
|
|
||||||
func (a *App) SetPidFile(path string) {
|
func (a *App) SetPidFile(path string) {
|
||||||
a.pidFile = path
|
a.pidFile = path
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLogFile 设置日志文件路径。
|
|
||||||
func (a *App) SetLogFile(path string) {
|
func (a *App) SetLogFile(path string) {
|
||||||
a.logFile = path
|
a.logFile = path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run 应用程序入口函数。
|
// Run starts the application: loads config, creates servers, and handles signals.
|
||||||
//
|
|
||||||
// 根据参数决定行为:生成配置、打印版本或启动应用。
|
|
||||||
//
|
|
||||||
// 参数:
|
|
||||||
// - cfgPath: 配置文件路径
|
|
||||||
// - genConfig: 是否仅生成默认配置并退出
|
|
||||||
// - outputPath: 配置输出路径,为空时输出到 stdout
|
|
||||||
// - showVersion: 是否仅打印版本信息并退出
|
|
||||||
//
|
|
||||||
// 返回值:
|
|
||||||
// - int: 退出码,0 表示正常退出,1 表示异常
|
|
||||||
func Run(cfgPath string, genConfig bool, outputPath string, showVersion bool) int {
|
|
||||||
if genConfig {
|
|
||||||
return generateConfig(outputPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
if showVersion {
|
|
||||||
printVersion()
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
app := NewApp(cfgPath)
|
|
||||||
return app.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
// generateConfig 生成默认配置文件。
|
|
||||||
func generateConfig(outputPath string) int {
|
|
||||||
cfg := config.DefaultConfig()
|
|
||||||
yamlData, err := config.GenerateConfigYAML(cfg)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "生成配置失败: %v\n", err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if outputPath == "" {
|
|
||||||
fmt.Print(string(yamlData))
|
|
||||||
} else {
|
|
||||||
if err := os.WriteFile(outputPath, yamlData, 0o644); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "写入文件失败: %v\n", err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
fmt.Printf("配置已写入: %s\n", outputPath)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// printVersion 打印版本信息。
|
|
||||||
func printVersion() {
|
|
||||||
fmt.Printf("lolly version %s\n", version.Version)
|
|
||||||
fmt.Printf(" Git: %s (%s)\n", version.GitCommit, version.GitBranch)
|
|
||||||
fmt.Printf(" Built: %s\n", version.BuildTime)
|
|
||||||
fmt.Printf(" Go: %s\n", version.GoVersion)
|
|
||||||
fmt.Printf(" Platform: %s\n", version.BuildPlatform)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run 启动应用程序。
|
|
||||||
func (a *App) Run() int {
|
func (a *App) Run() int {
|
||||||
// 加载配置
|
|
||||||
cfg, err := config.Load(a.cfgPath)
|
cfg, err := config.Load(a.cfgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "加载配置失败: %v\n", err)
|
fmt.Fprintf(os.Stderr, "加载配置失败: %v\n", err)
|
||||||
@ -149,7 +60,6 @@ func (a *App) Run() int {
|
|||||||
a.cfg = cfg
|
a.cfg = cfg
|
||||||
a.logger = logging.NewAppLogger(&cfg.Logging)
|
a.logger = logging.NewAppLogger(&cfg.Logging)
|
||||||
|
|
||||||
// 设置全局变量
|
|
||||||
variable.SetGlobalVariables(cfg.Variables.Set)
|
variable.SetGlobalVariables(cfg.Variables.Set)
|
||||||
if len(cfg.Variables.Set) > 0 {
|
if len(cfg.Variables.Set) > 0 {
|
||||||
a.logger.LogStartup("全局变量已加载", map[string]string{
|
a.logger.LogStartup("全局变量已加载", map[string]string{
|
||||||
@ -157,21 +67,18 @@ func (a *App) Run() int {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否是子进程(热升级)
|
// Inherit parent listeners when running as a graceful upgrade child.
|
||||||
if os.Getenv("GRACEFUL_UPGRADE") == "1" {
|
if os.Getenv("GRACEFUL_UPGRADE") == "1" {
|
||||||
a.logger.LogStartup("检测到热升级模式,继承父进程监听器", nil)
|
a.logger.LogStartup("检测到热升级模式,继承父进程监听器", nil)
|
||||||
// 创建升级管理器以获取继承的监听器
|
|
||||||
a.upgradeMgr = server.NewUpgradeManager(nil)
|
a.upgradeMgr = server.NewUpgradeManager(nil)
|
||||||
listeners, err := a.upgradeMgr.GetInheritedListeners()
|
listeners, err := a.upgradeMgr.GetInheritedListeners()
|
||||||
if err == nil && len(listeners) > 0 {
|
if err == nil && len(listeners) > 0 {
|
||||||
// 暂时保存监听器,等服务器创建后再设置
|
|
||||||
a.listeners = listeners
|
a.listeners = listeners
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.logger.LogStartup("配置加载成功", map[string]string{"config_path": a.cfgPath})
|
a.logger.LogStartup("配置加载成功", map[string]string{"config_path": a.cfgPath})
|
||||||
|
|
||||||
// 记录监听地址(支持多服务器模式)
|
|
||||||
mode := a.cfg.GetMode()
|
mode := a.cfg.GetMode()
|
||||||
if mode == config.ServerModeMultiServer {
|
if mode == config.ServerModeMultiServer {
|
||||||
for i, srv := range a.cfg.Servers {
|
for i, srv := range a.cfg.Servers {
|
||||||
@ -185,7 +92,6 @@ func (a *App) Run() int {
|
|||||||
a.logger.LogStartup("监听地址", map[string]string{"listen": a.cfg.Servers[0].Listen})
|
a.logger.LogStartup("监听地址", map[string]string{"listen": a.cfg.Servers[0].Listen})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 DNS 解析器(如果启用)
|
|
||||||
if a.cfg.Resolver.Enabled {
|
if a.cfg.Resolver.Enabled {
|
||||||
a.resv = resolver.New(&a.cfg.Resolver)
|
a.resv = resolver.New(&a.cfg.Resolver)
|
||||||
a.logger.LogStartup("DNS 解析器已启用", map[string]string{
|
a.logger.LogStartup("DNS 解析器已启用", map[string]string{
|
||||||
@ -194,24 +100,19 @@ func (a *App) Run() int {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 HTTP 服务器
|
|
||||||
a.srv = server.New(a.cfg)
|
a.srv = server.New(a.cfg)
|
||||||
|
|
||||||
// 设置 DNS 解析器到服务器
|
|
||||||
if a.resv != nil {
|
if a.resv != nil {
|
||||||
a.srv.SetResolver(a.resv)
|
a.srv.SetResolver(a.resv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有继承的监听器,设置到服务器
|
|
||||||
if len(a.listeners) > 0 {
|
if len(a.listeners) > 0 {
|
||||||
a.srv.SetListeners(a.listeners)
|
a.srv.SetListeners(a.listeners)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建 Stream 服务器(如果配置了)
|
|
||||||
if len(a.cfg.Stream) > 0 {
|
if len(a.cfg.Stream) > 0 {
|
||||||
a.streamSrv = stream.NewServer()
|
a.streamSrv = stream.NewServer()
|
||||||
for _, sc := range a.cfg.Stream {
|
for _, sc := range a.cfg.Stream {
|
||||||
// 转换目标配置
|
|
||||||
targets := make([]stream.TargetSpec, len(sc.Upstream.Targets))
|
targets := make([]stream.TargetSpec, len(sc.Upstream.Targets))
|
||||||
for i, t := range sc.Upstream.Targets {
|
for i, t := range sc.Upstream.Targets {
|
||||||
targets[i] = stream.TargetSpec{
|
targets[i] = stream.TargetSpec{
|
||||||
@ -220,12 +121,10 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加上游配置
|
|
||||||
if err := a.streamSrv.AddUpstream(sc.Listen, targets, sc.Upstream.LoadBalance, stream.HealthCheckSpec{}); err != nil {
|
if err := a.streamSrv.AddUpstream(sc.Listen, targets, sc.Upstream.LoadBalance, stream.HealthCheckSpec{}); err != nil {
|
||||||
a.logger.Error().Err(err).Msg("添加 Stream 上游失败")
|
a.logger.Error().Err(err).Msg("添加 Stream 上游失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听端口
|
|
||||||
if sc.Protocol == "udp" {
|
if sc.Protocol == "udp" {
|
||||||
if err := a.streamSrv.ListenUDP(sc.Listen, sc.Listen, 60*time.Second); err != nil {
|
if err := a.streamSrv.ListenUDP(sc.Listen, sc.Listen, 60*time.Second); err != nil {
|
||||||
a.logger.Error().Err(err).Str("listen", sc.Listen).Msg("监听 UDP 失败")
|
a.logger.Error().Err(err).Str("listen", sc.Listen).Msg("监听 UDP 失败")
|
||||||
@ -237,7 +136,6 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启动 Stream 服务器
|
|
||||||
go func() {
|
go func() {
|
||||||
a.logger.LogStartup("Stream 服务器启动中", nil)
|
a.logger.LogStartup("Stream 服务器启动中", nil)
|
||||||
if err := a.streamSrv.Start(); err != nil {
|
if err := a.streamSrv.Start(); err != nil {
|
||||||
@ -246,7 +144,6 @@ func (a *App) Run() int {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建并启动 HTTP/3 服务器(如果启用)
|
|
||||||
if a.cfg.HTTP3.Enabled && a.cfg.Servers[0].SSL.Cert != "" {
|
if a.cfg.HTTP3.Enabled && a.cfg.Servers[0].SSL.Cert != "" {
|
||||||
tlsConfig, err := a.srv.GetTLSConfig()
|
tlsConfig, err := a.srv.GetTLSConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -266,13 +163,11 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建并启动 HTTP/2 服务器(如果启用且配置了 TLS)
|
|
||||||
if a.cfg.Servers[0].SSL.HTTP2.Enabled && a.cfg.Servers[0].SSL.Cert != "" {
|
if a.cfg.Servers[0].SSL.HTTP2.Enabled && a.cfg.Servers[0].SSL.Cert != "" {
|
||||||
tlsConfig, err := a.srv.GetTLSConfig()
|
tlsConfig, err := a.srv.GetTLSConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.logger.Error().Err(err).Msg("获取 TLS 配置失败,跳过 HTTP/2")
|
a.logger.Error().Err(err).Msg("获取 TLS 配置失败,跳过 HTTP/2")
|
||||||
} else {
|
} else {
|
||||||
// 创建 HTTP/2 服务器,共享同一个 handler
|
|
||||||
a.http2Srv, err = http2.NewServer(&a.cfg.Servers[0].SSL.HTTP2, a.srv.GetHandler(), tlsConfig)
|
a.http2Srv, err = http2.NewServer(&a.cfg.Servers[0].SSL.HTTP2, a.srv.GetHandler(), tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.logger.Error().Err(err).Msg("创建 HTTP/2 服务器失败")
|
a.logger.Error().Err(err).Msg("创建 HTTP/2 服务器失败")
|
||||||
@ -283,8 +178,7 @@ func (a *App) Run() int {
|
|||||||
"max_concurrent_streams": fmt.Sprintf("%d", a.cfg.Servers[0].SSL.HTTP2.MaxConcurrentStreams),
|
"max_concurrent_streams": fmt.Sprintf("%d", a.cfg.Servers[0].SSL.HTTP2.MaxConcurrentStreams),
|
||||||
"push_enabled": fmt.Sprintf("%t", a.cfg.Servers[0].SSL.HTTP2.PushEnabled),
|
"push_enabled": fmt.Sprintf("%t", a.cfg.Servers[0].SSL.HTTP2.PushEnabled),
|
||||||
})
|
})
|
||||||
// HTTP/2 服务器使用与主服务器相同的监听器
|
// HTTP/2 shares the main server's listener; ALPN negotiates protocol selection.
|
||||||
// 通过 ALPN 协商自动处理协议选择
|
|
||||||
listeners := a.srv.GetListeners()
|
listeners := a.srv.GetListeners()
|
||||||
if len(listeners) > 0 {
|
if len(listeners) > 0 {
|
||||||
if err := a.http2Srv.Serve(listeners[0]); err != nil {
|
if err := a.http2Srv.Serve(listeners[0]); err != nil {
|
||||||
@ -298,7 +192,6 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建升级管理器
|
|
||||||
a.upgradeMgr = server.NewUpgradeManager(a.srv)
|
a.upgradeMgr = server.NewUpgradeManager(a.srv)
|
||||||
a.srv.SetUpgradeManager(a.upgradeMgr)
|
a.srv.SetUpgradeManager(a.upgradeMgr)
|
||||||
if a.pidFile != "" {
|
if a.pidFile != "" {
|
||||||
@ -306,11 +199,9 @@ func (a *App) Run() int {
|
|||||||
_ = a.upgradeMgr.WritePid()
|
_ = a.upgradeMgr.WritePid()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启动信号处理
|
|
||||||
sigChan := make(chan os.Signal, 1)
|
sigChan := make(chan os.Signal, 1)
|
||||||
a.setupSignalHandlers(sigChan)
|
a.setupSignalHandlers(sigChan)
|
||||||
|
|
||||||
// 启动 HTTP 服务器
|
|
||||||
errChan := make(chan error, 1)
|
errChan := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
a.logger.LogStartup("HTTP 服务器启动中", nil)
|
a.logger.LogStartup("HTTP 服务器启动中", nil)
|
||||||
@ -319,17 +210,14 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// SIGINT 计数器,用于强制退出
|
|
||||||
sigintCount := 0
|
sigintCount := 0
|
||||||
|
|
||||||
// 等待信号或启动错误
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case err := <-errChan:
|
case err := <-errChan:
|
||||||
a.logger.Error().Err(err).Msg("服务器启动失败")
|
a.logger.Error().Err(err).Msg("服务器启动失败")
|
||||||
return 1
|
return 1
|
||||||
case sig := <-sigChan:
|
case sig := <-sigChan:
|
||||||
// 多次 SIGINT 强制退出
|
|
||||||
if sig == syscall.SIGINT {
|
if sig == syscall.SIGINT {
|
||||||
sigintCount++
|
sigintCount++
|
||||||
if sigintCount >= 3 {
|
if sigintCount >= 3 {
|
||||||
@ -338,33 +226,28 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !a.handleSignal(sig) {
|
if !a.handleSignal(sig) {
|
||||||
// 返回 false 表示退出
|
|
||||||
a.logger.LogShutdown("服务器已停止")
|
a.logger.LogShutdown("服务器已停止")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
// 返回 true 表示继续运行(如重载配置)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupSignalHandlers 设置信号处理。
|
|
||||||
func (a *App) setupSignalHandlers(sigChan chan<- os.Signal) {
|
func (a *App) setupSignalHandlers(sigChan chan<- os.Signal) {
|
||||||
signal.Notify(sigChan,
|
signal.Notify(sigChan,
|
||||||
syscall.SIGTERM, // 快速停止(kill 或 systemd stop)
|
syscall.SIGTERM,
|
||||||
syscall.SIGINT, // 快速停止(Ctrl+C)
|
syscall.SIGINT,
|
||||||
syscall.SIGQUIT, // 优雅停止
|
syscall.SIGQUIT,
|
||||||
syscall.SIGHUP, // 重载配置
|
syscall.SIGHUP,
|
||||||
syscall.SIGUSR1, // 重新打开日志
|
syscall.SIGUSR1,
|
||||||
syscall.SIGUSR2, // 热升级
|
syscall.SIGUSR2,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleSignal 处理信号,返回 false 表示退出。
|
// handleSignal returns false to indicate the app should exit.
|
||||||
func (a *App) handleSignal(sig os.Signal) bool {
|
func (a *App) handleSignal(sig os.Signal) bool {
|
||||||
// 防御性 nil-check:确保 a.cfg 不为 nil
|
|
||||||
if a.cfg == nil {
|
if a.cfg == nil {
|
||||||
a.logger.Error().Msg("信号处理失败: 配置为 nil,使用默认超时")
|
a.logger.Error().Msg("信号处理失败: 配置为 nil,使用默认超时")
|
||||||
// 使用默认超时继续处理信号
|
|
||||||
a.cfg = &config.Config{
|
a.cfg = &config.Config{
|
||||||
Shutdown: config.ShutdownConfig{
|
Shutdown: config.ShutdownConfig{
|
||||||
GracefulTimeout: 30 * time.Second,
|
GracefulTimeout: 30 * time.Second,
|
||||||
@ -375,10 +258,9 @@ func (a *App) handleSignal(sig os.Signal) bool {
|
|||||||
|
|
||||||
switch sig {
|
switch sig {
|
||||||
case syscall.SIGQUIT:
|
case syscall.SIGQUIT:
|
||||||
// 优雅停止:等待请求完成
|
|
||||||
timeout := a.cfg.Shutdown.GracefulTimeout
|
timeout := a.cfg.Shutdown.GracefulTimeout
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
timeout = 30 * time.Second // 默认值
|
timeout = 30 * time.Second
|
||||||
}
|
}
|
||||||
a.logger.LogSignal("SIGQUIT", fmt.Sprintf("优雅停止(等待 %v)", timeout))
|
a.logger.LogSignal("SIGQUIT", fmt.Sprintf("优雅停止(等待 %v)", timeout))
|
||||||
a.shutdownHTTP2()
|
a.shutdownHTTP2()
|
||||||
@ -387,10 +269,9 @@ func (a *App) handleSignal(sig os.Signal) bool {
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
case syscall.SIGTERM, syscall.SIGINT:
|
case syscall.SIGTERM, syscall.SIGINT:
|
||||||
// 快速停止
|
|
||||||
timeout := a.cfg.Shutdown.FastTimeout
|
timeout := a.cfg.Shutdown.FastTimeout
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
timeout = 5 * time.Second // 默认值
|
timeout = 5 * time.Second
|
||||||
}
|
}
|
||||||
sigTyped, ok := sig.(syscall.Signal)
|
sigTyped, ok := sig.(syscall.Signal)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -404,19 +285,16 @@ func (a *App) handleSignal(sig os.Signal) bool {
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
case syscall.SIGHUP:
|
case syscall.SIGHUP:
|
||||||
// 重载配置
|
|
||||||
a.logger.LogSignal("SIGHUP", "重载配置")
|
a.logger.LogSignal("SIGHUP", "重载配置")
|
||||||
a.reloadConfig()
|
a.reloadConfig()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case syscall.SIGUSR1:
|
case syscall.SIGUSR1:
|
||||||
// 重新打开日志
|
|
||||||
a.logger.LogSignal("SIGUSR1", "重新打开日志")
|
a.logger.LogSignal("SIGUSR1", "重新打开日志")
|
||||||
a.reopenLogs()
|
a.reopenLogs()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case syscall.SIGUSR2:
|
case syscall.SIGUSR2:
|
||||||
// 热升级
|
|
||||||
a.logger.LogSignal("SIGUSR2", "执行热升级")
|
a.logger.LogSignal("SIGUSR2", "执行热升级")
|
||||||
a.gracefulUpgrade()
|
a.gracefulUpgrade()
|
||||||
return true
|
return true
|
||||||
@ -427,7 +305,6 @@ func (a *App) handleSignal(sig os.Signal) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shutdownHTTP3 关闭 HTTP/3 服务器。
|
|
||||||
func (a *App) shutdownHTTP3() {
|
func (a *App) shutdownHTTP3() {
|
||||||
if a.http3Srv != nil {
|
if a.http3Srv != nil {
|
||||||
if err := a.http3Srv.Stop(); err != nil {
|
if err := a.http3Srv.Stop(); err != nil {
|
||||||
@ -436,7 +313,6 @@ func (a *App) shutdownHTTP3() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shutdownHTTP2 关闭 HTTP/2 服务器。
|
|
||||||
func (a *App) shutdownHTTP2() {
|
func (a *App) shutdownHTTP2() {
|
||||||
if a.http2Srv != nil {
|
if a.http2Srv != nil {
|
||||||
if err := a.http2Srv.Stop(); err != nil {
|
if err := a.http2Srv.Stop(); err != nil {
|
||||||
@ -445,7 +321,6 @@ func (a *App) shutdownHTTP2() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reloadConfig 重载配置。
|
|
||||||
func (a *App) reloadConfig() {
|
func (a *App) reloadConfig() {
|
||||||
newCfg, err := config.Load(a.cfgPath)
|
newCfg, err := config.Load(a.cfgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -453,15 +328,12 @@ func (a *App) reloadConfig() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新配置
|
|
||||||
a.cfg = newCfg
|
a.cfg = newCfg
|
||||||
a.logger = logging.NewAppLogger(&newCfg.Logging)
|
a.logger = logging.NewAppLogger(&newCfg.Logging)
|
||||||
a.logger.LogStartup("配置重载成功", nil)
|
a.logger.LogStartup("配置重载成功", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// reopenLogs 重新打开日志文件。
|
|
||||||
func (a *App) reopenLogs() {
|
func (a *App) reopenLogs() {
|
||||||
// 重新初始化日志系统
|
|
||||||
if a.cfg != nil {
|
if a.cfg != nil {
|
||||||
logging.Init(a.cfg.Logging.Error.Level, a.cfg.Logging.Format)
|
logging.Init(a.cfg.Logging.Error.Level, a.cfg.Logging.Format)
|
||||||
a.logger = logging.NewAppLogger(&a.cfg.Logging)
|
a.logger = logging.NewAppLogger(&a.cfg.Logging)
|
||||||
@ -469,22 +341,18 @@ func (a *App) reopenLogs() {
|
|||||||
a.logger.LogStartup("日志已重新打开", nil)
|
a.logger.LogStartup("日志已重新打开", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// gracefulUpgrade 执行热升级。
|
|
||||||
func (a *App) gracefulUpgrade() {
|
func (a *App) gracefulUpgrade() {
|
||||||
// 获取当前可执行文件路径
|
|
||||||
execPath, err := os.Executable()
|
execPath, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.logger.Error().Err(err).Msg("获取可执行文件路径失败")
|
a.logger.Error().Err(err).Msg("获取可执行文件路径失败")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 防御性 nil-check:确保 srv 不为 nil
|
|
||||||
if a.srv == nil {
|
if a.srv == nil {
|
||||||
a.logger.Error().Msg("热升级失败: 服务器实例为 nil")
|
a.logger.Error().Msg("热升级失败: 服务器实例为 nil")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 尝试从服务器获取监听器
|
|
||||||
listeners := a.srv.GetListeners()
|
listeners := a.srv.GetListeners()
|
||||||
if len(listeners) == 0 {
|
if len(listeners) == 0 {
|
||||||
a.logger.Error().Msg("热升级失败: 服务器未保存监听器(热升级当前未完全实现)")
|
a.logger.Error().Msg("热升级失败: 服务器未保存监听器(热升级当前未完全实现)")
|
||||||
@ -492,10 +360,8 @@ func (a *App) gracefulUpgrade() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置监听器到升级管理器
|
|
||||||
a.upgradeMgr.SetListeners(listeners)
|
a.upgradeMgr.SetListeners(listeners)
|
||||||
|
|
||||||
// 执行升级
|
|
||||||
if err := a.upgradeMgr.GracefulUpgrade(execPath); err != nil {
|
if err := a.upgradeMgr.GracefulUpgrade(execPath); err != nil {
|
||||||
a.logger.Error().Err(err).Msg("热升级失败")
|
a.logger.Error().Err(err).Msg("热升级失败")
|
||||||
return
|
return
|
||||||
@ -503,7 +369,6 @@ func (a *App) gracefulUpgrade() {
|
|||||||
|
|
||||||
a.logger.LogStartup("热升级已启动,新进程正在接管", nil)
|
a.logger.LogStartup("热升级已启动,新进程正在接管", nil)
|
||||||
|
|
||||||
// 当前进程优雅停止 - 使用配置的超时
|
|
||||||
timeout := a.cfg.Shutdown.GracefulTimeout
|
timeout := a.cfg.Shutdown.GracefulTimeout
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
timeout = 30 * time.Second
|
timeout = 30 * time.Second
|
||||||
@ -513,9 +378,8 @@ func (a *App) gracefulUpgrade() {
|
|||||||
_ = a.srv.GracefulStop(timeout)
|
_ = a.srv.GracefulStop(timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sigName 返回信号名称(用于日志输出)。
|
|
||||||
func sigName(sig syscall.Signal) string {
|
func sigName(sig syscall.Signal) string {
|
||||||
//nolint:exhaustive // 只处理应用关心的信号
|
//nolint:exhaustive // Only handling app-relevant signals.
|
||||||
switch sig {
|
switch sig {
|
||||||
case syscall.SIGTERM:
|
case syscall.SIGTERM:
|
||||||
return "SIGTERM"
|
return "SIGTERM"
|
||||||
|
|||||||
@ -1,11 +1,7 @@
|
|||||||
//go:build windows
|
//go:build windows
|
||||||
|
|
||||||
// Package app 提供 Windows 平台的应用程序逻辑 stub。
|
// Windows lacks POSIX signals (SIGUSR1, SIGUSR2, SIGHUP, SIGQUIT);
|
||||||
//
|
// this file provides stub implementations for those Unix-specific signals.
|
||||||
// Windows 不支持 POSIX 信号(SIGUSR1、SIGUSR2、SIGHUP、SIGQUIT),
|
|
||||||
// 该文件提供兼容的实现,忽略这些 Unix 特有的信号。
|
|
||||||
//
|
|
||||||
// 作者:xfy
|
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -24,102 +20,39 @@ import (
|
|||||||
"rua.plus/lolly/internal/server"
|
"rua.plus/lolly/internal/server"
|
||||||
"rua.plus/lolly/internal/stream"
|
"rua.plus/lolly/internal/stream"
|
||||||
"rua.plus/lolly/internal/variable"
|
"rua.plus/lolly/internal/variable"
|
||||||
"rua.plus/lolly/internal/version"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// App 应用程序结构(Windows 版本)。
|
// App manages the server lifecycle (Windows version).
|
||||||
type App struct {
|
type App struct {
|
||||||
cfgPath string
|
resv resolver.Resolver
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
srv *server.Server
|
srv *server.Server
|
||||||
http3Srv *http3.Server
|
http3Srv *http3.Server
|
||||||
http2Srv *http2.Server
|
http2Srv *http2.Server
|
||||||
streamSrv *stream.Server
|
streamSrv *stream.Server
|
||||||
|
logger *logging.AppLogger
|
||||||
|
cfgPath string
|
||||||
|
pidFile string
|
||||||
|
logFile string
|
||||||
|
listeners []net.Listener
|
||||||
upgradeMgr *server.UpgradeManager
|
upgradeMgr *server.UpgradeManager
|
||||||
pidFile string
|
|
||||||
logFile string
|
|
||||||
listeners []net.Listener
|
|
||||||
logger *logging.AppLogger
|
|
||||||
resv resolver.Resolver
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewApp 创建应用程序实例(Windows 版本)。
|
|
||||||
//
|
|
||||||
// 该函数初始化 App 结构体,保存配置文件路径供后续加载使用。
|
|
||||||
//
|
|
||||||
// 参数:
|
|
||||||
// - cfgPath: 配置文件路径(YAML 格式),用于加载服务器配置
|
|
||||||
//
|
|
||||||
// 返回值:
|
|
||||||
// - *App: 初始化的应用程序实例,包含配置路径等信息
|
|
||||||
func NewApp(cfgPath string) *App {
|
func NewApp(cfgPath string) *App {
|
||||||
return &App{cfgPath: cfgPath}
|
return &App{
|
||||||
|
cfgPath: cfgPath,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPidFile 设置 PID 文件路径。
|
|
||||||
func (a *App) SetPidFile(path string) {
|
func (a *App) SetPidFile(path string) {
|
||||||
a.pidFile = path
|
a.pidFile = path
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLogFile 设置日志文件路径。
|
|
||||||
func (a *App) SetLogFile(path string) {
|
func (a *App) SetLogFile(path string) {
|
||||||
a.logFile = path
|
a.logFile = path
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run 应用程序入口函数(Windows 版本)。
|
// Run starts the application: loads config, creates servers, and handles signals (Windows version).
|
||||||
//
|
|
||||||
// 根据参数决定行为:生成配置、打印版本或启动应用。
|
|
||||||
//
|
|
||||||
// 参数:
|
|
||||||
// - cfgPath: 配置文件路径
|
|
||||||
// - genConfig: 是否仅生成默认配置并退出
|
|
||||||
// - outputPath: 配置输出路径,为空时输出到 stdout
|
|
||||||
// - showVersion: 是否仅打印版本信息并退出
|
|
||||||
//
|
|
||||||
// 返回值:
|
|
||||||
// - int: 退出码,0 表示正常退出,1 表示异常
|
|
||||||
func Run(cfgPath string, genConfig bool, outputPath string, showVersion bool) int {
|
|
||||||
if genConfig {
|
|
||||||
return generateConfig(outputPath)
|
|
||||||
}
|
|
||||||
if showVersion {
|
|
||||||
printVersion()
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
app := NewApp(cfgPath)
|
|
||||||
return app.Run()
|
|
||||||
}
|
|
||||||
|
|
||||||
// generateConfig 生成默认配置文件。
|
|
||||||
func generateConfig(outputPath string) int {
|
|
||||||
cfg := config.DefaultConfig()
|
|
||||||
yamlData, err := config.GenerateConfigYAML(cfg)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "生成配置失败: %v\n", err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
if outputPath == "" {
|
|
||||||
fmt.Print(string(yamlData))
|
|
||||||
} else {
|
|
||||||
if err := os.WriteFile(outputPath, yamlData, 0o644); err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "写入文件失败: %v\n", err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
fmt.Printf("配置已写入: %s\n", outputPath)
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// printVersion 打印版本信息。
|
|
||||||
func printVersion() {
|
|
||||||
fmt.Printf("lolly version %s\n", version.Version)
|
|
||||||
fmt.Printf(" Git: %s (%s)\n", version.GitCommit, version.GitBranch)
|
|
||||||
fmt.Printf(" Built: %s\n", version.BuildTime)
|
|
||||||
fmt.Printf(" Go: %s\n", version.GoVersion)
|
|
||||||
fmt.Printf(" Platform: %s\n", version.BuildPlatform)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run 启动应用程序。
|
|
||||||
func (a *App) Run() int {
|
func (a *App) Run() int {
|
||||||
cfg, err := config.Load(a.cfgPath)
|
cfg, err := config.Load(a.cfgPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -137,7 +70,19 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a.logger.LogStartup("配置加载成功", map[string]string{"config_path": a.cfgPath})
|
a.logger.LogStartup("配置加载成功", map[string]string{"config_path": a.cfgPath})
|
||||||
a.logger.LogStartup("监听地址", map[string]string{"listen": a.cfg.Servers[0].Listen})
|
|
||||||
|
mode := a.cfg.GetMode()
|
||||||
|
if mode == config.ServerModeMultiServer {
|
||||||
|
for i, srv := range a.cfg.Servers {
|
||||||
|
a.logger.LogStartup("监听地址", map[string]string{
|
||||||
|
"index": fmt.Sprintf("[%d]", i),
|
||||||
|
"listen": srv.Listen,
|
||||||
|
"name": srv.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
a.logger.LogStartup("监听地址", map[string]string{"listen": a.cfg.Servers[0].Listen})
|
||||||
|
}
|
||||||
|
|
||||||
if a.cfg.Resolver.Enabled {
|
if a.cfg.Resolver.Enabled {
|
||||||
a.resv = resolver.New(&a.cfg.Resolver)
|
a.resv = resolver.New(&a.cfg.Resolver)
|
||||||
@ -148,11 +93,11 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a.srv = server.New(a.cfg)
|
a.srv = server.New(a.cfg)
|
||||||
|
|
||||||
if a.resv != nil {
|
if a.resv != nil {
|
||||||
a.srv.SetResolver(a.resv)
|
a.srv.SetResolver(a.resv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream 服务器
|
|
||||||
if len(a.cfg.Stream) > 0 {
|
if len(a.cfg.Stream) > 0 {
|
||||||
a.streamSrv = stream.NewServer()
|
a.streamSrv = stream.NewServer()
|
||||||
for _, sc := range a.cfg.Stream {
|
for _, sc := range a.cfg.Stream {
|
||||||
@ -163,9 +108,11 @@ func (a *App) Run() int {
|
|||||||
Weight: t.Weight,
|
Weight: t.Weight,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := a.streamSrv.AddUpstream(sc.Listen, targets, sc.Upstream.LoadBalance, stream.HealthCheckSpec{}); err != nil {
|
if err := a.streamSrv.AddUpstream(sc.Listen, targets, sc.Upstream.LoadBalance, stream.HealthCheckSpec{}); err != nil {
|
||||||
a.logger.Error().Err(err).Msg("添加 Stream 上游失败")
|
a.logger.Error().Err(err).Msg("添加 Stream 上游失败")
|
||||||
}
|
}
|
||||||
|
|
||||||
if sc.Protocol == "udp" {
|
if sc.Protocol == "udp" {
|
||||||
if err := a.streamSrv.ListenUDP(sc.Listen, sc.Listen, 60*time.Second); err != nil {
|
if err := a.streamSrv.ListenUDP(sc.Listen, sc.Listen, 60*time.Second); err != nil {
|
||||||
a.logger.Error().Err(err).Str("listen", sc.Listen).Msg("监听 UDP 失败")
|
a.logger.Error().Err(err).Str("listen", sc.Listen).Msg("监听 UDP 失败")
|
||||||
@ -176,6 +123,7 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
a.logger.LogStartup("Stream 服务器启动中", nil)
|
a.logger.LogStartup("Stream 服务器启动中", nil)
|
||||||
if err := a.streamSrv.Start(); err != nil {
|
if err := a.streamSrv.Start(); err != nil {
|
||||||
@ -184,7 +132,6 @@ func (a *App) Run() int {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP/3 服务器
|
|
||||||
if a.cfg.HTTP3.Enabled && a.cfg.Servers[0].SSL.Cert != "" {
|
if a.cfg.HTTP3.Enabled && a.cfg.Servers[0].SSL.Cert != "" {
|
||||||
tlsConfig, err := a.srv.GetTLSConfig()
|
tlsConfig, err := a.srv.GetTLSConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -204,7 +151,6 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTP/2 服务器
|
|
||||||
if a.cfg.Servers[0].SSL.HTTP2.Enabled && a.cfg.Servers[0].SSL.Cert != "" {
|
if a.cfg.Servers[0].SSL.HTTP2.Enabled && a.cfg.Servers[0].SSL.Cert != "" {
|
||||||
tlsConfig, err := a.srv.GetTLSConfig()
|
tlsConfig, err := a.srv.GetTLSConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -233,7 +179,6 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建升级管理器(Windows stub)
|
|
||||||
a.upgradeMgr = server.NewUpgradeManager(a.srv)
|
a.upgradeMgr = server.NewUpgradeManager(a.srv)
|
||||||
if a.pidFile != "" {
|
if a.pidFile != "" {
|
||||||
a.upgradeMgr.SetPidFile(a.pidFile)
|
a.upgradeMgr.SetPidFile(a.pidFile)
|
||||||
@ -274,9 +219,6 @@ func (a *App) Run() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupSignalHandlers 设置信号处理(Windows 版本)。
|
|
||||||
//
|
|
||||||
// Windows 仅支持 SIGINT 和 SIGTERM,忽略 Unix 特有的信号。
|
|
||||||
func (a *App) setupSignalHandlers(sigChan chan<- os.Signal) {
|
func (a *App) setupSignalHandlers(sigChan chan<- os.Signal) {
|
||||||
signal.Notify(sigChan,
|
signal.Notify(sigChan,
|
||||||
syscall.SIGTERM,
|
syscall.SIGTERM,
|
||||||
@ -284,16 +226,13 @@ func (a *App) setupSignalHandlers(sigChan chan<- os.Signal) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleSignal 处理信号(Windows 版本)。
|
// handleSignal returns false to indicate the app should exit (Windows version).
|
||||||
//
|
|
||||||
// Windows 仅处理 SIGTERM 和 SIGINT,其他信号忽略并继续运行。
|
|
||||||
func (a *App) handleSignal(sig os.Signal) bool {
|
func (a *App) handleSignal(sig os.Signal) bool {
|
||||||
switch sig {
|
switch sig {
|
||||||
case syscall.SIGTERM, syscall.SIGINT:
|
case syscall.SIGTERM, syscall.SIGINT:
|
||||||
// 快速停止
|
|
||||||
timeout := a.cfg.Shutdown.FastTimeout
|
timeout := a.cfg.Shutdown.FastTimeout
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
timeout = 5 * time.Second // 默认值
|
timeout = 5 * time.Second
|
||||||
}
|
}
|
||||||
a.logger.LogSignal(sigName(sig.(syscall.Signal)), "停止服务器")
|
a.logger.LogSignal(sigName(sig.(syscall.Signal)), "停止服务器")
|
||||||
a.shutdownHTTP2()
|
a.shutdownHTTP2()
|
||||||
@ -306,7 +245,6 @@ func (a *App) handleSignal(sig os.Signal) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shutdownHTTP3 关闭 HTTP/3 服务器。
|
|
||||||
func (a *App) shutdownHTTP3() {
|
func (a *App) shutdownHTTP3() {
|
||||||
if a.http3Srv != nil {
|
if a.http3Srv != nil {
|
||||||
if err := a.http3Srv.Stop(); err != nil {
|
if err := a.http3Srv.Stop(); err != nil {
|
||||||
@ -315,7 +253,6 @@ func (a *App) shutdownHTTP3() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shutdownHTTP2 关闭 HTTP/2 服务器。
|
|
||||||
func (a *App) shutdownHTTP2() {
|
func (a *App) shutdownHTTP2() {
|
||||||
if a.http2Srv != nil {
|
if a.http2Srv != nil {
|
||||||
if err := a.http2Srv.Stop(); err != nil {
|
if err := a.http2Srv.Stop(); err != nil {
|
||||||
@ -324,12 +261,10 @@ func (a *App) shutdownHTTP2() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reloadConfig 重载配置(Windows stub)。
|
|
||||||
func (a *App) reloadConfig() {
|
func (a *App) reloadConfig() {
|
||||||
// Windows stub - 功能受限
|
// Windows stub - functionality limited
|
||||||
}
|
}
|
||||||
|
|
||||||
// reopenLogs 重新打开日志文件(Windows stub)。
|
|
||||||
func (a *App) reopenLogs() {
|
func (a *App) reopenLogs() {
|
||||||
if a.cfg != nil {
|
if a.cfg != nil {
|
||||||
logging.Init(a.cfg.Logging.Error.Level, a.cfg.Logging.Format)
|
logging.Init(a.cfg.Logging.Error.Level, a.cfg.Logging.Format)
|
||||||
@ -338,14 +273,10 @@ func (a *App) reopenLogs() {
|
|||||||
a.logger.LogStartup("日志已重新打开", nil)
|
a.logger.LogStartup("日志已重新打开", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// gracefulUpgrade 执行热升级(Windows stub)。
|
|
||||||
//
|
|
||||||
// Windows 不支持热升级,此方法为空实现。
|
|
||||||
func (a *App) gracefulUpgrade() {
|
func (a *App) gracefulUpgrade() {
|
||||||
a.logger.Info().Msg("Windows 不支持热升级")
|
a.logger.Info().Msg("Windows 不支持热升级")
|
||||||
}
|
}
|
||||||
|
|
||||||
// sigName 返回信号名称(Windows 版本)。
|
|
||||||
func sigName(sig syscall.Signal) string {
|
func sigName(sig syscall.Signal) string {
|
||||||
switch sig {
|
switch sig {
|
||||||
case syscall.SIGTERM:
|
case syscall.SIGTERM:
|
||||||
@ -355,4 +286,4 @@ func sigName(sig syscall.Signal) string {
|
|||||||
default:
|
default:
|
||||||
return fmt.Sprintf("Signal(%d)", sig)
|
return fmt.Sprintf("Signal(%d)", sig)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
112
internal/app/import.go
Normal file
112
internal/app/import.go
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package app
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"rua.plus/lolly/internal/config"
|
||||||
|
"rua.plus/lolly/internal/converter/nginx"
|
||||||
|
"rua.plus/lolly/internal/version"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run decides behavior based on flags: generate config, import nginx config, print version, or start the app.
|
||||||
|
func Run(cfgPath string, genConfig bool, outputPath string, importPath string, showVersion bool) int {
|
||||||
|
if genConfig && importPath != "" {
|
||||||
|
fmt.Fprintln(os.Stderr, "error: --generate-config and --import are mutually exclusive")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if outputPath != "" && !genConfig && importPath == "" {
|
||||||
|
fmt.Fprintln(os.Stderr, "error: -o requires either --generate-config or --import")
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if genConfig {
|
||||||
|
return generateConfig(outputPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if importPath != "" {
|
||||||
|
if err := importNginxConfig(importPath, outputPath); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "error:", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if showVersion {
|
||||||
|
printVersion()
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
app := NewApp(cfgPath)
|
||||||
|
return app.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateConfig(outputPath string) int {
|
||||||
|
cfg := config.DefaultConfig()
|
||||||
|
yamlData, err := config.GenerateConfigYAML(cfg)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "生成配置失败: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if outputPath == "" {
|
||||||
|
fmt.Print(string(yamlData))
|
||||||
|
} else {
|
||||||
|
if err := os.WriteFile(outputPath, yamlData, 0o644); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "写入文件失败: %v\n", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
fmt.Printf("配置已写入: %s\n", outputPath)
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func importNginxConfig(path, outputPath string) error {
|
||||||
|
nginxCfg, err := nginx.ParseFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("解析 nginx 配置失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := nginx.Convert(nginxCfg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("转换配置失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, w := range result.Warnings {
|
||||||
|
fmt.Fprintf(os.Stderr, "warning: %s:line %d: %s\n", w.File, w.Line, w.Message)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := config.Validate(result.Config); err != nil {
|
||||||
|
return fmt.Errorf("转换后配置验证失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
yamlData, err := yaml.Marshal(result.Config)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("序列化 YAML 失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if outputPath == "" {
|
||||||
|
os.Stdout.Write(yamlData)
|
||||||
|
} else {
|
||||||
|
if err := os.MkdirAll(filepath.Dir(outputPath), 0o755); err != nil {
|
||||||
|
return fmt.Errorf("创建输出目录失败: %w", err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(outputPath, yamlData, 0o644); err != nil {
|
||||||
|
return fmt.Errorf("写入文件失败: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("配置已写入: %s\n", outputPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func printVersion() {
|
||||||
|
fmt.Printf("lolly version %s\n", version.Version)
|
||||||
|
fmt.Printf(" Git: %s (%s)\n", version.GitCommit, version.GitBranch)
|
||||||
|
fmt.Printf(" Built: %s\n", version.BuildTime)
|
||||||
|
fmt.Printf(" Go: %s\n", version.GoVersion)
|
||||||
|
fmt.Printf(" Platform: %s\n", version.BuildPlatform)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user