golang框架解析-iris

前言

報了個駕校,時隔兩個多月沒發文章了,駕考上週終於都結束了,這之後得補補前兩月的文章了。之前定了個目標,讀完beego、iris、gin等go框架的源碼,之前已經發過一篇過於beego的文章golang框架解析-beego,今天帶來的是go框架iris的解析,主要講解iris框架的一個生命週期過程。

在讀這篇文章之前,如果沒看過golang框架解析-beego的可以先去看看,因爲golang框架解析-beego有講關於go如何啓動一個http server,這個知識點對理解本篇文章有很大的幫助。

安裝

使用glide安裝:

glide get github.com/kataras/iris
glide get github.com/kataras/golog

啓動一個簡單的iris http服務:

//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"))
}

iris的生命週期

20190628234814.png

訪問圖片源地址查看大圖 http://cdn.tigerb.cn/20190628...

上圖是我在讀iris代碼時,整理的iris框架的一個生命週期流程圖,內容比較多。總的來說劃分爲四個大的部分:

橙色部分

初始化iris.Application:

  • 創建iris.Application
  • 創建APIBuilder(app.Get()等方法的路由都是註冊到這裏)
  • 創建Router(每個http請求都是通過router處理的)

藍色部分

註冊路由到app.APIBuilder

紫色部分

初始化一個http.Server

綠色部分

http://cdn.tigerb.cn/20190629172558.webp

構建路由handler&啓動http server:

  • 註冊app.APIBuilderapp.Router.routesProvider
  • 註冊app.APIBuilder.routes的路由到app.Router.requestHandler
  • 啓動http server

關鍵代碼解析

  1. 創建一個iris Application
// 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...)
}
  1. 關於router.NewAPIBuilder()

APIBuilder的routes屬性很關鍵,最終的我們定義的路由都是註冊到了這裏。

// APIBuilder
api := &APIBuilder{
    macros:            macro.Defaults,
    errorCodeHandlers: defaultErrorCodeHandlers(),
    reporter:          errors.NewReporter(),
    relativePath:      "/",
    // 最終的我們定義的路由都是註冊到了這裏
    routes:            new(repository),
}

// repository的結構
type repository struct {
    routes []*Route
}

結論:用戶路由註冊到了app.APIBuilder.routes

  1. 關於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

  1. 註冊路由

這裏很簡單了就是註冊用戶路由到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,
}
  1. 構建請求handler
//啓動路由
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)
  1. 啓動HTTP Server

最後我們就是啓動這個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)

結語

最後我們再簡單的回顧下上面的流程:

20190629151818.png

《golang框架解析》系列文章鏈接如下:

clipboard.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章