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

Go標(biāo)準(zhǔn)庫(kù)http?server優(yōu)雅啟動(dòng)深入理解

 更新時(shí)間:2024年01月15日 10:52:33   作者:涼涼的知識(shí)庫(kù)  
這篇文章主要介紹了Go標(biāo)準(zhǔn)庫(kù)http?server優(yōu)雅啟動(dòng)深入理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

如何用最少的代碼創(chuàng)建一個(gè)HTTP server?

package main

import (
 "net"
 "net/http"
)

func main() {
 // 方式1
 err := http.ListenAndServe(":8080", nil)
 if err != nil {
   panic(err)
 }
}

點(diǎn)開(kāi)http.ListenAndServe可以看到函數(shù)只是創(chuàng)建了Server類型并調(diào)用server.ListenAndServe()

所以下面的和上面的代碼沒(méi)有區(qū)別

package main

import (
 "net"
 "net/http"
)

func main() {
 // 方式2
 server := &http.Server{Addr: ":8080"}
 err := server.ListenAndServe()
 if err != nil {
  panic(err)
 }
}

ListenAndServe()如其名會(huì)干兩件事

  • 監(jiān)聽(tīng)一個(gè)端口,即Listen的過(guò)程

  • 處理進(jìn)入端口的連接,即Serve的過(guò)程

所以下面的代碼和上面的代碼也沒(méi)區(qū)別

package main

import (
 "net"
 "net/http"
)

func main() {
 // 方式3
 ln, err := net.Listen("tcp", ":8080")
 if err != nil {
  panic(err)
 }

 server := &http.Server{}
 err = server.Serve(ln)
 if err != nil {
  panic(err)
 }
}

一張圖展示三種使用方式

路由?no!Handler!

按上面的代碼啟動(dòng)HTTP Server沒(méi)有太大意義,因?yàn)槲覀冞€沒(méi)有設(shè)定路由,所以無(wú)法正常響應(yīng)請(qǐng)求

$ curl  127.0.0.1:8080 
404 page not found

暫停思考一下,服務(wù)器返回404是因?yàn)闆](méi)有設(shè)定路由么?no,no,no,你需要轉(zhuǎn)變一下思維。服務(wù)器返回404不是因?yàn)槲覀儧](méi)有設(shè)置路由,而是因?yàn)闆](méi)有設(shè)置請(qǐng)求的處理程序,這個(gè)處理程序在Go中叫作:Handler!

type Handler interface {
 ServeHTTP(ResponseWriter, *Request)
}

?? 怎么定義請(qǐng)求的處理程序?

由上可知,僅需要實(shí)現(xiàn)ServeHTTP(ResponseWriter, *Request)接口即可

注意,示例代碼沒(méi)有判斷任何路由(PATH)

type handlerImp struct {
}
func (imp handlerImp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 if r.Method == "GET" {
  w.Write([]byte("Receive GET request"))
  return
 }
 if r.Method == "POST" {
  w.Write([]byte("Receive POST request"))
  return
 }
 return
}

?? 怎么設(shè)置請(qǐng)求的處理程序?

三種方式本質(zhì)上都是把自定義的Handler賦值到ServerHandler屬性中

func main() {
 // 方式1
 // err := http.ListenAndServe(":8080", handlerImp{})
 // if err != nil {
 //  panic(err)
 // }
 // 方式2
 // server := &http.Server{Addr: ":8080", Handler: handlerImp{}}
 // err := server.ListenAndServe()
 // if err != nil {
 //  panic(err)
 // }
 // 方式3
 ln, err := net.Listen("tcp", ":8080")
 if err != nil {
  panic(err)
 }
 server := &http.Server{Handler:handlerImp{}}
 err = server.Serve(ln)
 if err != nil {
  panic(err)
 }
}

?? 設(shè)置請(qǐng)求的處理程序之后的效果

handlerImp只針對(duì)Method做了不同的響應(yīng),沒(méi)有對(duì)PATH做任何的判斷,所以無(wú)論請(qǐng)求什么樣的路徑都能拿到一個(gè)預(yù)期的響應(yīng)。

$ curl  -X POST 127.0.0.1:8080/foo
Receive POST request%  

$ curl  127.0.0.1:8080/foo/bar 
Receive GET request%  

此時(shí)再體會(huì)一下這句話:我們?cè)O(shè)置的不是路由,而是設(shè)置請(qǐng)求的處理程序

再聊Handler

type handlerImp struct {
}
func (imp handlerImp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 if r.Method == "GET" {
  w.Write([]byte("Receive GET request"))
  return
 }
 if r.Method == "POST" {
  w.Write([]byte("Receive POST request"))
  return
 }
 return
}

如上所述,無(wú)論任何PATH,任何Method等,所有的請(qǐng)求都會(huì)被handlerImp.ServeHTTP處理。

我們可以判斷PATH、Method等,根據(jù)不同的請(qǐng)求特征執(zhí)行不同的邏輯,并且全部在這一個(gè)函數(shù)中全部完成

很明顯,這違反了高內(nèi)聚,低耦合的編程范式

停下來(lái)思考下,如何編寫(xiě)一個(gè)高內(nèi)聚,低耦合的handlerImp.ServeHTTP,使之針對(duì)不同HTTP請(qǐng)求執(zhí)行不同的邏輯呢

type handlerImp struct {
}
func (imp handlerImp) handleMethodGet(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte("Receive GET request"))
 return
}
func (imp handlerImp) handleMethodPost(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte("Receive POST request"))
 return
}
func (imp handlerImp) handlePathFoo(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte("Receive path foo"))
 return
}
func (imp handlerImp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 if r.URL.Path == "/foo" {
  imp.handlePathFoo(w, r)
  return
 }
 if r.Method == "GET" {
  imp.handleMethodGet(w, r)
  return
 }
 if r.Method == "POST" {
  imp.handleMethodPost(w, r)
  return
 }
 return
}

如果你的答案和上面的代碼類似,那么我對(duì)于這段代碼的點(diǎn)評(píng)是:不太高明??

?? 如何編寫(xiě)一個(gè)高內(nèi)聚,低耦合的ServeHTTP,針對(duì)不同HTTP請(qǐng)求執(zhí)行不同的邏輯?

不知道你有沒(méi)有聽(tīng)過(guò)設(shè)計(jì)模式中,組合模式。沒(méi)有了解可以去了解下,或者看下圖

經(jīng)過(guò)組合模式重新設(shè)計(jì)的handlerImp,已經(jīng)不再包含具體的邏輯了,它先搜索有沒(méi)有針對(duì)PATH處理的邏輯,再搜索有沒(méi)有針對(duì)Method處理的邏輯,它專注于邏輯分派,它是組合模式中的容器。

容器(Container):容器接收到請(qǐng)求后會(huì)將工作分配給自己的子項(xiàng)目, 處理中間結(jié)果, 然后將最終結(jié)果返回給客戶端。

type handlerImp struct {
 pathHandlers   map[string]http.Handler
 methodHandlers map[string]http.Handler
}
func NewHandlerImp() handlerImp {
 return handlerImp{
  pathHandlers:   make(map[string]http.Handler),
  methodHandlers: make(map[string]http.Handler),
 }
}
func (imp handlerImp) AddPathHandler(path string, h http.Handler) {
 imp.pathHandlers[path] = h
}
func (imp handlerImp) AddMethodHandler(method string, h http.Handler) {
 imp.methodHandlers[method] = h
}
func (imp handlerImp) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 if h, ok := imp.pathHandlers[r.URL.Path]; ok {
  h.ServeHTTP(w, r)
  return
 }
 if h, ok := imp.methodHandlers[r.Method]; ok {
  h.ServeHTTP(w, r)
  return
 }
 return
}

重新設(shè)計(jì)的handlerImp不執(zhí)行邏輯,實(shí)際的邏輯被分離到每一個(gè)葉子結(jié)點(diǎn)中,而每一個(gè)葉子結(jié)點(diǎn)也都實(shí)現(xiàn)了ServeHTTP函數(shù),即Handler接口

type PathFoo struct {
}
func (m PathFoo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte("Receive path foo"))
 return
}
type MethodGet struct {
}
func (m MethodGet) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte("Receive GET request"))
 return
}
type MethodPost struct {
}
func (m MethodPost) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte("Receive POST request"))
 return
}

再次強(qiáng)調(diào),通過(guò)對(duì)組合模式的運(yùn)用,我們把邏輯分派的功能聚合到handlerImp,把具體的邏輯聚合到PathFoo、MethodGetMethodPost

func main() {
 // 方式3
 ln, err := net.Listen("tcp", ":8080")
 if err != nil {
  panic(err)
 }

 h := NewHandlerImp()
 h.AddMethodHandler("GET", MethodGet{})
 h.AddMethodHandler("POST", MethodPost{})
 h.AddPathHandler("/foo", PathFoo{})

 server := &http.Server{Handler: h}
 err = server.Serve(ln)
 if err != nil {
  panic(err)
 }
}

一些Handlers

上面實(shí)現(xiàn)的handlerImp利用組合設(shè)計(jì)模式,已經(jīng)能針對(duì)Path和Method設(shè)定和處理不同的邏輯,但整體功能略顯簡(jiǎn)單。有哪些可以供我們使用且功能強(qiáng)大的Handlers呢?

http.ServeMux

Go標(biāo)準(zhǔn)庫(kù)中就提供了一個(gè)Handler實(shí)現(xiàn)叫作http.ServeMux

?? 當(dāng)前(go1.21.*)版本僅支持匹配Path,但目前已經(jīng)在討論支持Method匹配和占位符了:net/http: add methods and path variables to ServeMux patterns #60227[1]

使用的方式如下

http.ServeMux提供兩個(gè)函數(shù)用于注冊(cè)不同Path的處理函數(shù)

  • ServeMux.Handle 接收的是Handler接口實(shí)現(xiàn)

  • ServeMux.HandleFunc 接收的是匿名函數(shù)

type PathBar struct {
}

func (m PathBar) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte("Receive path bar"))
 return
}

func main() {
 // 方式3
 ln, err := net.Listen("tcp", ":8080")
 if err != nil {
  panic(err)
 }

 mx := http.NewServeMux()
 mx.Handle("/bar/", PathBar{})
 mx.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("Receive path foo"))
 })

 server := &http.Server{Handler: mx}
 err = server.Serve(ln)
 if err != nil {
  panic(err)
 }
}

代碼mx.Handle("/bar/", PathBar{})/bar//結(jié)尾,所以它可以匹配/bar/*所有的Path

關(guān)于http.ServeMux的細(xì)節(jié)不是本篇重點(diǎn),后續(xù)會(huì)單獨(dú)介紹

?? 默認(rèn)的Handler

因?yàn)槭菢?biāo)準(zhǔn)庫(kù)內(nèi)置的實(shí)現(xiàn),當(dāng)沒(méi)有設(shè)置http.Server.Handler屬性時(shí),http.Server就會(huì)使用一個(gè)全局的變量DefaultServeMux *ServeMux來(lái)作為http.Server.Handler的值

var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux

http包同時(shí)提供了兩個(gè)函數(shù)可以在DefaultServeMux注冊(cè)不同Path的處理函數(shù)

func main() {
 http.Handle("/bar/", PathBar{})
 http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("Receive path foo"))
 })

 // 方式1
 err := http.ListenAndServe(":8080", nil)
 if err != nil {
  panic(err)
 }
}

http.Handle 接收的是Handler接口實(shí)現(xiàn),對(duì)應(yīng)的是

func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

http.HandleFunc 接收的是匿名函數(shù),對(duì)應(yīng)的是

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
 DefaultServeMux.HandleFunc(pattern, handler)
}

gorilla/mux

gorilla/mux是一個(gè)相當(dāng)流行的第三方庫(kù),用法這里簡(jiǎn)單寫(xiě)下

除了經(jīng)典的Handle、HandleFunc函數(shù),gorilla/mux還提供了Methods、Schemes、Host等非常復(fù)雜的功能

但無(wú)論多復(fù)雜,其一定包含了ServeHTTP函數(shù),即實(shí)現(xiàn)了Handler接口

func main() {
 r := mux.NewRouter()
    r.Handle("/foo/{bar}", PathBar{})
 r.Handle("/bar/", PathBar{})
 r.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("Receive path foo"))
 })
 r.Methods("GET").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("Receive GET request"))
 })

 // 方式1
 err := http.ListenAndServe(":8080", r)
 if err != nil {
  panic(err)
 }
}

其他

還有很多其他優(yōu)秀的mux實(shí)現(xiàn),具體可以參考各自的官方文檔。

https://github.com/go-chi/chi  star 15.9k

https://github.com/julienschmidt/httprouter  star 15.6k

關(guān)于Go標(biāo)準(zhǔn)庫(kù)、第三方庫(kù)中這些結(jié)構(gòu)的關(guān)系通過(guò)下圖展示

再聊組合模式

無(wú)論是官方的http.ServeMux,還是一些第三方庫(kù),實(shí)現(xiàn)上大多使用了組合設(shè)計(jì)模式

組合模式的魔力還不止于此。思考一下這個(gè)場(chǎng)景:目前已經(jīng)存在路由servemux/*,并且使用了ServeMux

mx := http.NewServeMux()
mx.Handle("/servemux/bar/", PathBar{})
mx.HandleFunc("/servemux/foo", func(w http.ResponseWriter, r *http.Request) {
 w.Write([]byte("Receive servemux path foo"))
})

但此時(shí)還有另外一組路由/gorilla/*,使用了開(kāi)源庫(kù)gorilla/mux

r := mux.NewRouter()
r.Handle("/gorilla/bar/", PathBar{})
r.HandleFunc("/gorilla/foo", func(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Receive gorilla path foo"))
})

如何啟動(dòng)這樣的服務(wù)器呢?

func main() {
 mx := http.NewServeMux()
 mx.Handle("/servemux/bar/", PathBar{})
 mx.HandleFunc("/servemux/foo", func(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("Receive servemux path foo"))
 })
 r := mux.NewRouter()
 r.Handle("/gorilla/bar/", PathBar{})
 r.HandleFunc("/gorilla/foo", func(w http.ResponseWriter, r *http.Request) {
  w.Write([]byte("Receive gorilla path foo"))
 })
 h := http.NewServeMux()
 h.Handle("/servemux/", mx)
 h.Handle("/gorilla/", r)
 // 方式1
 err := http.ListenAndServe(":8080", h)
 if err != nil {
  panic(err)
 }
}

利用組合設(shè)計(jì)模式,h := http.NewServeMux()作為新的容器,將不同的路由分配給另外兩個(gè)容器

  • mx := http.NewServeMux()

  • r := mux.NewRouter()

總結(jié)

本文主要介紹了Go http server的啟動(dòng)方式,重點(diǎn)介紹了http server的請(qǐng)求處理器

type Handler interface {
 ServeHTTP(ResponseWriter, *Request)
}

別看它僅包含一個(gè)方法,但在組合模式的加成下,可以實(shí)現(xiàn)千變?nèi)f化的形態(tài)。

除了Go標(biāo)準(zhǔn)庫(kù)中提供了http.ServeMux還有一系列開(kāi)源庫(kù)gorilla/mux、go-chi/chijulienschmidt/httprouter對(duì)Handler進(jìn)行了實(shí)現(xiàn)。

每一個(gè)庫(kù)具有的能力、使用方式、性能不同,但萬(wàn)變不離其宗,都繞不開(kāi)組合模式和Handler接口

以上就是Go標(biāo)準(zhǔn)庫(kù)http server優(yōu)雅啟動(dòng)深入理解的詳細(xì)內(nèi)容,更多關(guān)于Go標(biāo)準(zhǔn)庫(kù)http server啟動(dòng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Gin框架中的路由與請(qǐng)求處理的實(shí)現(xiàn)

    Gin框架中的路由與請(qǐng)求處理的實(shí)現(xiàn)

    本文主要介紹了Gin框架中的路由與請(qǐng)求處理的實(shí)現(xiàn),包括路徑參數(shù)、查詢參數(shù)和路由分組的使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-06-06
  • 使用Go語(yǔ)言啟動(dòng)Redis的實(shí)例詳解

    使用Go語(yǔ)言啟動(dòng)Redis的實(shí)例詳解

    這篇文章主要為大家介紹了Go語(yǔ)言中一個(gè)可以用來(lái)啟動(dòng)?redis-server?的開(kāi)源庫(kù)?github.com/stvp/tempredis,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-01-01
  • golang使用http client發(fā)起get和post請(qǐng)求示例

    golang使用http client發(fā)起get和post請(qǐng)求示例

    這篇文章主要介紹了golang使用http client發(fā)起get和post請(qǐng)求示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • Golang中channel使用的一些小技巧

    Golang中channel使用的一些小技巧

    這篇文章主要介紹了Golang中channel使用的一些小技巧,本文講解了關(guān)閉2次、讀取的時(shí)候channel提前關(guān)閉了、向已經(jīng)關(guān)閉的channel寫(xiě)數(shù)據(jù)等技巧及這實(shí)例代碼,需要的朋友可以參考下
    2015-07-07
  • 詳解go-micro微服務(wù)consul配置及注冊(cè)中心

    詳解go-micro微服務(wù)consul配置及注冊(cè)中心

    這篇文章主要為大家介紹了go-micro微服務(wù)consul配置及注冊(cè)中心示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • beego獲取ajax數(shù)據(jù)的實(shí)例

    beego獲取ajax數(shù)據(jù)的實(shí)例

    下面小編就為大家分享一篇beego獲取ajax數(shù)據(jù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2017-12-12
  • go語(yǔ)言編程二維碼生成及識(shí)別

    go語(yǔ)言編程二維碼生成及識(shí)別

    這篇文章主要為大家介紹了go語(yǔ)言編程二維碼的生成及識(shí)別示例演示,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-04-04
  • Go語(yǔ)言中panic和recover的實(shí)現(xiàn)

    Go語(yǔ)言中panic和recover的實(shí)現(xiàn)

    本文主要介紹了Go語(yǔ)言中panic和recover的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-10-10
  • Go語(yǔ)言中的逃逸分析究竟是什么?

    Go語(yǔ)言中的逃逸分析究竟是什么?

    這篇文章主要介紹了Go語(yǔ)言中的逃逸,套喲究竟是什么呢?通俗來(lái)講,當(dāng)一個(gè)對(duì)象的指針被多個(gè)方法或線程引用時(shí),我們稱這個(gè)指針發(fā)生了“逃逸”。下面文章將詳細(xì)介紹Go語(yǔ)言中的逃逸,需要的朋友可以參考一下
    2021-09-09
  • go?mod文件內(nèi)容版本號(hào)簡(jiǎn)單用法詳解

    go?mod文件內(nèi)容版本號(hào)簡(jiǎn)單用法詳解

    這篇文章主要為大家介紹了go?mod文件內(nèi)容版本號(hào)簡(jiǎn)單用法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10

最新評(píng)論

定南县| 西丰县| 峨山| 江津市| 建昌县| 洞口县| 南江县| 朝阳市| 祁连县| 南投市| 灵川县| 抚松县| 钟祥市| 临夏县| 河池市| 丹阳市| 达孜县| 砚山县| 久治县| 平和县| 铁岭市| 英超| 自贡市| 山阳县| 凤凰县| 二连浩特市| 马尔康县| 福州市| 温宿县| 漳平市| 遵化市| 高邑县| 金溪县| 阜城县| 丹阳市| 宜昌市| 朔州市| 衡阳县| 南陵县| 萍乡市| 申扎县|