likes
comments
collection
share

Gin框架:启动Web服务时,Gin框架做了什么?

作者站长头像
站长
· 阅读数 4

公众号:程序员读书,欢迎关注

大家好,在这个系列中,我们从源码的角度来探究一下Gin框架,每一篇文章只专注一个知识点的讲解!

在这篇文章中,我们来探究一下,启动Web服务时,Gin框架做了什么?

Gin启动Web服务的两种方式

使用Gin框架启动一个Web服务器,本质上是创建一个gin.Engine对象的实例,有两种方式。

一是通过New方法创建gin.Engine对象实例:

 package main
 ​
 import (
   "github.com/gin-gonic/gin"
 )
 ​
 func main() {
   engine := gin.New()
   engine.Run()
 }

二是通过Default方法创建gin.Engine对象实例:

 package main
 ​
 import (
   "github.com/gin-gonic/gin"
 )
 ​
 func main() {
   engine := gin.Default()
   engine.Run()
 }

创建gin.Engine对象的实例后,调用其Run方法,就这样,一个Web服务器便启动了!

Default和New方法

DefaultNew方法创建的对象有什么不同呢?

通过查看Default方法的源码可以得知,Default方法实际上是调用New方法的:

 func Default() *Engine {
   debugPrintWARNINGDefault()
   engine := New()
   engine.Use(Logger(), Recovery())
   return engine
 }

Default方法会调用gin.EngineUse方法设置Logger()Recovery()两个中间件。

gin.Engine对象

gin.Engine对象是gin包下的一个结构体:

 type Engine struct {
     //路由分组
   RouterGroup
     //当请求以/结尾时,是否重定向,默认为是
   RedirectTrailingSlash bool
   //是否自动修复路径并重定向,默认为否
     RedirectFixedPath bool
   //当请求method不匹配时,是否返回403,默认为否
     HandleMethodNotAllowed bool
     //是否按RemoteIPHeaders属性规则的头部解析客户端IP地址
   ForwardedByClientIP bool
     //是否从原始请求路径中查找参数
   UseRawPath bool
     //是否取消url转义
   UnescapePathValues bool
   //是否移除多余的/
     RemoveExtraSlash bool
   //获取客户端IP的头部字段列表
     RemoteIPHeaders []string
     //信任的平台
   TrustedPlatform string
     //请求内容大小限制,默认为32M
   MaxMultipartMemory int64
     //是否开启http2
   UseH2C bool
     //是否开启回退
   ContextWithFallback bool
     //HTML模板动态代码分隔符
   delims           render.Delims
     //安全json前缀
   secureJSONPrefix string
     //HTML模板渲染器
   HTMLRender       render.HTMLRender
   //HTML模板处理函数
     FuncMap          template.FuncMap
   //未匹配路由时的处理函数
     allNoRoute       HandlersChain
   //未匹配方法时的处理函数
     allNoMethod      HandlersChain
   //未匹配路由时的处理函数
     noRoute          HandlersChain
   //未匹配方法时的处理函数
     noMethod         HandlersChain
   //缓存池
     pool             sync.Pool
     //路由树
   trees            methodTrees
   //参数个数
     maxParams        uint16
   //路径个数
   maxSections      uint16
     //信任代理列表
   trustedProxies   []string
     //信任的ip列表
   trustedCIDRs     []*net.IPNet
 }

Engine结构有非常多的属性,我们在后面的源码学习中再详细讲解。

这里对于Engine对象,我们先有两个基本的认识:

  • 我们看到Engine对象拥有路由分组RouteGroup对象,因此可以说Engine对象是所有路由的根路由。
  • Engine对象还实现了http.Handler接口:
 type Handler interface {
   ServeHTTP(ResponseWriter, *Request)
 }

因此可以把Engine对象实例作为net/http中的路由复用器,所以,我们还可以这样启动Web服务器:

 engine := gin.New()
 http.ListenAndServe(":8080", engine)

实际上,Run方法底层就是调用http.ListenAndServe方法。

Run方法

创建gin.Engine对象实例之后,调用Run()方法启动Web服务器时,也可以传一个参数作为端口号:

 Run(":3000")

在没有设置端口号时,也可以通过环境变量PORT设置端口号:

 $ PORT=3000 && go run main.go

如果没有传环境变量或者给Run()方法设置参数,则默认设置为监听8080端口,这些从Run()方法的源码都可以看得出来:

 //gin.go第367行起
 func (engine *Engine) Run(addr ...string) (err error) {
   defer func() { debugPrintError(err) }()
 ​
   if engine.isUnsafeTrustedProxies() {
     debugPrint("[WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.\n" +
       "Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.")
   }
 ​
   address := resolveAddress(addr)
   debugPrint("Listening and serving HTTP on %s\n", address)
   err = http.ListenAndServe(address, engine.Handler())
   return
 }
 ​
 //utils.go第141行起
 func resolveAddress(addr []string) string {
   switch len(addr) {
   case 0:
     if port := os.Getenv("PORT"); port != "" {
       debugPrint("Environment variable PORT="%s"", port)
       return ":" + port
     }
     debugPrint("Environment variable PORT is undefined. Using port :8080 by default")
     return ":8080"
   case 1:
     return addr[0]
   default:
     panic("too many parameters")
   }
 }

从上面的源码可以看到Run方法的参数虽然是可变长的,但resolveAddress()函数会检查传入参数的个数,当向Run方法传入多个参数是会触发panic

小结

gin.Engine对象是Gin框架的入口,该对象有非常多的属性,另外,gin.Engine对象实现了http.Handler接口,可以与net/http完美兼容,调用gin.EngineRun()方法可以启动一个Web服务。