golang框架解析-iris

前言

報了個駕校,時隔兩個多月沒發文章了,駕考上週終於都結束了,這以後得補補前兩月的文章了。以前定了個目標,讀完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服務:api

//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的生命週期

訪問圖片源地址查看大圖 cdn.tigerb.cn/20190628234814.pngbash

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

橙色部分

初始化iris.Application:框架

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

藍色部分

註冊路由到app.APIBuilderdom

紫色部分

初始化一個http.Serveride

綠色部分

構建路由handler&啓動http server:ui

  • 註冊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)
複製代碼

結語

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

《golang框架解析》系列文章連接以下:

相關文章
相關標籤/搜索