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

Go標準庫http?server優(yōu)雅啟動深入理解

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

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

package main

import (
 "net"
 "net/http"
)

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

點開http.ListenAndServe可以看到函數只是創(chuàng)建了Server類型并調用server.ListenAndServe()

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

package main

import (
 "net"
 "net/http"
)

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

ListenAndServe()如其名會干兩件事

  • 監(jiān)聽一個端口,即Listen的過程

  • 處理進入端口的連接,即Serve的過程

所以下面的代碼和上面的代碼也沒區(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!

按上面的代碼啟動HTTP Server沒有太大意義,因為我們還沒有設定路由,所以無法正常響應請求

$ curl  127.0.0.1:8080 
404 page not found

暫停思考一下,服務器返回404是因為沒有設定路由么?no,no,no,你需要轉變一下思維。服務器返回404不是因為我們沒有設置路由,而是因為沒有設置請求的處理程序,這個處理程序在Go中叫作:Handler!

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

?? 怎么定義請求的處理程序?

由上可知,僅需要實現ServeHTTP(ResponseWriter, *Request)接口即可

注意,示例代碼沒有判斷任何路由(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
}

?? 怎么設置請求的處理程序?

三種方式本質上都是把自定義的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)
 }
}

?? 設置請求的處理程序之后的效果

handlerImp只針對Method做了不同的響應,沒有對PATH做任何的判斷,所以無論請求什么樣的路徑都能拿到一個預期的響應。

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

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

此時再體會一下這句話:我們設置的不是路由,而是設置請求的處理程序

再聊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
}

如上所述,無論任何PATH,任何Method等,所有的請求都會被handlerImp.ServeHTTP處理。

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

很明顯,這違反了高內聚,低耦合的編程范式

停下來思考下,如何編寫一個高內聚,低耦合的handlerImp.ServeHTTP,使之針對不同HTTP請求執(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
}

如果你的答案和上面的代碼類似,那么我對于這段代碼的點評是:不太高明??

?? 如何編寫一個高內聚,低耦合的ServeHTTP,針對不同HTTP請求執(zhí)行不同的邏輯?

不知道你有沒有聽過設計模式中,組合模式。沒有了解可以去了解下,或者看下圖

經過組合模式重新設計的handlerImp,已經不再包含具體的邏輯了,它先搜索有沒有針對PATH處理的邏輯,再搜索有沒有針對Method處理的邏輯,它專注于邏輯分派,它是組合模式中的容器。

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

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
}

重新設計的handlerImp不執(zhí)行邏輯,實際的邏輯被分離到每一個葉子結點中,而每一個葉子結點也都實現了ServeHTTP函數,即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
}

再次強調,通過對組合模式的運用,我們把邏輯分派的功能聚合到handlerImp,把具體的邏輯聚合到PathFoo、MethodGet、MethodPost

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

上面實現的handlerImp利用組合設計模式,已經能針對Path和Method設定和處理不同的邏輯,但整體功能略顯簡單。有哪些可以供我們使用且功能強大的Handlers呢?

http.ServeMux

Go標準庫中就提供了一個Handler實現叫作http.ServeMux

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

使用的方式如下

http.ServeMux提供兩個函數用于注冊不同Path的處理函數

  • ServeMux.Handle 接收的是Handler接口實現

  • ServeMux.HandleFunc 接收的是匿名函數

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//結尾,所以它可以匹配/bar/*所有的Path

關于http.ServeMux的細節(jié)不是本篇重點,后續(xù)會單獨介紹

?? 默認的Handler

因為是標準庫內置的實現,當沒有設置http.Server.Handler屬性時,http.Server就會使用一個全局的變量DefaultServeMux *ServeMux來作為http.Server.Handler的值

var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux

http包同時提供了兩個函數可以在DefaultServeMux注冊不同Path的處理函數

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接口實現,對應的是

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

http.HandleFunc 接收的是匿名函數,對應的是

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

gorilla/mux

gorilla/mux是一個相當流行的第三方庫,用法這里簡單寫下

除了經典的Handle、HandleFunc函數,gorilla/mux還提供了MethodsSchemes、Host等非常復雜的功能

但無論多復雜,其一定包含了ServeHTTP函數,即實現了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實現,具體可以參考各自的官方文檔。

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

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

關于Go標準庫、第三方庫中這些結構的關系通過下圖展示

再聊組合模式

無論是官方的http.ServeMux,還是一些第三方庫,實現上大多使用了組合設計模式

組合模式的魔力還不止于此。思考一下這個場景:目前已經存在路由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"))
})

但此時還有另外一組路由/gorilla/*,使用了開源庫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"))
})

如何啟動這樣的服務器呢?

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)
 }
}

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

  • mx := http.NewServeMux()

  • r := mux.NewRouter()

總結

本文主要介紹了Go http server的啟動方式,重點介紹了http server的請求處理器

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

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

除了Go標準庫中提供了http.ServeMux還有一系列開源庫gorilla/muxgo-chi/chi、julienschmidt/httprouterHandler進行了實現。

每一個庫具有的能力、使用方式、性能不同,但萬變不離其宗,都繞不開組合模式和Handler接口

以上就是Go標準庫http server優(yōu)雅啟動深入理解的詳細內容,更多關于Go標準庫http server啟動的資料請關注腳本之家其它相關文章!

相關文章

  • Go 語言 IDE 中的 VSCode 配置使用教程

    Go 語言 IDE 中的 VSCode 配置使用教程

    Gogland 是 JetBrains 公司推出的Go語言集成開發(fā)環(huán)境。這篇文章主要介紹了Go 語言 IDE 中的 VSCode 配置使用教程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-05-05
  • Golang實現常見排序算法的示例代碼

    Golang實現常見排序算法的示例代碼

    現在的面試真的是越來越卷了,算法已經成為了面試過程中必不可少的一個環(huán)節(jié),你如果想進稍微好一點的公司,算法是必不可少的一個環(huán)節(jié)。本文為大家準備了Golang實現常見排序算法的示例代碼,需要的可以參考一下
    2022-05-05
  • go語言fasthttp使用實例小結

    go語言fasthttp使用實例小結

    fasthttp?是一個使用?Go?語言開發(fā)的?HTTP?包,主打高性能,針對?HTTP?請求響應流程中的?hot?path?代碼進行了優(yōu)化,下面我們就來介紹go語言fasthttp使用實例小結,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • golang數據結構之golang稀疏數組sparsearray詳解

    golang數據結構之golang稀疏數組sparsearray詳解

    這篇文章主要介紹了golang數據結構之golang稀疏數組sparsearray的相關知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • Go 熱加載之fresh詳解

    Go 熱加載之fresh詳解

    這篇文章主要為大家介紹了Go 熱加載之fresh詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Go語言實現的web爬蟲實例

    Go語言實現的web爬蟲實例

    這篇文章主要介紹了Go語言實現的web爬蟲,實例分析了web爬蟲的原理與Go語言的實現技巧,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-02-02
  • Go語言單元測試超詳細解析

    Go語言單元測試超詳細解析

    本文介紹了了Go語言單元測試超詳細解析,測試函數分為函數的基本測試、函數的組測試、函數的子測試,進行基準測試時往往是對函數的算法進行測驗,有時后一個算法在測試數據的基量不同時測試出的效果會不同我們需要對不同數量級的樣本進行測試,下文需要的朋友可以參考下
    2022-02-02
  • Go計時器的示例代碼

    Go計時器的示例代碼

    定時器是任何編程語言的重要工具,它允許開發(fā)人員在特定時間間隔安排任務或執(zhí)行代碼,本文主要介紹了Go計時器的示例代碼,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01
  • golang?使用sort.slice包實現對象list排序

    golang?使用sort.slice包實現對象list排序

    這篇文章主要介紹了golang?使用sort.slice包實現對象list排序,對比sort跟slice兩種排序的使用方式區(qū)別展開內容,需要的小伙伴可以參考一下
    2022-03-03
  • 大多數Go程序員都走過的坑盤點解析

    大多數Go程序員都走過的坑盤點解析

    這篇文章主要為大家介紹了大多數Go程序員都走過的坑盤點解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-12-12

最新評論