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方法

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

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

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

Default方法会调用gin.EngineUse方法设置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.EngineRun()方法可以启动一个Web服务。

相关推荐
晚风吹长发19 小时前
初步了解Linux中的动静态库及其制作和使用
linux·运维·服务器·数据结构·c++·后端·算法
梁下轻语的秋缘20 小时前
ESP32-WROOM-32E存储全解析:RAM/Flash/SD卡读写与速度对比
java·后端·spring
wanzhong233320 小时前
开发日记8-优化接口使其更规范
java·后端·springboot
羊小猪~~21 小时前
【QT】--文件操作
前端·数据库·c++·后端·qt·qt6.3
宇宙帅猴21 小时前
【Ubuntu踩坑及解决方案(一)】
linux·运维·ubuntu·go
张彦峰ZYF1 天前
商品供给域的工程化简要设计考量
后端·系统架构·商品模型·商品供给
小北方城市网1 天前
微服务注册中心与配置中心实战(Nacos 版):实现服务治理与配置统一
人工智能·后端·安全·职场和发展·wpf·restful
爬山算法1 天前
Hibernate(47)Hibernate的会话范围(Scope)如何控制?
java·后端·hibernate
源码宝1 天前
云HIS二次开发实施路径指南
后端·源码·二次开发·saas·云his·医院信息系统
李慕婉学姐1 天前
Springboot旅游景点管理系统2fj40iq6(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端