前言
报了个驾校,时隔两个多月没发文章了,驾考上周终于都结束了,这之后得补补前两月的文章了。之前定了个目标,读完 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 框架解析》系列文章链接如下:
- golang 框架解析 -beego
- golang 框架解析 -iris