beego启动流程

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 时:

  1. 定位配置文件:conf/app.conf;若环境变量 BEEGO_RUNMODE 有值则找 conf/<mode>.app.conf;
  1. parseConfig 读文件 → assignConfig:
    • parseConfigForV1 兼容 beego 1.x 的扁平 key;
    • ac.Unmarshaler("", BConfig) 把 map 灌进 Config 结构体;
    • Log.Outputs 初始化日志输出;
  1. 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. 值得学的实现细节

  1. sync.Once 防重入:initBeforeHTTPRun、模板初始化等多处使用。
  1. 指针共享配置:NewHttpServerWithCfg 不复制 Config,保证 BConfig 修改全局生效(也埋了"误改影响所有 server"的坑)。
  1. goroutine + channel 结束信号 :endRunning 缓冲 1,错误非阻塞写入。
  1. 默认值集中 :newBConfig()(config.go:523-603)一处定义全部默认,便于审计。
相关推荐
fliter19 分钟前
Go Map 详解:键值对实际上是如何存储的
后端
万物智能19 分钟前
设备树DTS-【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
后端·算法
CadeCode22 分钟前
Oracle 逗号拼接字段处理
数据库·后端·性能优化
geovindu34 分钟前
CSharp: State Pattern
开发语言·后端·c#·.net·状态模式·行为模式
山岚的运维笔记37 分钟前
mysql 专业笔记 -- 第 1 章:MySQL 入门
运维·数据库·笔记·后端·学习·mysql·dba
fliter1 小时前
我们如何通过优化 1.1.1.1 的 DNS 缓存节省 100 TB 内存
后端
一开1 小时前
一个自己开发的 Agent Harness-子 Agent篇
后端
KoPa1 小时前
HeySmart:事件总线——异步解耦的艺术
前端·后端
吃饱了得干活1 小时前
MySQL 内核剖析:ACID 实现、引擎对决、B+树、索引与主从复制闭环
后端·mysql