beego源碼分析(一)——初探

初探

前言

我在http.ListenAndServe相關源碼簡析時最後的兩條總結:

1.http.HandleFunc將pattern及我們自定義的handler存儲在DefaultServeMux的一個map中。

2.當http.ListenAndServe的handler爲nil時,系統會從DefaultServeMux存儲信息的map中匹配pattern獲取對應的handler,進而處連接請求。

如果用更簡單的話來總結以上兩條,就是:路由的存儲與匹配

beego作爲一個基於golang之上的框架(此處只討論核心http及路由部分的處理),必然脫離不了語言底層的支持。因此,我們可以大膽猜測,beego的路由處理主要是在HandleFunc的封裝上,即如何更好的封裝HandlerFunc且便於使用,這應該也是beego支持多種路由方式的特點了。

一、beego.Run

我們先從源碼來驗證一條簡單的猜測,beego基於http.ListenAndServe來處理網絡請求(本次僅針對http協議的處理進行分析,https協議類似)。

beego.Run()->BeeApp.Run()

// Run beego application.
func (app *App) Run() {
    ...
    app.Server.Handler = app.Handlers
    app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
    app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
    app.Server.ErrorLog = logs.GetLogger("HTTP")

    // run graceful mode
    if BConfig.Listen.Graceful {
        httpsAddr := BConfig.Listen.HTTPSAddr
        app.Server.Addr = httpsAddr
        if BConfig.Listen.EnableHTTPS {
            go func() {
                ...
                if err := server.ListenAndServeTLS(BConfig.Listen.HTTPSCertFile, BConfig.Listen.HTTPSKeyFile); err != nil {
                    logs.Critical("ListenAndServeTLS: ", err, fmt.Sprintf("%d", os.Getpid()))
                    time.Sleep(100 * time.Microsecond)
                    endRunning <- true
                }
            }()
        }
        if BConfig.Listen.EnableHTTP {
            go func() {
                ...
                if err := server.ListenAndServe(); err != nil {
                    logs.Critical("ListenAndServe: ", err, fmt.Sprintf("%d", os.Getpid()))
                    time.Sleep(100 * time.Microsecond)
                    endRunning <- true
                }
            }()
        }
        <-endRunning
        return
    }
    ...
}

http.ListenAndServe的源碼如下:

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

可以很明顯的看出beego最終和http.ListenAndServe調用的接口是一樣的。(此處只對http部分進行分析,https協議也是通用的只是調用的接口是ListenAndServeTLS)

二、Router

1.支持的路由方式

根據官網的描述,beego支持以下路由方式:

  • 基礎路由
    • beego.Get
    • beego.Post
  • 自定義的 handler 實現
s := rpc.NewServer()
s.RegisterCodec(json.NewCodec(), "application/json")
s.RegisterService(new(HelloService), "")
beego.Handler("/rpc", s)
  • 固定路由&正則路由&自定義方法
// 固定路由
beego.Router("/", &controllers.MainController{})
beego.Router("/admin", &admin.UserController{})
// 正則路由
beego.Router("/api/?:id", &controllers.RController{})
beego.Router("/cms_:id([0-9]+).html", &controllers.CmsController{}))
// 指定方法
beego.Router("/api/list",&RestController{},"*:ListFood")
beego.Router("/api/create",&RestController{},"post:CreateFood")
  • 自動匹配路由
beego.AutoRouter(&controllers.ObjectController{})

-註解路由

// @router /all/:key [get]
func (this *CMSController) AllBlock() {

}

beego.Include(&CMSController{})
  • namespace
ns :=
beego.NewNamespace("/v1",
    beego.NSCond(func(ctx *context.Context) bool {
        if ctx.Input.Domain() == "api.beego.me" {
            return true
        }
        return false
    }),
    beego.NSBefore(auth),
    beego.NSGet("/notallowed", func(ctx *context.Context) {
        ctx.Output.Body([]byte("notAllowed"))
    }),
    beego.NSRouter("/version", &AdminController{}, "get:ShowAPIVersion"),
    beego.NSRouter("/changepassword", &UserController{}),
    beego.NSNamespace("/shop",
        beego.NSBefore(sentry),
        beego.NSGet("/:id", func(ctx *context.Context) {
            ctx.Output.Body([]byte("notAllowed"))
        }),
    ),
    beego.NSNamespace("/cms",
        beego.NSInclude(
            &controllers.MainController{},
            &controllers.CMSController{},
            &controllers.BlockController{},
        ),
    ),
)
//註冊 namespace
beego.AddNamespace(ns)

NSNamespace/NewNamespace內部的部分存儲路由時,只處理了自身的路由,總的路由還需要在NSNamespace/NewNamespace進行添加prefix,最終存儲在BeeApp.Handlers.routers.
數據傳輸在此進行:

    app.Server.Handler = app.Handlers
    app.Server.ReadTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
    app.Server.WriteTimeout = time.Duration(BConfig.Listen.ServerTimeOut) * time.Second
    app.Server.ErrorLog = logs.GetLogger("HTTP")

在此,我們研究下幾個主要入口作爲示例來分析源碼。

2.具體路由方式的分析

在初探中我們只做簡單的功能分析,具體的分析我們在後續的文章中加以補充。

(1)beego.Get

beego.Get->BeeApp.Handlers.Get->p.AddMethod->p.addToRouter
其中p即BeeApp.Handlers,因此這一連串的操作主要是將router信息存入BeeApp.Handlers中。

(2)beego.Router

beego.Router->BeeApp.Handlers.Add->p.addWithMethodParams->p.addToRouter
與(1)同理

(3)beego.AutoRouter

beego.AutoRouter->BeeApp.Handlers.AddAuto->p.AddAutoPrefix->p.addToRouter

與(1)同理

(4)beego.Include

beego.Include->BeeApp.Handlers.Include->p.addWithMethodParams->p.addToRouter

與(1)同理

(5)beego.AddNamespace

beego.AddNamespace->BeeApp.Handlers.routers[k]

直接將namespace.handlers.routers中的添加前綴後存入BeeApp.Handlers.routers中。

AddNamespace、NSNamespace及其內部的一系列NSNamespace方法集最終依然調用n.handlers的方法集中,然後經過AddNamespace,保存至BeeApp.Handlers中

三、ServeHTTP

既然處理請求時,經過了http.ListenAndServe,根據我們對ListenAndServe的分析,最終請求會交由server.Handler的ServeHTTP來處理。beego中的Handler即爲BeeApp.Handlers,其類型是ControllerRegister,也就是說請求最終交由ControllerRegister的ServeHTTP實現來處理。此處我們也是隻談下處理過程,具體的細節我們在之後的分析文章內細談。

三、總結

我們分別從兩個入口beego.Run、router兩個入口的使用,可以看到beego處理的邏輯基本上和golang底層http.ListenAndServe、http.HandleFunc的原理一致,只是有更高層次的封裝,以及更便利的路由聲明及處理。在後續的文章中我們會對beego的處理作進一步的分析。

最後,我們可以做下幾點簡單的總結:

(1)routers package主要是生成路由及處理的相關信息,並保存至BeeApp.Handler中,

(2)beego.Run則是帶着BeeApp.Handler的啓動http的服務,

(3)接收請求後,由BeeApp.Handler的ServeHTTP進行處理,作出對應響應。

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