最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文詳解Go Http Server原理

 更新時間:2023年01月13日 12:02:57   作者:捉蟲大師  
這篇文章主要為大家介紹了Go Http Server原理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

從一個 Demo 入手

俗話說萬事開頭難,但用 Go 實現(xiàn)一個 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")
}

這么簡單,能與之一戰(zhàn)的恐怕只有 Python 了吧,而且 Go 還能編譯成可執(zhí)行的二進制文件,你說牛啤不牛???

Http Server 如何處理連接?

我們從這一行代碼看起

http.ListenAndServe(":81", nil)

從命名來看,這個方法干了兩件事,監(jiān)聽并且服務,從方法的單一職責上來說,我覺得不ok,一個方法怎么能干兩件事?但這是大佬寫的代碼,就很合理。

第一個參數(shù)Addr是要監(jiān)聽的地址和端口,第二個參數(shù)Handler一般是nil,它是真正的邏輯處理,但我們通常用第一行代碼那樣來注冊處理器,這代碼一看就感覺是把 path 映射到業(yè)務邏輯上,我們先大概了解,待會再來看它

http.HandleFunc("/hello", hello)

如果了解過一點網(wǎng)絡編程基礎(chǔ),就會知道操作系統(tǒng)提供了bind、listenaccept這樣的系統(tǒng)調(diào)用,我們只要按順序發(fā)起調(diào)用,就能組合出一個 Server。

Go 也是利用這些系統(tǒng)調(diào)用,把他們都封裝在了ListenAndServe中。

Listen 往下追究就是系統(tǒng)調(diào)用,所以我們重點看 Serve

把分支代碼收起來,只看主干,發(fā)現(xiàn)是一個 for 循環(huán)里面在不停地 Accept,而這個 Accept 在沒有連接時是阻塞的,當有連接時,起一個新的協(xié)程來處理。

Http Server 如何處理請求的?

一些前置工作

處理請求的一行代碼是,可以看出是每個連接單開了一個協(xié)程處理:

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,可采取如上的方法設置,在每次連接狀態(tài)改變時執(zhí)行 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)

serve 方法到底干了什么

為了能看清楚 Accept 后,serve 方法到底干了什么,我們再簡化一下:

func (c *conn) serve(ctx context.Context) {
	...
	for {
		w, err := c.readRequest(ctx)
		...
		serverHandler{c.server}.ServeHTTP(w, w.req)
		...
	}
}

serve 也是一個大循環(huán),循環(huán)里面主要是讀取一個請求,然后將請求交給 Handler 處理。

為什么是一個大循環(huán)呢?因為每個 serve 處理的是一個連接,一個連接可以有多次請求。

讀請求就顯得比較枯燥乏味,按照Http協(xié)議,讀出URL,header,body等信息。

這里有個細節(jié)是在每次讀取了一個請求后,還開了一個協(xié)程去讀下一個請求,也算是做了優(yōu)化吧。

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 也需要去尋找路由,這個路由的規(guī)則還是比較簡單,主要由如下三條:

  • 如果注冊了帶 host 的路由,則按 host + path 去尋找,如果沒注冊帶 host 的路由,則按 path 尋找
  • 路由規(guī)則匹配以完全匹配優(yōu)先,如果注冊的路由規(guī)則最后一個字符是/,則除了完全匹配外,還會以前綴查找

舉幾個例子來理解一下:

  • 帶 host 的匹配規(guī)則

注冊路由為

http.HandleFunc("/hello", hello)
http.HandleFunc("127.0.0.1/hello", hello2)

此時如果執(zhí)行

curl 'http://127.0.0.1:81/hello'

則會匹配到 hello2,但如果執(zhí)行

curl 'http://localhost:81/hello'

就匹配的是 hello

  • 前綴匹配

如果注冊路由為

http.HandleFunc("/hello", hello)
http.HandleFunc("127.0.0.1/hello/", hello2)

注意第二個最后還有個/,此時如果執(zhí)行

curl 'http://127.0.0.1:81/hello/roshi'

也能匹配到 hello2,怎么樣,是不是理解了?

找到路由之后就直接調(diào)用我們開頭注冊的方法,如果我們往 Response 中寫入數(shù)據(jù),就能返回給客戶端,這樣一個請求就處理完成了。

總結(jié)

最后我們回憶下 Go Http Server 的要點:

  • 用 Go 起一個 Http Server 非常簡單
  • Go Http Server 本質(zhì)是一個大循環(huán),每當有一個新連接時,會起一個新的協(xié)程來處理
  • 每個連接的處理也是一個大循環(huán),這個循環(huán)里做了讀取請求、尋找路由、執(zhí)行邏輯三件大事

以上就是一文詳解Go Http Server原理的詳細內(nèi)容,更多關(guān)于Go Http Server原理的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go語言使用MongoDB數(shù)據(jù)庫詳細步驟

    Go語言使用MongoDB數(shù)據(jù)庫詳細步驟

    mongodb是一種高性能、開源、文檔型的nosql數(shù)據(jù)庫,被廣泛應用于web應用、大數(shù)據(jù)以及云計算領(lǐng)域,下面這篇文章主要給大家介紹了關(guān)于Go語言使用MongoDB數(shù)據(jù)庫的詳細步驟,需要的朋友可以參考下
    2024-05-05
  • Go語言接口的嵌套的具體使用

    Go語言接口的嵌套的具體使用

    在Go語言中,不僅結(jié)構(gòu)體與結(jié)構(gòu)體之間可以嵌套,接口與接口間也可以通過嵌套創(chuàng)造出新的接口,本文主要介紹了Go語言接口的嵌套的具體使用,感興趣的可以了解一下
    2023-04-04
  • 使用Go Validator有效驗證數(shù)據(jù)示例分析

    使用Go Validator有效驗證數(shù)據(jù)示例分析

    作為一名開發(fā)者,確保Go應用中處理的數(shù)據(jù)是有效和準確的非常重要,Go Validator是一個開源的數(shù)據(jù)驗證庫,為Go結(jié)構(gòu)體提供強大且易于使用的數(shù)據(jù)驗證功能,本篇文章將介紹Go Validator庫的主要特點以及如何在Go應用中使用它來有效驗證數(shù)據(jù)
    2023-12-12
  • Golang中rune和byte的使用與區(qū)別

    Golang中rune和byte的使用與區(qū)別

    rune和byte都是Go語言中表示單個字符的類型,本文就來介紹一下Golang中rune和byte的使用與區(qū)別,具有一定的參考價值,感興趣的可以了解一下
    2025-02-02
  • GO語言實現(xiàn)列出目錄和遍歷目錄的方法

    GO語言實現(xiàn)列出目錄和遍歷目錄的方法

    這篇文章主要介紹了GO語言實現(xiàn)列出目錄和遍歷目錄的方法,涉及ioutil.ReadDir()與filepath.Walk()的應用,是非常實用的技巧,需要的朋友可以參考下
    2014-12-12
  • Golang使用反射的動態(tài)方法調(diào)用詳解

    Golang使用反射的動態(tài)方法調(diào)用詳解

    Go是一種靜態(tài)類型的語言,提供了大量的安全性和性能。這篇文章主要和大家介紹一下Golang使用反射的動態(tài)方法調(diào)用,感興趣的小伙伴可以了解一下
    2023-03-03
  • golang連接池檢查連接失敗時如何重試(示例代碼)

    golang連接池檢查連接失敗時如何重試(示例代碼)

    在Go中,可以通過使用database/sql包的DB類型的Ping方法來檢查數(shù)據(jù)庫連接的可用性,本文通過示例代碼,演示了如何在連接檢查失敗時進行重試,感興趣的朋友一起看看吧
    2023-10-10
  • go字符串拼接方式及性能比拼小結(jié)

    go字符串拼接方式及性能比拼小結(jié)

    在golang中字符串的拼接方式有多種,本文將會介紹比較常用的幾種方式,并且對各種方式進行壓測,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • 入門學習Go的基本語法

    入門學習Go的基本語法

    這篇文章主要介紹了Go語法的基本概念,本文講解了Golang的基本知識、基礎(chǔ)語法、相關(guān)術(shù)語等,需要的朋友可以參考下,希望能幫助到你
    2021-07-07
  • 自定義Go?Json的序列化方法譯文

    自定義Go?Json的序列化方法譯文

    這篇文章主要為大家介紹了自定義Go?Json序列化方法譯文,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-06-06

最新評論

仲巴县| 四会市| 东源县| 厦门市| 天镇县| 循化| 遂川县| SHOW| 洪雅县| 城口县| 叶城县| 宣恩县| 贵州省| 板桥市| 德阳市| 睢宁县| 克什克腾旗| 桦川县| 西林县| 钟山县| 东海县| 会同县| 庆元县| 磐石市| 西贡区| 大丰市| 和硕县| 慈利县| 仙居县| 汽车| 大英县| 洪江市| 于田县| 怀宁县| 曲周县| 宜昌市| 麻城市| 慈利县| 房山区| 郴州市| 西吉县|