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

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

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

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

Gin启动Web服务的两种方式

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

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

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

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

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

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

Default和New方法

Default和New方法创建的对象有什么不同呢?

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

scss 复制代码
 func Default() *Engine {
   debugPrintWARNINGDefault()
   engine := New()
   engine.Use(Logger(), Recovery())
   return engine
 }

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

gin.Engine对象

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

go 复制代码
 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接口:
go 复制代码
 type Handler interface {
   ServeHTTP(ResponseWriter, *Request)
 }

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

css 复制代码
 engine := gin.New()
 http.ListenAndServe(":8080", engine)

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

Run方法

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

scss 复制代码
 Run(":3000")

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

go 复制代码
 $ PORT=3000 && go run main.go

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

go 复制代码
 //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.Engine的Run()方法可以启动一个Web服务。

相关推荐
猿究院_夏燊36 分钟前
Spring Bean 生命周期
java·后端·spring
CCblandy2 小时前
雪花 ID 四个边界实测:时钟回拨重号 500 个,前端 JSON.parse 直接丢精度
后端·python·算法·性能优化·架构
我是人✓2 小时前
SpringBoot项目的五种创建方式
java·spring boot·后端
念念清晰4 小时前
000-OpenJDK17 源码构建:MacOS环境(M芯片)
java·后端
来福猿4 小时前
SpringCloud Config 源码分析:配置拉取、刷新与多环境管理
后端·spring·spring cloud
IT_陈寒6 小时前
React组件莫名重渲染?你可能忘了这个小细节
前端·人工智能·后端
用户9888321871747 小时前
给 .NET 网络调用设定“预算”:用 CancellationTokenSource.CancelAfter 处理超时取消
后端
清风儿3297 小时前
一块不会说话的电表,把我们折腾了半个月
后端
预知同行7 小时前
深入解析 MoE 混合专家模型:从 Router 到 All-to-All 的架构全景与生产实践
后端·架构
upala7 小时前
【仓颉语言入门 · 第二篇 第 8 课】
后端