前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >golang框架解析-iris

golang框架解析-iris

作者头像
大愚
发布2019-07-24 15:42:24
1.4K0
发布2019-07-24 15:42:24
举报
文章被收录于专栏:大愚Talk

前言

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

在读这篇文章之前,如果没看过《golang框架解析-beego》的可以先去看看,因为《golang框架解析-beego》有讲关于go如何启动一个http server,这个知识点对理解本篇文章有很大的帮助。

安装

使用glide安装:

代码语言:javascript
复制
glide get github.com/kataras/iris
glide get github.com/kataras/golog

启动一个简单的iris http服务:

代码语言:javascript
复制
//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/20190628234814.png

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

橙色部分:

初始化iris.Application:

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

注册路由到app.APIBuilder

紫色部分:

初始化一个http.Server

绿色部分:

构建路由handler&启动http server:

  • 注册 app.APIBuilder.routes 的路由到 app.Router.requestHandler
  • 注册 app.APIBuilder 到 app.Router.routesProvider
  • 启动 http server

关键代码解析

  1. 创建一个iris Application
代码语言:javascript
复制
// 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属性很关键,最终的我们定义的路由都是注册到了这里。

代码语言:javascript
复制
// 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请求之后就会执行这个方法,我们看看具体方法内容。

代码语言:javascript
复制
// implement ServeHTTP
func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 所以这里可以看出accept请求之后会执行mainHandler
	router.mainHandler(w, r)
}
代码语言:javascript
复制
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

代码语言:javascript
复制
//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
代码语言:javascript
复制
//启动路由
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服务启动基本一致。

代码语言:javascript
复制
// 赋值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.简单的回顾下上面的流程:
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 大愚Talk 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 安装
  • iris的生命周期
    • 橙色部分:
      • 蓝色部分:
        • 紫色部分:
          • 绿色部分:
          • 关键代码解析
          相关产品与服务
          消息队列 TDMQ
          消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档