亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

一文詳解Go Http Server原理

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

從一個(gè) Demo 入手

俗話說(shuō)萬(wàn)事開頭難,但用 Go 實(shí)現(xiàn)一個(gè) Http Server 真不難,簡(jiǎn)單到什么程度?起一個(gè) Server,并且能響應(yīng)請(qǐng)求,算上包名、導(dǎo)入的依賴,甚至空行,也就只要 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")
}

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

Http Server 如何處理連接?

我們從這一行代碼看起

http.ListenAndServe(":81", nil)

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

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

http.HandleFunc("/hello", hello)

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

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

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

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

Http Server 如何處理請(qǐng)求的?

一些前置工作

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

go c.serve(connCtx)

這里的 connCtx 代入了當(dāng)前的 Server 對(duì)象:

ctx := context.WithValue(baseCtx, ServerContextKey, srv)
...
connCtx := ctx

而且還提供了修改它的 hook 方法 srv.ConnContext,可以在每次 Accept 時(shí)修改原始的 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 的,可以改成這樣來(lái)自定義:

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,可采取如上的方法設(shè)置,在每次連接狀態(tài)改變時(shí)執(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 方法到底干了什么,我們?cè)俸?jiǎn)化一下:

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

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

為什么是一個(gè)大循環(huán)呢?因?yàn)槊總€(gè) serve 處理的是一個(gè)連接,一個(gè)連接可以有多次請(qǐng)求。

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

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

for {
	w, err := c.readRequest(ctx)
	...
	if requestBodyRemains(req.Body) {
		registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
	} else {
		w.conn.r.startBackgroundRead()
	}
	...
}

請(qǐng)求如何路由?

當(dāng)讀取到一個(gè)請(qǐng)求后,便進(jìn)入這一行代碼:

serverHandler{c.server}.ServeHTTP(w, w.req)

ServeHTTP 找到我們注冊(cè)的 Handler 去處理,如果請(qǐng)求的URI 是 *或請(qǐng)求 Method 是 OPTIONS,則使用globalOptionsHandler,也就是說(shuō)這類請(qǐng)求不需要我們手動(dòng)處理,直接就返回了。

對(duì)于我們注冊(cè)的 Handler 也需要去尋找路由,這個(gè)路由的規(guī)則還是比較簡(jiǎn)單,主要由如下三條:

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

舉幾個(gè)例子來(lái)理解一下:

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

注冊(cè)路由為

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

此時(shí)如果執(zhí)行

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

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

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

就匹配的是 hello

  • 前綴匹配

如果注冊(cè)路由為

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

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

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

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

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

總結(jié)

最后我們回憶下 Go Http Server 的要點(diǎn):

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

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

相關(guān)文章

  • Go?panic的三種產(chǎn)生方式細(xì)節(jié)探究

    Go?panic的三種產(chǎn)生方式細(xì)節(jié)探究

    這篇文章主要介紹了Go?panic的三種產(chǎn)生方式細(xì)節(jié)探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • go語(yǔ)言讀取json并下載高清妹子圖片

    go語(yǔ)言讀取json并下載高清妹子圖片

    前面我們介紹了使用python下載高清妹子圖,作為程序猿,我們當(dāng)然不能只會(huì)一種語(yǔ)言,今天我們就來(lái)使用go語(yǔ)言來(lái)讀取API來(lái)下載妹子圖吧,有需要的宅男們可以參考下。
    2015-03-03
  • Go語(yǔ)言hello world實(shí)例

    Go語(yǔ)言hello world實(shí)例

    這篇文章主要介紹了Go語(yǔ)言hello world實(shí)例,本文先是給出了hello world的代碼實(shí)例,然后對(duì)一些知識(shí)點(diǎn)和技巧做了解釋,需要的朋友可以參考下
    2014-10-10
  • golang中為什么不存在三元運(yùn)算符詳解

    golang中為什么不存在三元運(yùn)算符詳解

    這篇文章主要給大家介紹了關(guān)于golang中為什么不存在三元運(yùn)算符的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • Go語(yǔ)言里的new函數(shù)用法分析

    Go語(yǔ)言里的new函數(shù)用法分析

    這篇文章主要介紹了Go語(yǔ)言里的new函數(shù)用法,實(shí)例分析了new函數(shù)的功能及使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • Go讀取文件與寫入文件的三種方法操作指南

    Go讀取文件與寫入文件的三種方法操作指南

    在 Go 語(yǔ)言中也經(jīng)常會(huì)遇到操作文件的需求,下面這篇文章主要給大家介紹了關(guān)于Go讀取文件與寫入文件的三種方法操作,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-09-09
  • 一文帶你了解Go語(yǔ)言中的I/O接口設(shè)計(jì)

    一文帶你了解Go語(yǔ)言中的I/O接口設(shè)計(jì)

    I/O?操作在編程中扮演著至關(guān)重要的角色,它涉及程序與外部世界之間的數(shù)據(jù)交換,下面我們就來(lái)簡(jiǎn)單了解一下Go語(yǔ)言中的?I/O?接口設(shè)計(jì)吧
    2023-06-06
  • go 原生http web 服務(wù)跨域restful api的寫法介紹

    go 原生http web 服務(wù)跨域restful api的寫法介紹

    這篇文章主要介紹了go 原生http web 服務(wù)跨域restful api的寫法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • golang強(qiáng)制類型轉(zhuǎn)換和類型斷言

    golang強(qiáng)制類型轉(zhuǎn)換和類型斷言

    這篇文章主要介紹了詳情介紹golang類型轉(zhuǎn)換問(wèn)題,分別由介紹類型斷言和類型轉(zhuǎn)換,這兩者都是不同的概念,下面文章圍繞類型斷言和類型轉(zhuǎn)換的相關(guān)資料展開文章的詳細(xì)內(nèi)容,需要的朋友可以參考以下
    2021-12-12
  • Go 1.21新增的slices包中切片函數(shù)用法詳解

    Go 1.21新增的slices包中切片函數(shù)用法詳解

    Go 1.21新增的 slices 包提供了很多和切片相關(guān)的函數(shù),可以用于任何類型的切片,本文通過(guò)代碼示例為大家介紹了部分切片函數(shù)的具體用法,感興趣的小伙伴可以了解一下
    2023-08-08

最新評(píng)論