详细讲解go web框架之gin框架源码解析记录及思路流程和理解

本文深入解析Gin框架的实现原理,包括HTTP请求处理流程、路由匹配机制、中间件设计及Context对象的应用。通过源码分析,揭示Gin框架高效运作的秘密。

开篇

开始

1. 从入门教程推出 Engine 对象

  • 从最开始的语句来入门教程:

    func main() {
         
         
    	// 创建一个默认的路由引擎
    	r := gin.Default()
    	// GET:请求方式;/hello:请求的路径
    	// 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数
    	r.GET("/hello", func(c *gin.Context) {
         
         
    		// c.JSON:返回JSON格式的数据
    		c.JSON(200, gin.H{
         
         
    			"message": "Hello world!",
    		})
    	})
    	// 启动HTTP服务,默认在0.0.0.0:8080启动服务
    	r.Run()
    }
    
  • 我们点击进去 Run 函数的源码:

    //执行命令将路由器连接到http。服务器并开始侦听和服务HTTP请求。
    // http的快捷方式。ListenAndServe (addr,路由器)
    //注意:这个方法会无限期地阻塞调用goroutine,除非发生错误。
    func (engine *Engine) Run(addr ...string) (err error) {
         
         
    	defer func() {
         
          debugPrintError(err) }()
    
    	trustedCIDRs, err := engine.prepareTrustedCIDRs()
    	if err != nil {
         
         
    		return err
    	}
    	engine.trustedCIDRs = trustedCIDRs
    	address := resolveAddress(addr)
    	debugPrint("Listening and serving HTTP on %s\n", address)
    	err = http.ListenAndServe(address, engine)
    	return
    }
    
    • 其中最核心的语句就是 : err = http.ListenAndServe(address, engine)
      • 这就是调用的官方标准包, net/http。 可以去查看对应的用法:
        在这里插入图片描述

        • 它负责监听 tcp请求,并且交给 handler 参数去调用一个 Server函数处理接收到的链接。
        • 一般handler 为 nil, 那么默认有一个 DefaultServeMux 对象, 去调用 server函数处理请求。
      • 打开 http.ListenAndServe 源码,会发现,它获取的handler参数就是传递给 Server对象的。

        // ListenAndServe监听TCP网络地址addr,然后调用
        //使用handler处理传入连接的请求。
        //已接受的连接配置为启用TCP keep-alive。
        //
        //处理程序通常为nil,在这种情况下使用DefaultServeMux。
        //
        // ListenAndServe总是返回一个非nil错误。
        func ListenAndServe(addr string, handler Handler) error {
                 
                  
        	server := &Server{
                 
                 Addr: addr, Handler: handler}   //源码中创建 server服务器对象,将handler传递进去
        	return server.ListenAndServe()
        }
        
      • Server函数: server对象的方法,接收每一个链接,并开启 go程,读取请求,调用handler回复请求。
        在这里插入图片描述

      • 默认的 DefaultServeMux

        // ServeMux是一个HTTP请求复用器。
        //它将每个传入请求的URL与一个已注册的列表进行匹配
        // 的模式,并调用处理程序的模式
        //最接近URL。
        //
        //模式名称固定,根路径,如"/favicon.ico",
        //或根子树,如"/images/"(注意后面的斜杠)。
        //较长的模式优先于较短的模式,因此
        //如果有两个处理程序注册"/images/"
        //和"/images/thumbnails/",后一个处理器将是
        //调用以"/images/thumbnails/"开头的路径
        //前将接收任何其他路径的请求
        / / /图片/子树。
        //
        //注意,因为以斜杠结尾的模式命名了根子树,
        //模式"/"匹配所有未被其他注册的路径
        //模式,而不仅仅是URL与Path == "/"。
        //
        //如果一个子树已经注册,并且接收到一个命名为
        //不带末尾斜杠的子树根,ServeMux重定向它
        //请求到子树根(添加末尾的斜杠)。这种行为可以
        //被一个单独的注册路径覆盖
        //末尾的斜杠。例如,注册“/images/”会导致ServeMux
        //重定向请求"/images"到"/images",除非"/images"有
        //已单独注册。
        //
        //模式可以选择以主机名开头,限制匹配为
        //该主机上的url。特定于主机的模式优先
        //通用模式,以便处理程序可以注册这两个模式
        // "/codesearch"和"codesearch.google.com/"不需要接管
        //请求“http://www.google.com/”。
        //
        // ServeMux还负责清理URL请求路径和主机
        //头,剥离端口号和重定向任何请求包含。或
        / / . .元素或重复的斜杠到等效的、更干净的URL。
        type ServeMux struct {
                 
                 
        	mu    sync.RWMutex
        	m     map[string]muxEntry
        	es    []muxEntry // slice of entries sorted from longest to shortest.
        	hosts bool       // whether any patterns contain hostnames
        }
        
        // NewServeMux allocates and returns a new ServeMux.
        func NewServeMux() *ServeMux {
                 
                  return new(ServeMux) }
        
        // DefaultServeMux is the default ServeMux used by Serve.
        var DefaultServeMux = &defaultServeMux
        
        var defaultServeMux ServeMux
        
      • 而对应的server对象在这

        type Server struct {
                 
                 
            Addr           string        // 监听的TCP地址,如果为空字符串会使用":http"
            Handler        Handler       // 调用的处理器,如为nil会调用http.DefaultServeMux
            ReadTimeout    time.Duration // 请求的读取操作在超时前的最大持续时间
            WriteTimeout   time.Duration // 回复的写入操作在超时前的最大持续时间
            MaxHeaderBytes int           // 请求的头域最大长度,如为0则用DefaultMaxHeaderBytes
            TLSConfig      *tls.Config   // 可选的TLS配置,用于ListenAndServeTLS方法
            // TLSNextProto(可选地)指定一个函数来在一个NPN型协议升级出现时接管TLS连接的所有权。
            // 映射的键为商谈的协议名;映射的值为函数,该函数的Handler参数应处理HTTP请求,
            // 并且初始化Handler.ServeHTTP的*Request参数的TLS和RemoteAddr字段(如果未设置)。
            // 连接在函数返回时会自动关闭。
            TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
            // ConnState字段指定一个可选的回调函数,该函数会在一个与客户端的连接改变状态时被调用。
            // 参见ConnState类型和相关常数获取细节。
            ConnState func(net.Conn, ConnState)
            // ErrorLog指定一个可选的日志记录器,用于记录接收连接时的错误和处理器不正常的行为。
            // 如果本字段为nil,日志会通过log包的标准日志记录器写入os.Stderr。
            ErrorLog *log.Logger
            // 内含隐藏或非导出字段
        }
        
  • 可以看到, 我们的代码中: err = http.ListenAndServe(address, engine)

    • 也就是将 engine 传递进去, 作为 handler 参数,也就是不使用默认的 DefaultServeMux

    • engine 是 *Engine 类型。 也就是 我们 gin.Default() 出来的“引擎”(Default 出来的默认带有两个中间件)。 engin 既然可以作为 Handler 类型参数, 那么肯定实现了对应的接口:

      //处理程序响应HTTP请求。
      //
      // serverhttp应该向ResponseWriter写入应答头和数据
      //返回。返回请求完成的信号;它
      // 的ResponseWriter或从
      //请求。正文后或与之同时完成
      // ServeHTTP电话。
      //
      //根据HTTP客户端软件、HTTP协议版本和
      //客户端和Go服务器之间的任何中介,它可能不会
      //可以读取请求。身体写完后给
      // ResponseWriter。谨慎的处理程序应该读取请求。身体
      //首先,然后回复。
      //
      //除了读取主体外,处理程序不应该修改
      //请求提供。
      //
      //如果ServeHTTP崩溃,服务器(调用ServeHTTP的人)会假设
      //将panic的影响与主动请求隔离。
      //它恢复panic,记录堆栈跟踪到服务器错误日志,
      //关闭网络连接或者发送HTTP/2
      // RST_STREAM,取决于HTTP协议。中止处理程序,所以
      //客户端看到一个中断的响应,但服务器没有记录
      //一个错误,慌乱与值ErrAbortHandler。
      type Handler interface {
             
             
      	ServeHTTP(ResponseWriter, *Request)
      }
      
  • 总结

    • 调用Run方法, 实际底层是使用 http包的方法, 将 Engine 类型对象作为 handler 参数传递进去(实现了Handler接口),构造 Server对象, 代替默认的 DefaultServeMux 处理请求链接,DefaultServeMux 实现了 相关的 match 等方法(去server.go 搜索ServeMux, 找到它实现的相关方法) 。

2. Engine 对象介绍及handleHTTPRequest接口实现与路由方法树树相关操作

  • 认清 Engine 对象 及giin框架。
    • 源码上方的介绍:Engine 是框架的实例,它包含muxer,中间件和配置设置
      • 可以看到是非常重要的! 同时我们也知道了 gin 框架的主要作用, 就是 充当了 muxer 代替 net/http 包里 启动服务器对象的默认 muxer, 同时还提供了 中间件配置设置等功能
  • 我们查看源码,找到Egine 对象: 比较长,注释很清晰,每个功能的作用。
    • 参考文章: https://zhuanlan.zhihu.com/p/102303084, Engine 相关常用的方法,在这里都有介绍。一定进去查看下!

      • 理解了其实还可以使用 goland编辑器, 鼠标停在gin,Default()。Defalut上。 然后出现返回类型, *Engine, 点击一下,就会出现相关具有的方法 和 内容。(缺少一个maxParams参数,老版本这个参数是在路由树的里定义的)
      //Engine是框架的实例,它包含muxer,中间件和配置设置。
      //创建一个Engine实例,使用New()或Default()
      type Engine struct {
             
             
          // 路由组,在实际开发过程中我们通常会使用路由组来组织和管理一些列的路由. 比如: /apis/,/v1/等分组路由
          RouterGroup
          // 开启自动重定向。如果当前路由没有匹配到,但是存在不带/开头的handler就会重定向. 比如: 用户输入/foo/但是存在一个/foo 就会自动重定向到该handler,并且会向客户端返回301或者307状态码(区别在于GET方法和其他方法)
          RedirectTrailingSlash bool
          // 如果开启该参数,没有handler注册时,路由会尝试自己去修复当前的请求地址.
          // 修复流程:
          // 1.首位多余元素会被删除(../ or //); 2.然后路由会对新的路径进行不区分大小写的查找;3.如果能正常找到对应的handler,路由就会重定向到正确的handler上并返回301或者307.(比如: 用户访问/FOO 和 /..//Foo可能会被重定向到/foo这个路由上)
          RedirectFixedPath bool
          // 如果开启该参数,当当前请求不能被路由时,路由会自己去检查其他方法是否被允许.在这种情况下会响应"Method Not Allowed",并返回状态码405; 如果没有其他方法被允许,将会委托给NotFound的handler
          HandleMethodNotAllowed bool
          // 是否转发客户端ip
          ForwardedByClientIP    bool
          // 如果开启将会在请求中增加一个以"X-AppEngine..."开头的header
          AppEngine bool
          // 如果开启将会使用url.RawPath去查找参数(默认:false)
          UseRawPath bool
          // 如果开启,请求路径将不会被转义. 如果UseRawPath为false,该参数实际上就为true(因为使用的是url.Path)
          UnescapePathValues bool
          // maxMemory参数的值(http.Request的ParseMultipartForm调用时的参数)
          MaxMultipartMemory int64
          // 是否删除额外的反斜线(开始时可解析有额外斜线的请求)
          RemoveExtraSlash bool
          // 分隔符(render.Delims表示使用HTML渲染的一组左右分隔符,具体可见html/template库)
          delims           render.Delims
          // 设置在Context.SecureJSON中国的json前缀
          secureJsonPrefix string
          // 返回一个HTMLRender接口(用于渲染HTMLProduction和HTMLDebug两个结构体类型的模板)
          HTMLRender       render.HTMLRender
          // html/template包中的FuncMap map[string]interface{} ,用来定义从名称到函数的映射
          FuncMap          template.FuncMap
          // 以下
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值