94

Go框架解析:iris

 4 years ago
source link: https://www.tuicool.com/articles/aAVNZvF
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

报了个驾校,时隔两个多月没发文章了,驾考上周终于都结束了,这之后得补补前两月的文章了。之前定了个目标,读完beego、iris、gin等go框架的源码,之前已经发过一篇过于beego的文章Go框架解析:beego,今天带来的是go框架iris的解析,主要讲解iris框架的一个生命周期过程。

在读这篇文章之前,如果没看过Go框架解析:beego的可以先去看看,因为Go框架解析: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的生命周期

EF7jiiq.png!web

访问图片源地址查看大图 http://cdn.tigerb.cn/20190628234814.png

上图是我在读iris代码时,整理的iris框架的一个生命周期流程图,内容比较多。总的来说划分为四个大的部分:

橙色部分

初始化iris.Application:

  • 创建iris.Application
  • 创建APIBuilder(app.Get()等方法的路由都是注册到这里)
  • 创建Router(每个http请求都是通过router处理的)

蓝色部分

注册路由到app.APIBuilder

紫色部分

初始化一个http.Server

绿色部分

构建路由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()
:arrow_down:
// 构建
app.Build()
:arrow_down:
// 构建路由
app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder, false)
:arrow_down:
// 构建请求Handler 
// 把app.APIBuilder注册的api注册到了requestHandler里
// 因为我们在下面发现请求都是从router.requestHandler去处理的
requestHandler.Build(routesProvider)
:arrow_down:
// 赋值
router.requestHandler = requestHandler
router.routesProvider = routesProvider
:arrow_down:
// 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)
}
:arrow_down:
// 实际处理请求饿地方
// 路由的匹配就是这里了
func (h *routerHandler) HandleRequest(ctx context.Context)
  1. 启动HTTP Server

最后我们就是启动这个http server了,这里和绝大多数golang的http服务启动基本一致。

// 赋值http服务的ip+port
iris.Addr(":8888")
:arrow_down:
//创建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()
	}
}
:arrow_down:
// app.NewHost(&http.Server{Addr: addr})
// 就是这里赋值app.Router给http.Server的Handler的
if srv.Handler == nil {
    srv.Handler = app.Router
}
:arrow_down:
// 启动服务
su.Server.Serve(l)
:arrow_down:
// accept请求
l.Accept()
:arrow_down:
// 启动一个goroutine处理请求
go c.serve(ctx)
:arrow_down:
// 最终至此真相都大白了
serverHandler{c.server}.ServeHTTP(w, w.req)

结语

最后我们再简单的回顾下上面的流程:

IF3eiee.png!web

《Go框架解析:系列文章链接如下:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK