Go 框架解析 -iris_一个北漂的程序猿

文章目录

前言

之前已经发过一篇过于beego的文章《golang框架解析-beego》,今天带来的是go框架iris的解析,主要讲解iris框架的一个生命周期过程。

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

安装

使用glide安装:

1glide get github.com/kataras/iris 2glide get github.com/kataras/golog

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

1//main.go 2package main 3 4import "github.com/kataras/iris" 5 6func main() { 7 app := iris.Default() 8 app.Get("/ping", func(ctx iris.Context) { 9 ctx.JSON(iris.Map{ 10 "message": "pong", 11 }) 12 }) 13 app.Run(iris.Addr(":8888")) 14}

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
1// Application 首先看看我们的iris Application结构体组成 2type Application struct { 3 // 我们的路由都注册到了 APIBuilder 4 *router.APIBuilder 5 // *router.Router 实现了ServeHTTP方法 并且最终赋值给了&http.server{}.Handler 6 *router.Router 7 // 请求上下文池子 8 ContextPool *context.Pool 9 // 配置项 10 config *Configuration 11 // 日志 12 logger *golog.Logger 13 // 视图 14 view view.View 15 // 执行一次的once 16 once sync.Once 17 // 互斥锁 18 mu sync.Mutex 19 Hosts []*host.Supervisor 20 hostConfigurators []host.Configurator 21} 22 23// 创建了一个iris应用实例 24// 为什么不直接New呢? 25// 因为Default里面注册了两个handle 26// 1. recover panic的方法, 27// 2. 请求日志 28app := iris.Default() 29 30func Default() *Application { 31 app := New() 32 // 合成复用*APIBuilder的Use 33 app.Use(recover.New()) 34 // 合成复用*APIBuilder的Use 35 app.Use(requestLogger.New()) 36 37 return app 38} 39 40// app := New() 得到的结构体 41app := &Application{ 42 config: &config, 43 logger: golog.Default, 44 // 很关键:我们的路由都注册到了 APIBuilder 45 APIBuilder: router.NewAPIBuilder(), 46 // 很关键:*router.Router 实现了ServeHTTP方法 并且最终赋值给了&http.server{}.Handler 47 Router: router.NewRouter(), 48} 49 50// 注册api请求的中间件 51func (api *APIBuilder) Use(handlers ...context.Handler) { 52 api.middleware = append(api.middleware, handlers...) 53}
  1. 关于router.NewAPIBuilder()
    APIBuilder的routes属性很关键,最终的我们定义的路由都是注册到了这里。
1// APIBuilder 2api := &APIBuilder{ 3 macros: macro.Defaults, 4 errorCodeHandlers: defaultErrorCodeHandlers(), 5 reporter: errors.NewReporter(), 6 relativePath: "/", 7 // 最终的我们定义的路由都是注册到了这里 8 routes: new(repository), 9} 10 11// repository的结构 12type repository struct { 13 routes []*Route 14}

结论:用户路由注册到了app.APIBuilder.routes

  1. 关于router.NewRouter()

router.NewRouter()返回的是一个&Router{}指针,&Router{}有三个很关键的属性和一个ServeHTTP成员方法。

三个关键的属性:

  • ainHandler http.HandlerFunc
  • requestHandler RequestHandler
  • routesProvider RoutesProvider

我们再看成员方法ServeHTTP实现了ServeHTTP(w http.ResponseWriter, r *http.Request)方法,就是accept请求之后就会执行这个方法,我们看看具体方法内容。

1// implement ServeHTTP 2func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { 3 // 所以这里可以看出accept请求之后会执行mainHandler 4 router.mainHandler(w, r) 5}

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)

1cPool *context.Pool r 2routesProvider RoutesProvider

}

// implement ServeHTTP func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { // 每次http请求都会执行mainHandler router.mainHandler(w, r) }

1 2结论:每次http请求都会执行mainHandler 3 44. 注册路由 5 6这里很简单了就是注册用户路由到app.APIBuilder.routes 7

//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 25. 构建请求handler 3

//启动路由 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 26. 启动HTTP Server 3 4最后我们就是启动这个http server了,这里和绝大多数golang的http服务启动基本一致。 5

// 赋值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)

1 2[](https://blog.csdn.net/itqingliang/article/details/105650738)结语 3================================================================= 4 5最后我们再简单的回顾下上面的流程: 6 7![在这里插入图片描述](https://img-hello-world.oss-cn-beijing.aliyuncs.com/cf0dee5ebb20976ff1e69ef8c3e6d93c.png) 8 9[](https://blog.csdn.net/itqingliang/article/details/105650738)文献 10================================================================= 11 12https://mp.weixin.qq.com/s/-xhhuPkutY1-G4\_NugNotQ 13 14 15 16 17 18本文转自 [https://blog.csdn.net/itqingliang/article/details/105650738](https://blog.csdn.net/itqingliang/article/details/105650738),如有侵权,请联系删除。
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )