報了個駕校,時隔兩個多月沒發文章了,駕考上週終於都結束了,這以後得補補前兩月的文章了。以前定了個目標,讀完beego、iris、gin等go框架的源碼,以前已經發過一篇過於beego的文章golang框架解析-beego,今天帶來的是go框架iris的解析,主要講解iris框架的一個生命週期過程。git
在讀這篇文章以前,若是沒看過golang框架解析-beego的能夠先去看看,由於golang框架解析-beego有講關於go如何啓動一個http server,這個知識點對理解本篇文章有很大的幫助。github
使用glide安裝:golang
glide get github.com/kataras/iris glide get github.com/kataras/golog
啓動一個簡單的iris http服務:web
//main.go package main import "github.com/kataras/iris" func main() { app := iris.Default() app.Get("/ping", func(ctx iris.Context) { ctx.JSON(iris.Map{ "message": "pong", }) }) app.Run(iris.Addr(":8888")) }
訪問圖片源地址查看大圖 http://cdn.tigerb.cn/20190628...
上圖是我在讀iris代碼時,整理的iris框架的一個生命週期流程圖,內容比較多。總的來講劃分爲四個大的部分:api
初始化iris.Application:app
註冊路由到app.APIBuilder框架
初始化一個http.Serverdom
構建路由handler&啓動http server:ide
app.APIBuilder
到app.Router.routesProvider
app.APIBuilder.routes
的路由到app.Router.requestHandler
// Application 首先看看咱們的iris Application結構體組成 type Application struct { // 咱們的路由都註冊到了 APIBuilder *router.APIBuilder // *router.Router 實現了ServeHTTP方法 而且最終賦值給了&http.server{}.Handler *router.Router // 請求上下文池子 ContextPool *context.Pool // 配置項 config *Configuration // 日誌 logger *golog.Logger // 視圖 view view.View // 執行一次的once once sync.Once // 互斥鎖 mu sync.Mutex Hosts []*host.Supervisor hostConfigurators []host.Configurator } // 建立了一個iris應用實例 // 爲何不直接New呢? // 由於Default裏面註冊了兩個handle // 1. recover panic的方法, // 2. 請求日誌 app := iris.Default() func Default() *Application { app := New() // 合成複用*APIBuilder的Use app.Use(recover.New()) // 合成複用*APIBuilder的Use app.Use(requestLogger.New()) return app } // app := New() 獲得的結構體 app := &Application{ config: &config, logger: golog.Default, // 很關鍵:咱們的路由都註冊到了 APIBuilder APIBuilder: router.NewAPIBuilder(), // 很關鍵:*router.Router 實現了ServeHTTP方法 而且最終賦值給了&http.server{}.Handler Router: router.NewRouter(), } // 註冊api請求的中間件 func (api *APIBuilder) Use(handlers ...context.Handler) { api.middleware = append(api.middleware, handlers...) }
router.NewAPIBuilder()
APIBuilder的routes屬性很關鍵,最終的咱們定義的路由都是註冊到了這裏。ui
// APIBuilder api := &APIBuilder{ macros: macro.Defaults, errorCodeHandlers: defaultErrorCodeHandlers(), reporter: errors.NewReporter(), relativePath: "/", // 最終的咱們定義的路由都是註冊到了這裏 routes: new(repository), } // repository的結構 type repository struct { routes []*Route }
結論:用戶路由註冊到了app.APIBuilder.routes
router.NewRouter()
router.NewRouter()
返回的是一個&Router{}
指針,&Router{}
有三個很關鍵的屬性和一個ServeHTTP
成員方法。
三個關鍵的屬性:
mainHandler http.HandlerFunc
requestHandler RequestHandler
routesProvider RoutesProvider
咱們再當作員方法ServeHTTP
實現了ServeHTTP(w http.ResponseWriter, r *http.Request)
方法,就是accept請求以後就會執行這個方法,咱們看看具體方法內容。
// implement ServeHTTP func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { // 因此這裏能夠看出accept請求以後會執行mainHandler router.mainHandler(w, r) }
func NewRouter() *Router { return &Router{} } type Router struct { mu sync.Mutex requestHandler RequestHandler // 每次http請求都會執行mainHandler mainHandler http.HandlerFunc wrapperFunc func(http.ResponseWriter, *http.Request, http.HandlerFunc) cPool *context.Pool r routesProvider RoutesProvider } // implement ServeHTTP func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { // 每次http請求都會執行mainHandler router.mainHandler(w, r) }
結論:每次http請求都會執行mainHandler
這裏很簡單了就是註冊用戶路由到app.APIBuilder.routes
//router func (api *APIBuilder) Get(relativePath string, handlers ...context.Handler) *Route { return api.Handle(http.MethodGet, relativePath, handlers...) } route := &Route{ Name: defaultName, Method: method, methodBckp: method, Subdomain: subdomain, tmpl: tmpl, Path: path, Handlers: handlers, MainHandlerName: mainHandlerName, FormattedPath: formattedPath, }
//啓動路由 app.Run() ⬇️ // 構建 app.Build() ⬇️ // 構建路由 app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder, false) ⬇️ // 構建請求Handler // 把app.APIBuilder註冊的api註冊到了requestHandler裏 // 由於咱們在下面發現請求都是從router.requestHandler去處理的 requestHandler.Build(routesProvider) ⬇️ // 賦值 router.requestHandler = requestHandler router.routesProvider = routesProvider ⬇️ // the important 沒錯很重要的地方mainHandler被賦值的地方 // 也就是accpet請求實際執行的代碼 // 真相就在這 // the important router.mainHandler = func(w http.ResponseWriter, r *http.Request) { // 構建請求上下文 ctx := cPool.Acquire(w, r) // 處理請求 router.requestHandler.HandleRequest(ctx) // 釋放請求上下文 cPool.Release(ctx) } ⬇️ // 實際處理請求餓地方 // 路由的匹配就是這裏了 func (h *routerHandler) HandleRequest(ctx context.Context)
最後咱們就是啓動這個http server了,這裏和絕大多數golang的http服務啓動基本一致。
// 賦值http服務的ip+port iris.Addr(":8888") ⬇️ //建立http.Server並啓動服務的匿名方法 func Addr(addr string, hostConfigs ...host.Configurator) Runner { return func(app *Application) error { return app.NewHost(&http.Server{Addr: addr}). Configure(hostConfigs...). ListenAndServe() } } ⬇️ // app.NewHost(&http.Server{Addr: addr}) // 就是這裏賦值app.Router給http.Server的Handler的 if srv.Handler == nil { srv.Handler = app.Router } ⬇️ // 啓動服務 su.Server.Serve(l) ⬇️ // accept請求 l.Accept() ⬇️ // 啓動一個goroutine處理請求 go c.serve(ctx) ⬇️ // 最終至此真相都大白了 serverHandler{c.server}.ServeHTTP(w, w.req)
最後咱們再簡單的回顧下上面的流程:
《golang框架解析》系列文章連接以下: