大家好,我是渔夫子。本号新推出「Go工具箱」系列,意在给大家分享使用go语言编写的、实用的、好玩的工具。同时了解其底层的实现原理,以便更深入地了解Go语言。
ginbeegonet/http一、标准库 net/http 的请求流程
首先,我们来看下http包是如何处理请求的。通过以下代码我们就能启动一个http服务,并处理请求:
import (
"net/http"
)
func main() {
// 指定路由
http.Handle("/", &HomeHandler{})
// 启动http服务
http.ListenAndServe(":8000", nil)
}
type HomeHandler struct {}
// 实现ServeHTTP
func (h *HomeHandler) ServeHTTP(response http.ResponseWriter, request *http.Request) {
response.Write([]byte("Hello World"))
}
http://localhost:8000/HomeHandlerServeHTTPHello WorldHomeHandlerServeHTTPServeHTTPhttp.ListenAndServefunc ListenAndServe(addr string, handler Handler) error
HandlerHandlerServeHTTPtype Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
HomeHandlerServeHTTPhttp.ListenAndServe(":8000", nil)nilHomeHandlerServeHTTP/src/net/http/server.goserverHandler{c.server}.ServeHTTP(w, w.req)
serverHandlerc.servercc.serverhttp.ListenAndServe(":8000", nil)serverfunc ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
serverHandlerhttp.ListenAndServe(":8000", nil)nilserverHandler{c.server}.ServeHTTP(w, w.req)func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
...
handler.ServeHTTP(rw, req)
}
/src/net/http/server.goserverHandlernilDefaultServeMuxhander.ServeHTTPDefaultServeMuxServeHTTP/src/net/http/server.goh, _ := mux.Handler(r)h.ServeHTTPhandlerhandlerServeHTTPHomeHandlerServeHTTPfunc (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
**ServeHTTP****net/http**二、gin框架的http的请求流程
gin框架对http的处理流程本质上都是基于go标准包net/http的处理流程的。 下面我们看下gin框架是如何基于net/http实现对一个请求处理的。 首先我们看通过gin框架是如何启动http服务的:
import (
"github.com/gin-gonic/gin"
)
func main() {
// 初始化gin中自定义的Engine结构体对象
engine := gin.New()
// 添加路由
engine.GET("/", HomeHandler)
// 启动http服务
engine.Run(":8000")
}
func HomeHandler(ctx *gin.Context) {
ctx.Writer.Write([]byte("Hi, this is gin Home page"))
}
engine.Runnet/httphttpfunc (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
}
http.ListenAndServe(address, engine.Handler())httpgonet/httpnilengine.Handler()engine.Handler()http.Handlerenginehttp2enginehttp.Handlerhttp.HandlerServeHTTPfunc (engine *Engine) Handler() http.Handler {
if !engine.UseH2C {
// 这里直接返回了engine对象
return engine
}
h2s := &http2.Server{}
return h2c.NewHandler(engine, h2s)
}
EngineServeHTTP// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset()
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
engine.handleHTTPRequest(c)func (engine *Engine) handleHTTPRequest(c *Context) {
httpMethod := c.Request.Method
rPath := c.Request.URL.Path
//省略代码...
// 根据请求的方法httpMethod和请求路径rPath查找对应的路由
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
if t[i].method != httpMethod {
continue
}
root := t[i].root
// 在路由树中找到了该请求路径的路由
value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
if value.params != nil {
c.Params = *value.params
}
if value.handlers != nil {
c.handlers = value.handlers
c.fullPath = value.fullPath
c.Next()
c.writermem.WriteHeaderNow()
return
}
// 省略代码...
}
// 省略代码...
// 没有找到路由,则返回404
c.handlers = engine.allNoRoute
serveError(c, http.StatusNotFound, default404Body)
}
c.handlersc.Next()c.Next()c.handlersfunc (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
c.handlersHandlersChaintype HandlersChain []HandlerFunc
HandlersChainHandlerFuncHandlerFunctype HandlerFunc func(*Context)
engine.GET("/", HomeHandler)HomeHandlerHomeHandlerimport (
"github.com/gin-gonic/gin"
)
func main() {
// 初始化gin中自定义的Engine结构体对象
engine := gin.New()
// 添加路由
engine.GET("/", HomeHandler)
// 启动http服务
engine.Run(":8000")
}
func HomeHandler(ctx *gin.Context) {
ctx.Writer.Write([]byte("Hi, this is gin Home page"))
}
这样就形成了一个处理流程的闭环。我们总结下gin框架对http请求的处理流程。
- 首先,通过gin.New()创建一个Engine结构体实例,该Engine结构体实现了net/http包中的http.Handler接口中的ServeHTTP方法。
- 通过engine.Run函数启动服务。本质上也是通过net/http包中的http.ListenAndServe方法启动服务的,只不过是是将engine作为服务接收请求的默认handler。即Engine.ServeHTTP方法。
- 在Engine结构体的ServeHTTP方法中,通过路由查找找到该次请求的对应路由,然后执行对应的路由执行函数。即func(ctx *gin.Context)类型的路由。
以下是gin框架处理http请求的全景图:
三、beego框架的http请求处理流程
beego框架启动http服务并监听处理http请求本质上也是使用了标准包net/http中的方法。和gin框架不同的是,beego直接使用net/http包中的Server对象进行启动,而并没有使用http.ListenAndServe方法。但本质上是一样的,http.ListenAndServe方法的底层是也调用了net/http包中的Server对象启动的服务。
首先我们看下beego框架启动http服务的过程:
package main
import (
"github.com/beego/beego/v2/server/web"
beecontext "github.com/beego/beego/v2/server/web/context"
)
func main() {
web.Get("/home", HomeHandler)
web.Run(":8000")
}
func HomeHandler(ctx *beecontext.Context){
ctx.Output.Body([]byte("Hi, this is beego home"))
}
/home 8000web.Run(":8000")func Run(params ...string) {
if len(params) > 0 && params[0] != "" {
BeeApp.Run(params[0])
}
BeeApp.Run("")
}
BeeAppRunBeeApp.RunBeeApp.RunRunBeeApp.Run("")func (app *HttpServer) Run(addr string, mws ...MiddleWare) {
// init...
app.initAddr(addr)
app.Handlers.Init()
addr = app.Cfg.Listen.HTTPAddr
var (
err error
l net.Listener
endRunning = make(chan bool, 1)
)
app.Server.Handler = app.Handlers
if app.Cfg.Listen.EnableHTTP {
go func() {
app.Server.Addr = addr
if app.Cfg.Listen.ListenTCP4 {
// 省略...
} else {
if err := app.Server.ListenAndServe(); err != nil {
logs.Critical("ListenAndServe: ", err)
// 100毫秒 让所有的协程运行完成
time.Sleep(100 * time.Microsecond)
endRunning <- true
}
}
}()
}
// 通过通道进行阻塞
<-endRunning
BeeAppBeeApp*HttpServerinitvar BeeApp *HttpServer
我们看下HttpServer的结构体包含的主要字段如下:
http.ServerServerControllerRegisterHandlersControllerRegisterControllerRegisterroutersFilterRouterControllerRegisterServeHTTPhttp.Handlerfunc (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
ctx := p.GetContext()
ctx.Reset(rw, r)
defer p.GiveBackContext(ctx)
var preFilterParams map[string]string
p.chainRoot.filter(ctx, p.getUrlPath(ctx), preFilterParams)
}
p.chainRoot.filter(ctx, p.getUrlPath(ctx), preFilterParams)ControllerRegisterserveHttphttp.HandlerServerHTTPserveHttpchainRootnewFilterRouterfunc NewControllerRegisterWithCfg(cfg *Config) *ControllerRegister {
res := &ControllerRegister{
routers: make(map[string]*Tree), //路由表,一个方法一棵树
policies: make(map[string]*Tree),
pool: sync.Pool{
New: func() interface{} {
return beecontext.NewContext()
},
},
cfg: cfg,
filterChains: make([]filterChainConfig, 0, 4),
}
res.chainRoot = newFilterRouter("/*", res.serveHttp, WithCaseSensitive(false))
return res
}
最后,我们再看下路由注册的过程。路由注册有三种方式,这里我们只看其中的一种:用可执行函数进行注册,如下:
web.Get("/home", HomeHandler)
func HomeHandler(ctx *beecontext.Context){
ctx.Output.Body([]byte("Hi, this is beego home"))
}
HomeHandlerweb.GetControllerInfofunc (p *ControllerRegister) createRestfulRouter(f HandleFunc, pattern string) *ControllerInfo {
route := &ControllerInfo{}
route.pattern = pattern
route.routerType = routerTypeRESTFul
route.sessionOn = p.cfg.WebConfig.Session.SessionOn
route.runFunction = f
return route
}
fHomeHandlerrunFunctionroute.runFunction好了,beego框架处理http请求的流程基本就是这样,具体的路由实现我们后续再单独起一篇文章介绍。如下是该框架处理http请求的一个全景图:
四、总结
net/httphandlerServeHTTPServeHTTPServeHTTP