美文网首页程序员
gonic之中间件实现-异常恢复

gonic之中间件实现-异常恢复

作者: pysta | 来源:发表于2018-11-02 21:48 被阅读6次

    最近公司要求更新自研框架,就顺便看了一下gonic的框架实现,代码不多,感觉主要有两个特点,第一个httprouter来实现路由,第二个就是异常恢复,更深入看后原来是中间件,httprouter就不多说了,现记录一下中间件的实现,以便后面需要。

    简单的运行一下框架

    engine := gin.Default()

    engine.GET("/index", foo)

    engine.Run(":80")

    func foo(ctx *gin.Context)  {

    ctx.String(200, "hello world")

    }

    主要的一些源码

    func Default() *Engine {

    engine := New()

    engine.Use(Logger(), Recovery())

    return engine

    }

    engine := &Engine{

        RouterGroup: RouterGroup{

            Handlers: nil,

            basePath: "/",

            root:    true,

        },

        FuncMap:                template.FuncMap{},

        RedirectTrailingSlash:  true,

        RedirectFixedPath:      false,

        HandleMethodNotAllowed: false,

        ForwardedByClientIP:    true,

        AppEngine:              defaultAppEngine,

        UseRawPath:            false,

        UnescapePathValues:    true,

        trees:                  make(methodTrees, 0, 9),

        delims:                render.Delims{"{{", "}}"},

    }

    RouterGroup struct {

        Handlers HandlersChain

        basePath string

        engine  *Engine

        root    bool

    }

    type methodTrees []methodTree

    type methodTree struct {

    method string

    root  *node

    }

    type node struct {

    path      string

    wildChild bool

    nType    nodeType

    maxParams uint8

    indices  string

    children  []*node

    handlers  HandlersChain

    priority  uint32

    }

    1,代码的一个行,初始化框架引擎,关注里面比较重要的两个字段,routergroup和trees。

    routergroup是一个路由组,里面的handlers就是中间件,default函数里面的engine.Use(Logger(), Recovery())就是把logger和recovery注册进这个handlers里面,真正的路由实现是在trees里面,我们知道http method除了常用的get,post还有head等共9种。trees的每个tree就是一个保存了method和使用该method的所有path,源码中的methodtree和node就是实现这个目的,node里面会结构化的保存path信息。

    2,代码的第二行,注册路由。里面会把path(index)和handler(foo)注册到engine的trees里面,这里面注册handler的时候会把routergroup的中间件handlers和自定义的handler一起保存。源码如下:

    func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {

    absolutePath := group.calculateAbsolutePath(relativePath)

    handlers = group.combineHandlers(handlers)

    group.engine.addRoute(httpMethod, absolutePath, handlers)

    return group.returnObj()

    }

    func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {

    finalSize := len(group.Handlers) + len(handlers)

    if finalSize >= int(abortIndex) {

    panic("too many handlers")

    }

    mergedHandlers := make(HandlersChain, finalSize)

    copy(mergedHandlers, group.Handlers)

    copy(mergedHandlers[len(group.Handlers):], handlers)

    return mergedHandlers

    }

    3.启动框架监听端口。这时候如果有请求进来就会出发engine的ServeHTTP方法。

    func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {

    c := engine.pool.Get().(*Context)

    c.writermem.reset(w)

    c.Request = req

    c.reset()

    engine.handleHTTPRequest(c)

    engine.pool.Put(c)

    }

    其中的engine.handleHTTPRequest用来处理业务逻辑的。部分源码如下:

    func (engine *Engine) handleHTTPRequest(context *Context) {

    httpMethod := context.Request.Method

    var path string

    var unescape bool

    if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 {

    path = context.Request.URL.RawPath

    unescape = engine.UnescapePathValues

    } else {

    path = context.Request.URL.Path

    unescape = false

    }

    // Find root of the tree for the given HTTP method

    t := engine.trees

    for i, tl := 0, len(t); i < tl; i++ {

    if t[i].method == httpMethod {

    root := t[i].root

    // Find route in tree

    handlers, params, tsr := root.getValue(path, context.Params, unescape)

    if handlers != nil {

    context.handlers = handlers

    context.Params = params

    context.Next()

    context.writermem.WriteHeaderNow()

    return

    }

    if httpMethod != "CONNECT" && path != "/" {

    if tsr && engine.RedirectTrailingSlash {

    redirectTrailingSlash(context)

    return

    }

    if engine.RedirectFixedPath && redirectFixedPath(context, root, engine.RedirectFixedPath) {

    return

    }

    }

    break

    }

    }

    从源码中可以看到,通过http method找到对应的methoTree里的node,然后根据请求路径path找到对应的handleChain,这个handleChains就是第二部注册的routerGroup.handlers(中间件)+自己的业务handler(foo),

    其中的context.Next()会依次执行handlerChans里的handler,该代码会在同一个goroutine里执行,首先执行了中间件,而recovery中间件中有panic的捕获,所以即使自己的业务handler发生了panic也不会中断服务,会被revocer中间件捕获。

    阿里云双十一云服务器拼团活动,已经打到最低价99元一年!有需要的可以考虑一波了!

    https://m.aliyun.com/act/team1111/#/share?params=N.9g4CZ2TwSh.qilw7y0a

    相关文章

      网友评论

        本文标题:gonic之中间件实现-异常恢复

        本文链接:https://www.haomeiwen.com/subject/ttlhxqtx.html