<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
俗話說萬事開頭難,但用 Go 實現一個 Http Server 真不難,簡單到什麼程度?起一個 Server,並且能響應請求,算上包名、匯入的依賴,甚至空行,也就只要 15 行程式碼:
package main import ( "io" "net/http" ) func main() { http.HandleFunc("/hello", hello) http.ListenAndServe(":81", nil) } func hello(response http.ResponseWriter, request *http.Request) { io.WriteString(response, "hello world") }
這麼簡單,能與之一戰的恐怕只有 Python 了吧,而且 Go 還能編譯成可執行的二進位制檔案,你說牛啤不牛啤?
我們從這一行程式碼看起
http.ListenAndServe(":81", nil)
從命名來看,這個方法幹了兩件事,監聽並且服務,從方法的單一職責上來說,我覺得不ok,一個方法怎麼能幹兩件事?但這是大佬寫的程式碼,就很合理。
第一個引數Addr
是要監聽的地址和埠,第二個引數Handler
一般是nil
,它是真正的邏輯處理,但我們通常用第一行程式碼那樣來註冊處理器,這程式碼一看就感覺是把 path 對映到業務邏輯上,我們先大概瞭解,待會再來看它
http.HandleFunc("/hello", hello)
如果瞭解過一點網路程式設計基礎,就會知道作業系統提供了bind
、listen
、accept
這樣的系統呼叫,我們只要按順序發起呼叫,就能組合出一個 Server。
Go 也是利用這些系統呼叫,把他們都封裝在了ListenAndServe
中。
Listen
往下追究就是系統呼叫,所以我們重點看 Serve
:
把分支程式碼收起來,只看主幹,發現是一個 for 迴圈裡面在不停地 Accept,而這個 Accept 在沒有連線時是阻塞的,當有連線時,起一個新的協程來處理。
處理請求的一行程式碼是,可以看出是每個連線單開了一個協程處理:
go c.serve(connCtx)
這裡的 connCtx 代入了當前的 Server 物件:
ctx := context.WithValue(baseCtx, ServerContextKey, srv) ... connCtx := ctx
而且還提供了修改它的 hook 方法 srv.ConnContext
,可以在每次 Accept 時修改原始的 context
if cc := srv.ConnContext; cc != nil { connCtx = cc(connCtx, rw) if connCtx == nil { panic("ConnContext returned nil") } }
它的定義是:
// ConnContext optionally specifies a function that modifies // the context used for a new connection c. The provided ctx // is derived from the base context and has a ServerContextKey // value. ConnContext func(ctx context.Context, c net.Conn) context.Context
但是如果按照我開頭給的程式碼,你是沒法修改 srv.ConnContext
的,可以改成這樣來自定義:
func main() { http.HandleFunc("/hello", hello) server := http.Server{ Addr: ":81", ConnContext: func(ctx context.Context, c net.Conn) context.Context { return context.WithValue(ctx, "hello", "roshi") }, } server.ListenAndServe() }
同樣的 c.setState
也提供了 hook,可採取如上的方法設定,在每次連線狀態改變時執行 hook 方法:
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
// ConnState specifies an optional callback function that is // called when a client connection changes state. See the // ConnState type and associated constants for details. ConnState func(net.Conn, ConnState)
為了能看清楚 Accept 後,serve 方法到底幹了什麼,我們再簡化一下:
func (c *conn) serve(ctx context.Context) { ... for { w, err := c.readRequest(ctx) ... serverHandler{c.server}.ServeHTTP(w, w.req) ... } }
serve 也是一個大回圈,迴圈裡面主要是讀取一個請求,然後將請求交給 Handler 處理。
為什麼是一個大回圈呢?因為每個 serve 處理的是一個連線,一個連線可以有多次請求。
讀請求就顯得比較枯燥乏味,按照Http協定,讀出URL,header,body等資訊。
這裡有個細節是在每次讀取了一個請求後,還開了一個協程去讀下一個請求,也算是做了優化吧。
for { w, err := c.readRequest(ctx) ... if requestBodyRemains(req.Body) { registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead) } else { w.conn.r.startBackgroundRead() } ... }
當讀取到一個請求後,便進入這一行程式碼:
serverHandler{c.server}.ServeHTTP(w, w.req)
ServeHTTP 找到我們註冊的 Handler 去處理,如果請求的URI 是 *
或請求 Method 是 OPTIONS
,則使用globalOptionsHandler,也就是說這類請求不需要我們手動處理,直接就返回了。
對於我們註冊的 Handler 也需要去尋找路由,這個路由的規則還是比較簡單,主要由如下三條:
/
,則除了完全匹配外,還會以字首查詢舉幾個例子來理解一下:
註冊路由為
http.HandleFunc("/hello", hello) http.HandleFunc("127.0.0.1/hello", hello2)
此時如果執行
curl 'http://127.0.0.1:81/hello'
則會匹配到 hello2,但如果執行
curl 'http://localhost:81/hello'
就匹配的是 hello
如果註冊路由為
http.HandleFunc("/hello", hello) http.HandleFunc("127.0.0.1/hello/", hello2)
注意第二個最後還有個/
,此時如果執行
curl 'http://127.0.0.1:81/hello/roshi'
也能匹配到 hello2,怎麼樣,是不是理解了?
找到路由之後就直接呼叫我們開頭註冊的方法,如果我們往 Response 中寫入資料,就能返回給使用者端,這樣一個請求就處理完成了。
最後我們回憶下 Go Http Server 的要點:
以上就是一文詳解Go Http Server原理的詳細內容,更多關於Go Http Server原理的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45