前言
報了個駕校,時隔兩個多月沒發文章了,駕考上週終於都結束了,這之後得補補前兩月的文章了。之前定了個目標,讀完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的生命週期
訪問圖片源地址查看大圖 http://cdn.tigerb.cn/20190628...
上圖是我在讀iris代碼時,整理的iris框架的一個生命週期流程圖,內容比較多。總的來說劃分爲四個大的部分:
橙色部分
初始化iris.Application:
- 創建iris.Application
- 創建APIBuilder(app.Get()等方法的路由都是註冊到這裏)
- 創建Router(每個http請求都是通過router處理的)
藍色部分
註冊路由到app.APIBuilder
紫色部分
初始化一個http.Server
綠色部分
構建路由handler&啓動http server:
- 註冊
app.APIBuilder
到app.Router.routesProvider
- 註冊
app.APIBuilder.routes
的路由到app.Router.requestHandler
- 啓動http server
關鍵代碼解析
- 創建一個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...)
}
- 關於
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
- 關於
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,
}
- 構建請求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)
- 啓動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框架解析》系列文章鏈接如下: