使用 fasthttp 替代 net/http,实现 Phase 2 核心模块: - HTTP 服务器:fasthttp.Server 配置超时和连接限制 - 路由系统:fasthttp/router 基于 radix tree 匹配 - 静态文件服务:安全检查、索引文件支持 - 日志系统:zerolog 结构化日志 - 中间件框架:链式组合接口 - 虚拟主机管理:按 Host 头选择处理器 Co-Authored-By: Claude <noreply@anthropic.com>
140 lines
3.2 KiB
Go
140 lines
3.2 KiB
Go
// Package app 提供应用程序的启动和运行逻辑。
|
||
package app
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"os/signal"
|
||
"syscall"
|
||
"time"
|
||
|
||
"rua.plus/lolly/internal/config"
|
||
"rua.plus/lolly/internal/server"
|
||
)
|
||
|
||
// 版本信息,通过 -ldflags 注入。
|
||
var (
|
||
Version = "dev"
|
||
GitCommit = "unknown"
|
||
GitBranch = "unknown"
|
||
BuildTime = "unknown"
|
||
GoVersion = "unknown"
|
||
BuildPlatform = "unknown"
|
||
)
|
||
|
||
// 应用状态。
|
||
var (
|
||
shutdownTimeout = 30 * time.Second // 优雅停止超时时间
|
||
)
|
||
|
||
// Run 应用程序入口。
|
||
func Run(cfgPath string, genConfig bool, outputPath string, showVersion bool) int {
|
||
if genConfig {
|
||
return generateConfig(outputPath)
|
||
}
|
||
|
||
if showVersion {
|
||
printVersion()
|
||
return 0
|
||
}
|
||
|
||
return startServer(cfgPath)
|
||
}
|
||
|
||
// 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, 0644); 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)
|
||
fmt.Printf(" Git: %s (%s)\n", GitCommit, GitBranch)
|
||
fmt.Printf(" Built: %s\n", BuildTime)
|
||
fmt.Printf(" Go: %s\n", GoVersion)
|
||
fmt.Printf(" Platform: %s\n", BuildPlatform)
|
||
}
|
||
|
||
// startServer 启动服务器。
|
||
func startServer(cfgPath string) int {
|
||
cfg, err := config.Load(cfgPath)
|
||
if err != nil {
|
||
fmt.Fprintf(os.Stderr, "加载配置失败: %v\n", err)
|
||
return 1
|
||
}
|
||
|
||
fmt.Printf("配置加载成功: %s\n", cfgPath)
|
||
fmt.Printf("监听地址: %s\n", cfg.Server.Listen)
|
||
|
||
// 创建服务器
|
||
srv := server.New(cfg)
|
||
|
||
// 启动信号监听
|
||
sigChan := make(chan os.Signal, 1)
|
||
signal.Notify(sigChan,
|
||
syscall.SIGTERM, // 快速停止(kill 或 systemd stop)
|
||
syscall.SIGINT, // 快速停止(Ctrl+C)
|
||
syscall.SIGQUIT, // 优雅停止
|
||
)
|
||
|
||
// 启动服务器(在 goroutine 中)
|
||
errChan := make(chan error, 1)
|
||
go func() {
|
||
fmt.Println("服务器启动中...")
|
||
if err := srv.Start(); err != nil {
|
||
errChan <- err
|
||
}
|
||
}()
|
||
|
||
// 等待信号或启动错误
|
||
select {
|
||
case err := <-errChan:
|
||
fmt.Fprintf(os.Stderr, "服务器启动失败: %v\n", err)
|
||
return 1
|
||
case sig := <-sigChan:
|
||
// 根据信号类型决定停止方式
|
||
switch sig {
|
||
case syscall.SIGQUIT:
|
||
// 优雅停止:等待请求完成
|
||
fmt.Printf("\n收到 SIGQUIT,优雅停止(等待 %v)...\n", shutdownTimeout)
|
||
srv.GracefulStop(shutdownTimeout)
|
||
case syscall.SIGTERM, syscall.SIGINT:
|
||
// 快速停止
|
||
fmt.Printf("\n收到 %v,停止服务器...\n", sigName(sig.(syscall.Signal)))
|
||
srv.Stop()
|
||
}
|
||
}
|
||
|
||
fmt.Println("服务器已停止")
|
||
return 0
|
||
}
|
||
|
||
// sigName 返回信号名称(用于日志输出)。
|
||
func sigName(sig syscall.Signal) string {
|
||
switch sig {
|
||
case syscall.SIGTERM:
|
||
return "SIGTERM"
|
||
case syscall.SIGINT:
|
||
return "SIGINT"
|
||
case syscall.SIGQUIT:
|
||
return "SIGQUIT"
|
||
default:
|
||
return fmt.Sprintf("Signal(%d)", sig)
|
||
}
|
||
} |