gin-http流程
go http流程
go中使用web非常简单,因为api封装很完美,一般我们会写下面代码:
http.Handle("/",handler)
http.HandleFunc("/user",HandlerFunc)
http.ListenAndServe(":8080",nil)
还有下面的:
mux := http.ServeMux{}
mux.Handle("",handler)
http.ListenAndServe("",&mux)
前者没有自动路由器,那么会使用默认的serveMux。
还会有疑问,为什么有的是handler有的是方法? 其实方法也是实现了handler接口,传进去后被强转为handler即可。 证据如下:
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
Go代码的执行流程 通过对http包的分析之后,现在让我们来梳理一下整个的代码执行过程。
首先调用Http.HandleFunc
按顺序做了几件事:
1 调用了DefaultServeMux的HandleFunc
2 调用了DefaultServeMux的Handle
3 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则
其次调用http.ListenAndServe(":9090", nil)
按顺序做了几件事情:
-
实例化Server
-
调用Server的ListenAndServe()
-
调用net.Listen(“tcp”, addr)监听端口
-
启动一个for循环,在循环体中Accept请求
-
对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()
-
读取每个请求的内容w, err := c.readRequest()
-
判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux
-
调用handler的ServeHttp
-
在这个例子中,下面就进入到DefaultServeMux.ServeHttp
-
根据request选择handler,并且进入到这个handler的ServeHTTP mux.handler(r).ServeHTTP(w, r)
-
选择handler: A 判断是否有路由能满足这个request(循环遍历ServerMux的muxEntry) B 如果有路由满足,调用这个路由handler的ServeHttp C 如果没有路由满足,调用NotFoundHandler的ServeHttp
net/http 流程
不管server代码如何封装, 都离不开bind,listen,accept这些函数. 就从上面这个简单的demo入手查看源码.
注册路由
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World"))
})
if err := http.ListenAndServe(":8000", nil); err != nil {
fmt.Println("start http server fail:", err)
}
}
这段代码是在注册一个路由及这个路由的handler到DefaultServeMux中
// server.go:L2366-2388
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
if pattern == "" {
panic("http: invalid pattern")
}
if handler == nil {
panic("http: nil handler")
}
if _, exist := mux.m[pattern]; exist {
panic("http: multiple registrations for " + pattern)
}
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
mux.m[pattern] = muxEntry{h: handler, pattern: pattern}
if pattern[0] != '/' {
mux.hosts = true
}
}
可以看到这个路由注册太过简单了, 也就给gin, iris, echo等框架留下了扩展的空间
等待客户端连接
// net/http/server.go:L2752-2765
func (srv *Server) ListenAndServe() error {
// ... 省略代码
ln, err := net.Listen("tcp", addr) // <-----看这里listen
if err != nil {
return err
}
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
// net/http/server.go:L2805-2853
func (srv *Server) Serve(l net.Listener) error {
// ... 省略代码
for {
rw, e := l.Accept() // <----- 看这里accept
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx) // <--- 看这里
}
}
// net/http/server.go:L1739-1878
func (c *conn) serve(ctx context.Context) {
// ... 省略代码
serverHandler{c.server}.ServeHTTP(w, w.req)
w.cancelCtx()
if c.hijacked() {
return
}
w.finishRequest()
// ... 省略代码
}
// net/http/server.go:L2733-2742
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
// net/http/server.go:L2352-2362
func (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)
}
// net/http/server.go:L1963-1965
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
这基本是整个过程的代码了.
ln, err := net.Listen(“tcp”, addr)做了初试化了socket, bind, listen的操作.
rw, e := l.Accept()进行accept, 等待客户端进行连接.
go c.serve(ctx) 启动新的goroutine来处理本次请求. 同时主goroutine继续等待客户端连接, 进行高并发操作.
h, _ := mux.Handler(r) 获取注册的路由, 然后拿到这个路由的handler, 然后将处理结果返回给客户端