从 import beego 到端口可服务的完整链路:包 init → 默认应用构建 → Run 执行。
1. 两个阶段
css
阶段一:包初始化(import 即发生)
web 包 init → NewHttpSever() → BeeApp 就绪
config 包 init → 读 conf/app.conf → BConfig 就绪
阶段二:web.Run()(main 里调用)
initBeforeHTTPRun(Once) → 6 个默认钩子
initAddr → Handlers.Init() 组装 FilterChain
组装 http.Server → 监听 → Serve
2. 阶段一:包 init 链
2.1 BeeApp 的诞生(server.go:42-47)
scss
var BeeApp *HttpServer
func init() {
BeeApp = NewHttpSever() // 注意:官方拼写少了 r
}
NewHttpSever()(server.go:60)→ NewHttpServerWithCfg(BConfig)(server.go:65-74):
go
func NewHttpServerWithCfg(cfg *Config) *HttpServer {
cr := NewControllerRegisterWithCfg(cfg) // 路由注册器
app := &HttpServer{
Handlers: cr,
Server: &http.Server{}, // 空 server,Run 时填
Cfg: cfg, // 指针共享!
}
return app
}
关键细节 :Cfg 是 *Config 指针共享------之后修改 web.BConfig 会影响 BeeApp(以及用 BConfig 创建的所有 server)。
2.2 BConfig 的加载(config.go:458-483)
web 包 init 时:
- 定位配置文件:
conf/app.conf;若环境变量BEEGO_RUNMODE有值则找conf/<mode>.app.conf;
parseConfig读文件 →assignConfig:
-
- 先
parseConfigForV1兼容 beego 1.x 的扁平 key;
- 先
-
ac.Unmarshaler("", BConfig)把 map 灌进 Config 结构体;
-
- 按
Log.Outputs初始化日志输出;
- 按
beegoAppConfig包装(754-872):读取时优先RunMode::key段------这就是 Day05 分环境配置的机制。
2.3 其他包的 init(被 import 时)
exceptMethod收集(router.go:107-116):反射列出 Controller 基类全部方法名,AutoRouter 时排除;
- mime 表、HTTPMETHOD 表初始化;
- core/logs:全局
beeLogger = NewLogger()(预置 console)。
3. 阶段二:web.Run 全流程
包级 beego.Run(params...)(beego.go:50-56)只是代理 BeeApp.Run(...)。
HttpServer.Run(addr, mws...)(server.go:87-305)按序:
3.1 initBeforeHTTPRun(beego.go:63-85,sync.Once)
注册并执行 6 个默认钩子(hooks.go:69-76):
bash
registerMime # 扩展名→Content-Type 表
registerDefaultErrorHandler # 401/403/404/405/500/501/502/503/504/417/422/413 → ErrorMaps
registerSession # sessionon=true 时创建 GlobalSessions 并 go GC()
registerTemplate # AddViewPath(BConfig.WebConfig.ViewsPath)
registerAdmin # enableadmin=true 时起 8088 管理服务
registerGzip # InitGzip
外加用户 AddAPPStartHook 注册的钩子(按注册顺序)。
sync.Once 保证重复调用 Run(测试场景)不会重复注册。
3.2 地址与路由初始化
scss
app.initAddr(addr) // server.go:849-858:"host:port" 拆进 Cfg.Listen
app.Handlers.Init() // router.go:201-212:组装 FilterChain 链(见 S07)
3.3 组装 http.Server(server.go:143-149)
ini
app.Server.Handler = app.Handlers // ControllerRegister 实现 http.Handler
// 倒序包裹中间件:app.Run(":8080", mw1, mw2) 时 mw2(mw1(handler))
for i := len(mws) - 1; i >= 0; i-- {
app.Server.Handler = mws[i](app.Server.Handler)
}
app.Server.ReadTimeout = time.Duration(cfg.Listen.ServerTimeOut) * time.Second
app.Server.WriteTimeout = ...
app.Server.ErrorLog = logs.GetLogger("HTTP")
3.4 三种启动分支
A. FCGI (server.go:107-141):EnableStdIo → fcgi.Serve(os.Stdin/Stdout);否则监听 tcp/unix 后 fcgi.Serve。
B. Graceful (server.go:155-236,Listen.Graceful=true):
go
endRunning := make(chan bool, 1)
// HTTPS:grace.NewServer(...).ListenTLS / ListenMutualTLS / AutoTLS
// HTTP: ServeWithListener
// 每个 LifeCycleCallback 包成 grace.WithShutdownCallback
// 全部在 goroutine 中,主协程 <-endRunning 阻塞
C. 普通模式(server.go:239-304):
css
// HTTPS(可选):goroutine + ListenAndServeTLS(AutoTLS/MutualHTTPS 处理证书)
// HTTP:goroutine + ListenAndServe(ListenTCP4 强制时 net.Listen("tcp4")+Serve)
// 主协程 <-endRunning 阻塞,监听协程出错时写入
4. 中间件(MiddleWare)
go
type MiddleWare func(http.Handler) http.Handler // server.go:77
web.RunWithMiddleWares(":8080",
func(h http.Handler) http.Handler {
return myStdMiddleware(h) // 任何标准库中间件!
},
)
与 beego Filter 的区别:MiddleWare 在标准库层 包裹(最外层),Filter 在 beego 体系内(能拿 Context)。启动时的包裹顺序:先注册的在最外层。
5. 生命周期回调
scss
type LifeCycleCallback interface {
AfterStart(app *HttpServer)
BeforeShutdown(app *HttpServer)
}
web.BeeApp.LifeCycleCallbacks = append(...)
grace 模式下关闭前逐个调用 BeforeShutdown。
6. 启动时序图(汇总)
css
import web
└─ init: BeeApp=NewHttpSever() → NewControllerRegisterWithCfg
BConfig ← conf/app.conf(parseConfig/assignConfig/beegoAppConfig)
main: web.Run()
├─ initBeforeHTTPRun [Once]
│ ├─ 用户 hooks(AddAPPStartHook)
│ └─ mime → errorHandlers → session(+go GC) → template → admin → gzip
├─ initAddr / Handlers.Init()(FilterChain 组装)
├─ Server.Handler = Handlers;倒序包 MiddleWare;超时/ErrorLog
└─ 分支:fcgi | grace | 普通(groutine ListenAndServe)
└─ 主协程阻塞 ←endRunning
7. 值得学的实现细节
- sync.Once 防重入:initBeforeHTTPRun、模板初始化等多处使用。
- 指针共享配置:NewHttpServerWithCfg 不复制 Config,保证 BConfig 修改全局生效(也埋了"误改影响所有 server"的坑)。
- goroutine + channel 结束信号 :
endRunning缓冲 1,错误非阻塞写入。
- 默认值集中 :
newBConfig()(config.go:523-603)一处定义全部默认,便于审计。