请求执行全流程(serveHttp 走读) 1. 入口:ServeHTTP(router.go:995-1003)func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) { ctx : p.GetContext() // sync.Pool 取 defer p.GiveBackContext(ctx) // 归还池 ctx.Reset(rw, r) // 复用对象必须重置全部状态 p.chainRoot.filter(ctx, p.getUrlPath(ctx), nil) // 进入洋葱链 }三个前置知识:Context 对象池(GetContext/GiveBackContext:358-365):高并发下避免每请求分配 Context/Input/Output 大对象;ctx.Reset(context.go:183-192)清空 Session、Params、RequestBody 等所有状态——忘记 Reset 某字段 跨请求数据泄漏,这是改 context 包时最容易出的 bug。chainRoot.filter:洋葱链的最外层;链的最内层是serveHttp(S07 详析链的组装)。getUrlPath(1313-1319):RouterCaseSensitivefalse 时转小写。2. serveHttp 17 步(router.go:1005-1311)步骤 1-2:计时与 panic 防护(1005-1024)startTime : time.Now() defer func() { p.cfg.RecoverFunc(ctx, p.cfg) // 默认 defaultRecoverPanic(config.go:485-521) }()defaultRecoverPanic:recover 后打印堆栈(dev 带彩页)、查 ErrorMaps 对应错误码渲染、写 500。Abort/CustomAbort 也依赖这条 defer 链(它们 panic 一个特殊值)。步骤 3:基础设置(1026-1030)ctx.Output.EnableGzip cfg.EnableGzip if RunMode DEV { ctx.Output.Header(Server, ServerName) }步骤 4:method 校验(1032-1038)HTTP method 不在 HTTPMETHOD 表 →exception(405)→ goto Admin(跳到收尾段)。goto Admin:beego 用 goto 把中途失败统一汇到步骤 17 的收尾逻辑(日志统计),不重复代码。阅读时把Admin:标签当finally理解。步骤 5:BeforeStatic 过滤器 静态文件(1041-1050)if p.runFilters(ctx, urlPath, BeforeStatic) { goto Admin } serverStaticRouter(ctx) // fs.go:处理 StaticDir 映射 if ctx.ResponseWriter.Started { goto Admin } // 静态命中则结束静态文件细节(fs.go/staticfile.go):按前缀映射磁盘路径;≤100KB 文件走 LRU 内存缓存(StaticCacheFileSize/Num 配置);带过期头与 gzip。步骤 6:请求体处理(1052-1086)非 GET/HEAD:有文件上传(FORM header 含 multipart):http.MaxBytesReader(MaxUploadSize)包一层CopyRequestBodytrue:body 超 MaxMemory → 413;否则Input.CopyBody读入 RequestBodyInput.ParseFormOrMultiForm失败 → 413/500步骤 7:Session 初始化(1088-1106)// 先预演路由,看该路由是否 sessionOn(路由级开关) if err : p.FindRouter(ctx); err nil { if !ctx.Input.RunController ... sessionOn routerInfo.sessionOn ... } if sessionOn GlobalSessions ! nil { GlobalSessions.SessionStart(w, r) // 写入 ctx.Input.CruSession defer GlobalSessions.SessionRelease(ctx, w) // 请求结束持久化 }注意顺序:SessionStart 在 BeforeRouter 之前——所以过滤器里能直接用 CruSession(修正使用篇 Day08 的可能未初始化:实际在这已初始化,只要全局 sessionontrue)。步骤 8:BeforeRouter 过滤器(1107-1109)runFilters返回 true(已短路)→ goto Admin。改写路由的窗口:过滤器可设置ctx.Input.RunController/RunMethod。步骤 9:路由匹配(1111-1117)if ctx.Input.RunController nil { // 过滤器没改写过才走正常匹配 if err : p.FindRouter(ctx); err ! nil { exception(404) // → ErrorMaps/自定义错误页 goto Admin } }FindRouter(1336-1349):p.routers[method].Match(urlPath),结果 routerInfo 存入 ctx.Input.Data。步骤 10::splat 展开(1120-1123)*匹配到的多段按:splat0,:splat1…追加参数(树匹配时只写了:splat)。步骤 11:RouterPattern 注入(1130-1133)ctx.Input.SetData(RouterPattern, routerInfo.pattern)——控制器/过滤器可拿到实际命中的 pattern(监控、日志用)。步骤 12:BeforeExec 过滤器(1136-1138)步骤 13:policy 执行(1141-1143)execPolicy(policy.go:80-97):按 method 匹配 policies 树,顺序执行;任一 policy 使响应 Started → 短路。步骤 14:按 routerType 分派(1145-1175)switch routerInfo.routerType { case routerTypeRESTFul: // 函数路由 if m, ok : routerInfo.methods[runMethod]; ok { routerInfo.runFunction(ctx) } else { exception(405) } case routerTypeHandler: // http.Handler routerInfo.handler.ServeHTTP(ctx.ResponseWriter, ctx.Request) case routerTypeBeego: // 控制器路由 runRouter routerInfo.controllerType // POST 伪装:body/query 有 _methodPUT 时改写 runMethod(1161-1166) // 取 runMethod 顺序:methods[method] → methods[*] → method 本身 }步骤 15:控制器执行(1178-1254)步骤 16:AfterExec / FinishRouter 过滤器(1257-1263)步骤 17:Admin 收尾段(1265-1310)Admin: // 访问日志(AccessLogs 开启) p.LogAccess(ctx, startTime) // server.go:860-893 ctx.ResponseWriter.Elapsed time.Since(startTime) // 统计(EnableAdmin 且 FilterMonitorFunc 命中) if EnableAdmin { StatisticsMap.AddStatistics(...) } // QPS/耗时 // dev 调试输出(彩色行) // 最后:若 Output.Status ! 0 且未写过 → WriteHeader3. 完整时序(背诵版)① recover defer ② gzip/Server 头 ③ method 校验 ④ BeforeStatic → 静态文件 →(命中即止) ⑤ body/上传解析 ⑥ [路由级 session 预判] SessionStart defer Release ⑦ BeforeRouter(可改路由) ⑧ FindRouter(或用过滤器改写的控制器)→ 404 检查 ⑨ :splat 展开 / RouterPattern 注入 ⑩ BeforeExec ⑪ policy ⑫ 三类路由分派(beego/restful/handler) ⑬ 控制器:Init→Prepare→XSRF→URLMapping→Method→Render→Finish ⑭ AfterExec → FinishRouter ⑮ Admin:访问日志/QPS统计/补状态码4. 短路机制汇总(面试级)短路点触发条件后果过滤器返回Startedfilter 内有输出且 returnOnOutput跳过后续所有 filter控制器,直达 Admin 段exception(40x/50x)method 非法/路由未命中panic→RecoverFunc→错误页→Adminc.Abort(code,body)控制器主动panic→RecoverFunc 拦截→写响应静态文件 Started静态命中跳过路由直接 Adminpolicy Startedpolicy 输出跳过控制器5. 并发与性能要点每请求一个池化 Context:零分配热路径(除业务对象)。ResponseWriter 包装层(context.Response)记录Started/Status/Elapsed:防重复 WriteHeader(357-365)、供过滤器判断短路、监控计时——一个包装类解决三件事。控制器反射调用(MethodByName)有开销,但被方法名 switch 直接调 Get/Post的 fast path 缓解(只有自定义方法名才反射)。XSRF/Session/统计等所有可选功能都是配置开关同一段代码内 if,没有函数指针跳表——直白但高效。