自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Gin 源碼閱讀之 Gin 與 Net/Http 的關系

開發(fā) 后端
net/http基本已經提供http服務的70%的功能, 那些號稱賊快的go框架, 基本上都是提供一些功能, 讓我們能夠更好的處理客戶端發(fā)來的請求. 如果你有興趣的話,也可以基于 net/http 做一個 Go 框架出來。

[[422809]]

本文轉載自微信公眾號「HHFCodeRv」,作者haohongfan。轉載本文請聯(lián)系HHFCodeRv公眾號。

gin 是目前 Go 里面使用最廣泛的框架之一了,弄清楚 gin 框架的原理,有助于我們更好的使用 gin。這個系列 gin 源碼閱讀會逐步講明白 gin 的原理,歡迎關注后續(xù)文章。

gin 概覽

想弄清楚 gin, 需要弄明白以下幾個問題:

  • request數據是如何流轉的
  • gin框架到底扮演了什么角色
  • 請求從gin流入net/http, 最后又是如何回到gin中
  • gin的context為何能承擔起來復雜的需求
  • gin的路由算法
  • gin的中間件是什么
  • gin的Engine具體是個什么東西
  • net/http的requeset, response都提供了哪些有用的東西

從gin的官方第一個demo入手.

  1. package main 
  2.  
  3. import "github.com/gin-gonic/gin" 
  4.  
  5. func main() { 
  6.     r := gin.Default() 
  7.     r.GET("/ping", func(c *gin.Context) { 
  8.         c.JSON(200, gin.H{ 
  9.           "message""pong"
  10.         }) 
  11.     }) 
  12.     r.Run() // listen and serve on 0.0.0.0:8080 

r.Run() 的源碼:

  1. func (engine *Engine) Run(addr ...string) (err error) { 
  2.     defer func() { debugPrintError(err) }() 
  3.  
  4.     address := resolveAddress(addr) 
  5.     debugPrint("Listening and serving HTTP on %s\n", address) 
  6.     err = http.ListenAndServe(address, engine) 
  7.     return 

看到開始調用的是 http.ListenAndServe(address, engine), 這個函數是net/http的函數, 然后請求數據就在net/http開始流轉.

Request 數據是如何流轉的

先不使用gin, 直接使用net/http來處理http請求

  1. func main() { 
  2.     http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 
  3.         w.Write([]byte("Hello World")) 
  4.     }) 
  5.  
  6.     if err := http.ListenAndServe(":8000", nil); err != nil { 
  7.         fmt.Println("start http server fail:", err) 
  8.     } 

在瀏覽器中輸入localhost:8000, 會看到Hello World. 下面利用這個簡單demo看下request的流轉流程.

HTTP是如何建立起來的

簡單的說一下http請求是如何建立起來的(需要有基本的網絡基礎, 可以找相關的書籍查看, 推薦看UNIX網絡編程卷1:套接字聯(lián)網API)

TCP/IP 五層模型

socket建立過程

在TCP/IP五層模型下, HTTP位于應用層, 需要有傳輸層來承載HTTP協(xié)議. 傳輸層比較常見的協(xié)議是TCP,UDP, SCTP等. 由于UDP不可靠, SCTP有自己特殊的運用場景, 所以一般情況下HTTP是由TCP協(xié)議承載的(可以使用wireshark抓包然后查看各層協(xié)議)

使用TCP協(xié)議的話, 就會涉及到TCP是如何建立起來的. 面試中能夠常遇到的名詞三次握手, 四次揮手就是在這里產生的. 具體的建立流程就不在陳述了, 大概流程就是圖中左半邊

所以說, 要想能夠對客戶端http請求進行回應的話, 就首先需要建立起來TCP連接, 也就是socket. 下面要看下net/http是如何建立起來socket?

net/http 是如何建立 socket 的

從圖上可以看出, 不管server代碼如何封裝, 都離不開bind,listen,accept這些函數. 就從上面這個簡單的demo入手查看源碼.

  1. func main() { 
  2.     http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 
  3.         w.Write([]byte("Hello World")) 
  4.     }) 
  5.  
  6.     if err := http.ListenAndServe(":8000", nil); err != nil { 
  7.         fmt.Println("start http server fail:", err) 
  8.     } 

注冊路由

  1. http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 
  2.     w.Write([]byte("Hello World")) 
  3. }) 

這段代碼是在注冊一個路由及這個路由的handler到DefaultServeMux中

  1. // server.go:L2366-2388 
  2. func (mux *ServeMux) Handle(pattern string, handler Handler) { 
  3.     mux.mu.Lock() 
  4.     defer mux.mu.Unlock() 
  5.  
  6.     if pattern == "" { 
  7.         panic("http: invalid pattern"
  8.     } 
  9.     if handler == nil { 
  10.         panic("http: nil handler"
  11.     } 
  12.     if _, exist := mux.m[pattern]; exist { 
  13.         panic("http: multiple registrations for " + pattern) 
  14.     } 
  15.  
  16.     if mux.m == nil { 
  17.         mux.m = make(map[string]muxEntry) 
  18.     } 
  19.     mux.m[pattern] = muxEntry{h: handler, pattern: pattern} 
  20.  
  21.     if pattern[0] != '/' { 
  22.         mux.hosts = true 
  23.     } 

可以看到這個路由注冊太過簡單了, 也就給gin, iris, echo等框架留下了擴展的空間, 后面詳細說這個東西

服務監(jiān)聽及響應

上面路由已經注冊到net/http了, 下面就該如何建立socket了, 以及最后又如何取到已經注冊到的路由, 將正確的響應信息從handler中取出來返回給客戶端

1.創(chuàng)建 socket

  1. if err := http.ListenAndServe(":8000", nil); err != nil { 
  2.     fmt.Println("start http server fail:", err) 
  1. // net/http/server.go:L3002-3005 
  2. func ListenAndServe(addr string, handler Handler) error { 
  3.     server := &Server{Addr: addr, Handler: handler} 
  4.     return server.ListenAndServe() 
  1. // net/http/server.go:L2752-2765 
  2. func (srv *Server) ListenAndServe() error { 
  3.     // ... 省略代碼 
  4.     ln, err := net.Listen("tcp", addr) // <-----看這里listen 
  5.     if err != nil { 
  6.       return err 
  7.     } 
  8.     return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) 

2.Aaccept 等待客戶端鏈接

  1. // net/http/server.go:L2805-2853 
  2. func (srv *Server) Serve(l net.Listener) error { 
  3.     // ... 省略代碼 
  4.     for { 
  5.       rw, e := l.Accept() // <----- 看這里accept 
  6.       if e != nil { 
  7.         select { 
  8.         case <-srv.getDoneChan(): 
  9.           return ErrServerClosed 
  10.         default
  11.         } 
  12.         if ne, ok := e.(net.Error); ok && ne.Temporary() { 
  13.           if tempDelay == 0 { 
  14.             tempDelay = 5 * time.Millisecond 
  15.           } else { 
  16.             tempDelay *= 2 
  17.           } 
  18.           if max := 1 * time.Second; tempDelay > max { 
  19.             tempDelay = max 
  20.           } 
  21.           srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay) 
  22.           time.Sleep(tempDelay) 
  23.           continue 
  24.         } 
  25.         return e 
  26.       } 
  27.       tempDelay = 0 
  28.       c := srv.newConn(rw) 
  29.       c.setState(c.rwc, StateNew) // before Serve can return 
  30.       go c.serve(ctx) // <--- 看這里 
  31.     } 

3. 提供回調接口 ServeHTTP

  1. // net/http/server.go:L1739-1878 
  2. func (c *conn) serve(ctx context.Context) { 
  3.     // ... 省略代碼 
  4.     serverHandler{c.server}.ServeHTTP(w, w.req) 
  5.     w.cancelCtx() 
  6.     if c.hijacked() { 
  7.       return 
  8.     } 
  9.     w.finishRequest() 
  10.     // ... 省略代碼 
  1. // net/http/server.go:L2733-2742 
  2. func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { 
  3.     handler := sh.srv.Handler 
  4.     if handler == nil { 
  5.       handler = DefaultServeMux 
  6.     } 
  7.     if req.RequestURI == "*" && req.Method == "OPTIONS" { 
  8.       handler = globalOptionsHandler{} 
  9.     } 
  10.     handler.ServeHTTP(rw, req) 
  1. // net/http/server.go:L2352-2362 
  2. func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { 
  3.     if r.RequestURI == "*" { 
  4.       if r.ProtoAtLeast(1, 1) { 
  5.         w.Header().Set("Connection""close"
  6.       } 
  7.       w.WriteHeader(StatusBadRequest) 
  8.       return 
  9.     } 
  10.     h, _ := mux.Handler(r) // <--- 看這里 
  11.     h.ServeHTTP(w, r) 

4. 回調到實際要執(zhí)行的 ServeHTTP

  1. // net/http/server.go:L1963-1965 
  2. func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { 
  3.    f(w, r) 

這基本是整個過程的代碼了.

  1. ln, err := net.Listen("tcp", addr)做了初試化了socket, bind, listen的操作.
  2. rw, e := l.Accept()進行accept, 等待客戶端進行連接
  3. go c.serve(ctx) 啟動新的goroutine來處理本次請求. 同時主goroutine繼續(xù)等待客戶端連接, 進行高并發(fā)操作
  4. h, _ := mux.Handler(r) 獲取注冊的路由, 然后拿到這個路由的handler, 然后將處理結果返回給客戶端

從這里也能夠看出來, net/http基本上提供了全套的服務.

為什么會出現很多go框架

  1. // net/http/server.go:L2218-2238 
  2. func (mux *ServeMux) match(path string) (h Handler, pattern string) { 
  3.     // Check for exact match first
  4.     v, ok := mux.m[path] 
  5.     if ok { 
  6.         return v.h, v.pattern 
  7.     } 
  8.  
  9.     // Check for longest valid match. 
  10.     var n = 0 
  11.     for k, v := range mux.m { 
  12.       if !pathMatch(k, path) { 
  13.           continue 
  14.       } 
  15.       if h == nil || len(k) > n { 
  16.           n = len(k) 
  17.           h = v.h 
  18.           pattern = v.pattern 
  19.       } 
  20.     } 
  21.     return 

從這段函數可以看出來, 匹配規(guī)則過于簡單, 當能匹配到路由的時候就返回其對應的handler, 當不能匹配到時就返回/. net/http的路由匹配根本就不符合 RESTful 的規(guī)則,遇到稍微復雜一點的需求時,這個簡單的路由匹配規(guī)則簡直就是噩夢。

所以基本所有的go框架干的最主要的一件事情就是重寫net/http的route。我們直接說 gin就是一個 httprouter 也不過分, 當然gin也提供了其他比較主要的功能, 后面會一一介紹。

綜述, net/http基本已經提供http服務的70%的功能, 那些號稱賊快的go框架, 基本上都是提供一些功能, 讓我們能夠更好的處理客戶端發(fā)來的請求. 如果你有興趣的話,也可以基于 net/http 做一個 Go 框架出來。

 

責任編輯:武曉燕 來源: HHFCodeRv
相關推薦

2025-03-31 01:30:00

Gin框架模型

2024-11-11 10:09:23

2024-02-19 07:40:10

2024-03-05 07:55:41

框架GINGo

2023-03-06 08:37:58

JavaNIO

2024-12-09 00:00:15

Gin框架中間件

2021-04-02 08:02:10

Gin集成Casbin開源

2024-07-29 00:01:00

2023-05-28 13:03:46

BeegoGin設計

2024-11-25 08:14:09

Gin框架格式

2024-01-15 06:34:09

Gin鏡像容器

2024-12-24 10:50:05

GinWeb開發(fā)

2024-02-02 10:40:40

2024-11-11 00:45:54

Gin框架字段

2018-07-30 16:31:00

javascriptaxioshttp

2023-11-17 12:11:26

GORMGo Web

2024-01-30 12:08:31

Go框架停止服務

2024-11-18 09:18:21

Gin框架驗證器

2022-02-10 23:02:25

Go序列化Post

2022-12-26 00:00:01

Go框架前端
點贊
收藏

51CTO技術棧公眾號