公众号:程序员读书,欢迎关注
大家好,在这个系列中,我们从源码的角度来探究一下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
服务。