欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang構(gòu)建HTTP服務(wù)的實(shí)現(xiàn)步驟

 更新時(shí)間:2021年12月10日 10:58:43   作者:冷月醉雪  
其實(shí)很多框架都是在 最簡單的http服務(wù)上做擴(kuò)展的的,基本上都是遵循h(huán)ttp協(xié)議,本文主要介紹了golang構(gòu)建HTTP服務(wù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

???一個(gè)go最簡單的Http服務(wù)器程序

package main
 
import (
    "fmt"
    "net/http"
)
 
func IndexHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello world")
}
 
func main() {
    http.HandleFunc("/", IndexHandler)
    http.ListenAndServe("127.0.0.0:8000", nil)
}

HTTP

? ?除去細(xì)節(jié),理解HTTP構(gòu)建的網(wǎng)絡(luò)應(yīng)用只要關(guān)注兩個(gè)端--客戶端(client)和服務(wù)端(server),兩個(gè)端的交互來自client的request,以及server端的response。所謂的http服務(wù)器,主要在于如何接受client的request,并向client返回response。

? ?接收request的過程中,最重要的莫過于路由(router),即實(shí)現(xiàn)一個(gè)Multiplexer器。Go中既可以使用內(nèi)置的mutilplexer--DefaultServeMux,也可以自定義。Multiplexer路由的目的就是為了找到處理器函數(shù)(hander),后者將對request進(jìn)行處理,同時(shí)構(gòu)建response。

? 簡單總結(jié)就是這個(gè)流程:

? 因此,理解go中的http服務(wù),,最重要的就是要理解Multiplexer和hander,Golang中的Multiplexer基于ServerMux結(jié)構(gòu),同時(shí)也實(shí)現(xiàn)了Handler接口。

? ·hander函數(shù):具有func(w http.ResponseWriter, r *http.Requests)簽名的函數(shù)

? ·handler處理器(函數(shù)):經(jīng)過HanderFunc結(jié)構(gòu)包裝的handler函數(shù),它實(shí)現(xiàn)了ServeHTTP接口方法的函數(shù)。調(diào)用handler處理器的ServeHTTP方法時(shí),即調(diào)用handler函數(shù)本身。

? ·handler對象:實(shí)現(xiàn)了Hander接口ServeHTTP方法的結(jié)構(gòu)。

? ?Golang的http處理流程可以用下面一張圖表示,后面內(nèi)容是針對圖進(jìn)行說明:

Handler

? ?Golang沒有繼承,類多態(tài)的方法可以通過接口實(shí)現(xiàn)。所謂接口則是定義聲明了函數(shù)簽名,任何結(jié)構(gòu)只要實(shí)現(xiàn)了與接口函數(shù)簽名相同的方法,就等同于實(shí)現(xiàn)了接口。go的http服務(wù)都是基于handler進(jìn)行處理的。

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

??? 任何結(jié)構(gòu)體,只要實(shí)現(xiàn)了ServeHTTP方法,這個(gè)結(jié)構(gòu)就可以稱之為handler對象。ServeMux會使用handler并調(diào)用其ServeHTTP方法處理請求并返回響應(yīng)。

ServeMux

? ?了解了Handler之后,再看ServeMux。ServeMux源碼很簡單:

type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry
    hosts bool 
}
 
type muxEntry struct {
    explicit bool
    h        Handler
    pattern  string
}

???ServeMux結(jié)構(gòu)中最重要的字段為m,這是一個(gè)map,key是一些url模式,value是一個(gè)muxEntry結(jié)構(gòu),后者里定義存儲了具體的url模式和handler。?

? ?當(dāng)然,所謂的ServeMux也實(shí)現(xiàn)了ServeHTTP接口,也算是一個(gè)handler,不過ServeMux的ServeHTTP方法不是用來處理request和respone,而是用來找到路由注冊的handler。

Server

? ?除了ServeMux和Handler,還有一個(gè)結(jié)構(gòu)Server需要了解。從http.ListenAndServe的源碼可以看出,它創(chuàng)建了一個(gè)server對象,并調(diào)用server對象的ListenAndServe方法:

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

???查看server的結(jié)構(gòu)如下:

type Server struct {
    Addr         string        
    Handler      Handler       
    ReadTimeout  time.Duration 
    WriteTimeout time.Duration 
    TLSConfig    *tls.Config   
 
    MaxHeaderBytes int
 
    TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
 
    ConnState func(net.Conn, ConnState)
    ErrorLog *log.Logger
    disableKeepAlives int32     nextProtoOnce     sync.Once 
    nextProtoErr      error     
}

???server結(jié)構(gòu)存儲了服務(wù)器處理請求常見的字段。其中Handler字段也保留Hander接口。如果Server接口沒有提供Handler結(jié)構(gòu)對象,那么會使用DefaultServeMux做Multiplexer。

創(chuàng)建HTTP服務(wù)

??? 創(chuàng)建一個(gè)http服務(wù),大致需要經(jīng)歷兩個(gè)過程,首先需要注冊路由,即提供url模式和handler函數(shù)的映射,其次就是實(shí)例化一個(gè)server對象,并開啟對客戶端的監(jiān)聽。

http.HandleFunc("/", indexHandler)
http.ListenAndServe("127.0.0.1:8000", nil)
 
或者:
 
server := &Server{Addr: addr, Handler: handler}
 
server.ListenAndServe()

http注冊路由

? ? net/http包暴露的注冊路由的api很簡單,http.HandleFunc選取了DefaultServeMux作為multiplexer:

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

???實(shí)際上,DefaultServeMux是ServeMux的一個(gè)實(shí)例。當(dāng)然http包也提供了NewServeMux方法創(chuàng)建一個(gè)ServeMux實(shí)例,默認(rèn)則創(chuàng)建一個(gè)DefaultServeMux:

// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux {
    return new(ServeMux)
}
 
// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = &defaultServeMux
 
var defaultServeMux ServeMux

???注意,go創(chuàng)建實(shí)例的過程中,也可以使用指針方式,即

   type Server struct{}
   server := Server{}

? ?和下面的一樣都可以創(chuàng)建Server的實(shí)例

 var DefaultServer Server
  var server = &DefalutServer

???因此DefaultServeMux的HandleFunc(pattern,handler)方法實(shí)際是定義在ServeMux下的:

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}

???上述代碼中,HandlerFunc是一個(gè)函數(shù)類型。同時(shí)實(shí)現(xiàn)了Handler接口的ServeHTTP方法。使用HandlerFunc類型包裝一下路由定義的indexHandler函數(shù),其目的就是為了讓這個(gè)函數(shù)也實(shí)現(xiàn)ServeHTTP方法,即轉(zhuǎn)變成一個(gè)handler處理器(函數(shù))。

type HandlerFunc func(ResponseWriter, *Request)
 
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

???一旦這樣做了,就意味著我們的indexHandler函數(shù)也有了ServeHTTP方法。
? ?此外,ServeMux的Handle方法,將會對pattern和handler函數(shù)做一個(gè)map映射:

func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()
 
    if pattern == "" {
        panic("http: invalid pattern " + pattern)
    }
    if handler == nil {
        panic("http: nil handler")
    }
    if mux.m[pattern].explicit {
        panic("http: multiple registrations for " + pattern)
    }
 
    if mux.m == nil {
        mux.m = make(map[string]muxEntry)
    }
    mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}
 
    if pattern[0] != '/' {
        mux.hosts = true
    }
 
    n := len(pattern)
    if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
 
        path := pattern
        if pattern[0] != '/' {
            path = pattern[strings.Index(pattern, "/"):]
        }
        url := &url.URL{Path: path}
        mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
    }
}

???由此可見,Handle函數(shù)的主要目的在于把handler和pattern模式綁定到map[string]muxEntry的map上,其中muxEntry保存了更多pattern和handler的信息。Server的m字段就是map[string]muxEntry這樣一個(gè)map。
? ?此時(shí),pattern和handler的路由注冊完成。

開啟監(jiān)聽

? ? 注冊好路由之后,啟動(dòng)web服務(wù)還需要開啟服務(wù)器監(jiān)聽。http的ListenAndServer方法中可以看到創(chuàng)建了一個(gè)Server對象,并調(diào)用了Server對象的同名方法:

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}
 
func (srv Server) ListenAndServe() error {
    addr := srv.Addr
    if addr == "" {
        addr = ":http"
    }
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        return err
    }
    return srv.Serve(tcpKeepAliveListener{ln.(net.TCPListener)})
}

???Server的ListenAndServer方法中,會初始化監(jiān)聽地址Addr,同時(shí)調(diào)用Listen方法設(shè)置監(jiān)聽。最后將監(jiān)聽的TCP對象傳入Serve方法:

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    ...
 
    baseCtx := context.Background()
    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
    for {
        rw, e := l.Accept()
        ...
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve(ctx)
    }
}

處理請求

???監(jiān)聽開啟之后,一旦客戶端請求到底,go就開啟一個(gè)協(xié)程處理請求,主要邏輯都在server方法之中。

? ?serve方法比較長,其主要職能就是,創(chuàng)建一個(gè)上下文對象,然后調(diào)用Listener的Accept方法用來獲取連接數(shù)據(jù)并使用newConn方法創(chuàng)建連接對象。最后使用goroutein協(xié)程的方式處理連接請求。因此每一個(gè)連接都開啟了一個(gè)協(xié)程,請求的上下文都不同,同時(shí)又保證了go的高并發(fā)。serve也是一個(gè)長長的方法:

func (c *conn) serve(ctx context.Context) {
    c.remoteAddr = c.rwc.RemoteAddr().String()
    defer func() {
        if err := recover(); err != nil {
            const size = 64 << 10
            buf := make([]byte, size)
            buf = buf[:runtime.Stack(buf, false)]
            c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)
        }
        if !c.hijacked() {
            c.close()
            c.setState(c.rwc, StateClosed)
        }
    }()
 
    ...
 
    for {
        w, err := c.readRequest(ctx)
        if c.r.remain != c.server.initialReadLimitSize() {
            // If we read any bytes off the wire, we're active.
            c.setState(c.rwc, StateActive)
        }
        ...
        
        }
        
        ...
    
        serverHandler{c.server}.ServeHTTP(w, w.req)
        w.cancelCtx()
        if c.hijacked() {
            return
        }
        w.finishRequest()
        if !w.shouldReuseConnection() {
            if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
                c.closeWriteAndWait()
            }
            return
        }
        c.setState(c.rwc, StateIdle)
    }
}

???盡管serve很長,里面的結(jié)構(gòu)和邏輯還是很清晰的,使用defer定義了函數(shù)退出時(shí),連接關(guān)閉相關(guān)的處理。然后就是讀取連接的網(wǎng)絡(luò)數(shù)據(jù),并處理讀取完畢時(shí)候的狀態(tài)。接下來就是調(diào)用serverHandler{c.server}.ServeHTTP(w,w.req)方法處理請求了。最后就是請求處理完畢的邏輯。serverHandler是一個(gè)重要的結(jié)構(gòu),它僅有一個(gè)字段,即Server結(jié)構(gòu),同時(shí)它也實(shí)現(xiàn)了Hander接口方法ServeHTTP,同時(shí)它也實(shí)現(xiàn)了Handler接口方法ServeHTTP,并在該接口方法中做了一個(gè)重要的事情,初始化multiplexer路由多路復(fù)用器。如果server對象沒有制定Handler,則使用默認(rèn)的DefaultServeMux作為路由Multiplexer。并調(diào)用初始化Handler的ServeHTTP方法。

type serverHandler struct {
    srv *Server
}
 
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req Request) {
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    handler.ServeHTTP(rw, req)
}

???這里DefaultServeMux的ServeHTTP方法其實(shí)也是定義在ServeMux結(jié)構(gòu)中的,相關(guān)代碼如下:

func (mux *ServeMux) (w ResponseWriter, r Request) {
    if r.RequestURI == "" {
        if r.ProtoAtLeast(1, 1) {
            w.Header().Set("Connection", "close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}
 
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
    if r.Method != "CONNECT" {
        if p := cleanPath(r.URL.Path); p != r.URL.Path {
            _, pattern = mux.handler(r.Host, p)
            url := *r.URL
            url.Path = p
            return RedirectHandler(url.String(), StatusMovedPermanently), pattern
        }
    }
    return mux.handler(r.Host, r.URL.Path)
}
 
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
    mux.mu.RLock()
    defer mux.mu.RUnlock()
 
    // Host-specific pattern takes precedence over generic ones
    if mux.hosts {
        h, pattern = mux.match(host + path)
    }
    if h == nil {
        h, pattern = mux.match(path)
    }
    if h == nil {
        h, pattern = NotFoundHandler(), ""
    }
    return
}
 
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    var n = 0
    for k, v := range mux.m {
        if !pathMatch(k, path) {
        continue
    }
    if h == nil || len(k) > n {
        n = len(k)
        h = v.h
        pattern = v.pattern
        }
    }
    return
}

???mux的ServeHTTP方法通過調(diào)用其Handler方法尋找注冊到路由上的handler函數(shù),并調(diào)用該函數(shù)的ServeHTTP方法。?

? ?mux的Handler方法對URL簡單的處理,然后調(diào)用handler方法,后者會創(chuàng)建一個(gè)鎖,同時(shí)調(diào)用match方法返回一個(gè)handler和pattern。
? ?在match方法中,mux的m字段是map[string]muxEntry,后者存儲了pattern和handler處理器函數(shù),因此通過迭代m尋找出注冊路由的pattern模式與實(shí)際url匹配的handler函數(shù)并返回。
? ?返回的結(jié)構(gòu)一直傳遞到mux的ServeHTTP方法,接下來調(diào)用handler函數(shù)的ServeHTTP方法,即IndexHandler函數(shù),然后把reponse寫到http.RequestWirter對象返回給客戶端。

? ?上述函數(shù)運(yùn)行結(jié)束即serverHandler{c.server}.ServeHTTP(w, w.req)運(yùn)行結(jié)束。接下來就是對請求處理完畢之后上希望和連接斷開的相關(guān)邏輯。

? ?至此,Golang中一個(gè)完整的http服務(wù)介紹完畢,包括注冊路由,開啟監(jiān)聽,處理連接,路由處理函數(shù)。

參考:

https://www.yuque.com/docs/share/24a1bd1c-e32d-4268-9115-bf03f53677d3

到此這篇關(guān)于golang構(gòu)建HTTP服務(wù)的實(shí)現(xiàn)步驟的文章就介紹到這了,更多相關(guān)golang構(gòu)建HTTP服務(wù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang使用singleflight解決并發(fā)重復(fù)請求

    Golang使用singleflight解決并發(fā)重復(fù)請求

    高并發(fā)的場景下,經(jīng)常會出現(xiàn)并發(fā)重復(fù)請求資源的情況,singleflight是golang內(nèi)置的一個(gè)包,這個(gè)包提供了對重復(fù)函數(shù)調(diào)用的抑制功能,所以下面我們就來看看如何使用它解決并發(fā)重復(fù)請求吧
    2023-08-08
  • Go singleflight使用以及原理

    Go singleflight使用以及原理

    singleflight官方解釋其為:singleflight提供了一個(gè)重復(fù)的函數(shù)調(diào)用抑制機(jī)制。通俗的解釋其作用是,若有多個(gè)協(xié)程運(yùn)行某函數(shù)時(shí),只讓一個(gè)協(xié)程去處理,然后批量返回。非常適合來做并發(fā)控制。常見用于緩存穿透的情況
    2023-01-01
  • Golang使用cobra實(shí)現(xiàn)命令行程序的示例代碼

    Golang使用cobra實(shí)現(xiàn)命令行程序的示例代碼

    Cobra 是 Go 語言中一個(gè)強(qiáng)大的命令行應(yīng)用庫,它提供了創(chuàng)建命令行工具所需的基本結(jié)構(gòu)和功能,被許多開發(fā)者用于構(gòu)建各種命令行工具和應(yīng)用程序,本文將給大家介紹Golang使用cobra實(shí)現(xiàn)命令行程序,文中通過代碼示例介紹的非常詳細(xì),需要的朋友可以參考下
    2024-02-02
  • Golang中文件目錄操作的實(shí)現(xiàn)步驟詳解

    Golang中文件目錄操作的實(shí)現(xiàn)步驟詳解

    在Golang中,文件目錄是指計(jì)算機(jī)文件系統(tǒng)中的文件夾或目錄。目錄是用于組織和存儲文件的一種方式,可以包含文件和其他子目錄,本文主要介紹了Golang中文件目錄操作的實(shí)現(xiàn)方法,需要的朋友可以參考下
    2023-05-05
  • golang中的net/rpc包使用概述(小結(jié))

    golang中的net/rpc包使用概述(小結(jié))

    本篇文章主要介紹了golang中的net/rpc包使用概述(小結(jié)),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-11-11
  • Golang利用channel協(xié)調(diào)協(xié)程的方法詳解

    Golang利用channel協(xié)調(diào)協(xié)程的方法詳解

    go?當(dāng)中的并發(fā)編程是通過goroutine來實(shí)現(xiàn)的,利用channel(管道)可以在協(xié)程之間傳遞數(shù)據(jù),所以本文就來講講Golang如何利用channel協(xié)調(diào)協(xié)程吧
    2023-05-05
  • 一文詳解Golang的函數(shù)特性

    一文詳解Golang的函數(shù)特性

    函數(shù)是?Golang?中非常重要的組成部分之一,它們提供了代碼的可重用性和組織性。在本文中,我們將深入了解?Golang?函數(shù)的多個(gè)方面,希望對大家有所幫助
    2023-04-04
  • Golang實(shí)現(xiàn)Mongo數(shù)據(jù)庫增刪改查操作

    Golang實(shí)現(xiàn)Mongo數(shù)據(jù)庫增刪改查操作

    本文主要介紹了Golang實(shí)現(xiàn)Mongo數(shù)據(jù)庫增刪改查操作,我們使用了 MongoDB的官方Go驅(qū)動(dòng)程序,實(shí)現(xiàn)了插入、查詢、更新和刪除操作,感興趣的可以了解一下
    2024-01-01
  • Go語言使用buffer讀取文件的實(shí)現(xiàn)示例

    Go語言使用buffer讀取文件的實(shí)現(xiàn)示例

    本文主要介紹了Go語言使用buffer讀取文件的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Go數(shù)據(jù)庫遷移的實(shí)現(xiàn)步驟

    Go數(shù)據(jù)庫遷移的實(shí)現(xiàn)步驟

    本文主要介紹了Go數(shù)據(jù)庫遷移的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07

最新評論